diff --git a/.gitignore b/.gitignore
index 2dc13af9..9752fac9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -163,6 +163,8 @@
 /chrome/web_ui_mojo_bindings.xml
 /chromecast/internal
 /chromeos/assistant/internal
+/chromeos/profiles/chromeos.orderfile.txt
+/chromeos/profiles/orderfile.local.txt
 /cipd_cache/
 /clank
 /cloud_print/cloud_print_version_resources.xml
diff --git a/.gn b/.gn
index a9f9422..70856f6 100644
--- a/.gn
+++ b/.gn
@@ -81,7 +81,7 @@
 
   #"//chrome/*",          # Many errors: https://crbug.com/949535
 
-  #"//chrome/android/*",  # 13 errors
+  "//chrome/android/*",
   "//chrome/app/*",
   "//chrome/app_shim/*",
 
diff --git a/BUILD.gn b/BUILD.gn
index 7a5ad82e..19fd588 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -148,6 +148,7 @@
       "//third_party/pdfium/samples:pdfium_test",
       "//third_party/webrtc/rtc_tools:frame_analyzer",
       "//tools/perf/clear_system_cache",
+      "//tools/polymer:polymer_tools_python_unittests",
       "//ui/accessibility:accessibility_unittests",
       "//ui/accessibility/extensions",
     ]
@@ -313,7 +314,6 @@
       "//chrome/test:android_browsertests",
       "//chrome/test/vr/perf:motopho_latency_test",
       "//components/invalidation/impl:components_invalidation_impl_junit_tests",
-      "//components/journey:journey_info_fetcher",
       "//components/policy/android:components_policy_junit_tests",
       "//components/signin/core/browser/android:components_signin_junit_tests",
       "//content/public/android:content_junit_tests",
diff --git a/DEPS b/DEPS
index c19eff0a..23203113 100644
--- a/DEPS
+++ b/DEPS
@@ -47,7 +47,8 @@
   # By default, we should check out everything needed to run on the main
   # chromium waterfalls. This var can be also be set to "small", in order
   # to skip things are not strictly needed to build chromium for development
-  # purposes.
+  # purposes, by adding the following line to src.git's .gclient entry:
+  #      "custom_vars": { "checkout_configuration": "small" },
   'checkout_configuration': 'default',
 
   # By default, don't check out android. Will be overridden by gclient
@@ -144,11 +145,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': 'd6cb7aecf13008c148d9043ea5364dc0784976bb',
+  'skia_revision': '59192a0d66a746e472802fed50c139b880ef11c4',
   # 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': '6c48f74fdafe303ea4013f443fd4c2a7ab824a85',
+  'v8_revision': '4035531228d69a8e3ec475ef75b51db302e70473',
   # 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.
@@ -156,7 +157,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '557e3853da56b9ff72be8ad8ce3643f5cdeb1d4d',
+  'angle_revision': '08b97da894b278c10e70b6888ac65e3972a13b31',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -164,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '0208b0ca4d258aa2ac8a938725d1afaa591c1a92',
+  'pdfium_revision': '6e3a40600f246bd8cff2367e3805ce53466cb349',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -207,7 +208,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': '3dd1372096afac06bb2c8c83fe2e3012429900d2',
+  'catapult_revision': 'c5817614666e57a37bebfb26e1cb85884321533e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -279,11 +280,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '271817784100310e8e694a537e2c036deaac6f46',
+  'dawn_revision': '9d2ccaf65c2a370c5f203e119e194a44fa039481',
   # 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': '89b9af539b5877093cdf7ba22cbff9354b6bf723',
+  'quiche_revision': '753002daef6045500f08066b132da7fd71ab4ca9',
   # 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.
@@ -483,7 +484,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'd57b1482fc27dae45805189a4639d2951406e284',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'e5a70e02cd24d86f9c3e1a9298c4ddca4555bd40',
       'condition': 'checkout_ios',
   },
 
@@ -808,7 +809,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3fa46deb9ba5a499fb8cf91ce1f207c093348e2e',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bf99f644ae328a8c0e265c2a4b510f27ad41bfb9',
       'condition': 'checkout_linux',
   },
 
@@ -823,7 +824,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '80a8441a9d67a14025a9541ff1e6a0f728376040',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '206270625a4ac391aada9ac5f0c32a4e66e9f59c',
       'condition': 'checkout_linux',
   },
 
@@ -833,7 +834,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '34cc05018d70cb48e482c6c82028725d4d3d4225',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '365720e237c2afc529446d1af0a253ab3edad3b4',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -868,7 +869,7 @@
   },
 
   'src/third_party/ffmpeg':
-    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'fff11548e7b6b95ba0fcb179eebc72ff1244b11d',
+    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'e0e3133c40fbca0032a2d9400da736e4b933882f',
 
   'src/third_party/flac':
     Var('chromium_git') + '/chromium/deps/flac.git' + '@' + 'af862024c8c8fa0ae07ced05e89013d881b00596',
@@ -902,7 +903,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '302fe97e7b02e37856961ea74f76759098b99188',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '42f813401bdfa640e4b87d8991e63f97fe5d5c43',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1095,7 +1096,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'bb407a27b2e32f89f0e9eeee2bcd0aa9d5cfea3f',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '18d309c12734d2f06d54ad1716e512153a13ab9d',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '51ca718c3adf0ddedacd7df25fe45f67dc5a9ce1',
@@ -1178,7 +1179,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'eb5dbc61ce239d7b7bba9d9400553906acadcae4',
+    Var('chromium_git') + '/openscreen' + '@' + 'd5b3c34bb4b3a1dd65cb13be818596bc81a7855a',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'c52d3b40dd802353c1f2ceec139922b374c1373a',
@@ -1206,7 +1207,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'b088b60121ed0b061ff8a18f9486a1be1252656f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '091864c00bffb363a0202fd5b7b1fc30ed04ca17',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1374,7 +1375,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '672a48d0d2b96ad6f80558247f34e70a975d1a2b',
+    Var('webrtc_git') + '/src.git' + '@' + '2bac7da1349c75e5cf89612ab9619a1920d5d974',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1415,7 +1416,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b48880d5ef41a2231530cddb6f70467bd5b9c7c8',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6129e06e7f87b0c16aca97e604344f3849c1bcd9',
     'condition': 'checkout_src_internal',
   },
 
@@ -2762,7 +2763,13 @@
     'name': 'Fetch Android AFDO profile',
     'pattern': '.',
     'condition': 'checkout_android or checkout_linux',
-    'action': ['vpython', 'src/chrome/android/profiles/update_afdo_profile.py'],
+    'action': [ 'vpython',
+                'src/tools/download_cros_provided_profile.py',
+                '--newest_state=src/chrome/android/profiles/newest.txt',
+                '--local_state=src/chrome/android/profiles/local.txt',
+                '--output_name=src/chrome/android/profiles/afdo.prof',
+                '--gs_url_base=chromeos-prebuilt/afdo-job/llvm',
+    ],
   },
   {
     'name': 'gvr_static_shim_android_arm_1',
@@ -2899,6 +2906,18 @@
     ],
   },
   {
+    'name': 'Fetch ChromeOS-specific orderfile for Chrome',
+    'pattern': '.',
+    'condition': 'checkout_chromeos or checkout_simplechrome',
+    'action': [ 'vpython',
+                'src/tools/download_cros_provided_profile.py',
+                '--newest_state=src/chromeos/profiles/orderfile.newest.txt',
+                '--local_state=src/chromeos/profiles/orderfile.local.txt',
+                '--output_name=src/chromeos/profiles/chromeos.orderfile.txt',
+                '--gs_url_base=chromeos-prebuilt/afdo-job/orderfiles/vetted',
+    ],
+  },
+  {
     # Pull doclava binaries if building for Android.
     'name': 'doclava',
     'pattern': '.',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index f28aa1a..3ecef02 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -693,6 +693,10 @@
     "renderer/print_render_frame_observer.h",
   ]
 
+  public_deps = [
+    "//base",
+  ]
+
   deps = [
     ":generate_aw_resources",
     ":generate_aw_strings",
@@ -701,7 +705,6 @@
     "//android_webview/browser/gfx:gfx",
     "//android_webview/common",
     "//android_webview/common:common_mojom",
-    "//base",
     "//base/third_party/dynamic_annotations:dynamic_annotations",
     "//components/about_ui",
     "//components/autofill/android:provider",
@@ -887,6 +890,7 @@
     "java/src/org/chromium/android_webview/permission/AwPermissionRequest.java",
     "java/src/org/chromium/android_webview/policy/AwPolicyProvider.java",
     "java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java",
+    "java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java",
   ]
   deps = [
     ":android_webview_commandline_java",
diff --git a/android_webview/browser/aw_feature_list.cc b/android_webview/browser/aw_feature_list.cc
index 846a497..50acf01 100644
--- a/android_webview/browser/aw_feature_list.cc
+++ b/android_webview/browser/aw_feature_list.cc
@@ -57,10 +57,6 @@
 const base::Feature kWebViewPageStartedOnCommit{
     "WebViewPageStartedOnCommit", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Whether the application package name is logged in UMA.
-const base::Feature kWebViewUmaLogAppPackageName{
-    "WebViewUmaLogAppPackageName", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enable raster in wide color gamut for apps that use webview in a wide color
 // gamut activity.
 const base::Feature kWebViewWideColorGamutSupport{
diff --git a/android_webview/browser/aw_feature_list.h b/android_webview/browser/aw_feature_list.h
index 42cf080..f98fc23 100644
--- a/android_webview/browser/aw_feature_list.h
+++ b/android_webview/browser/aw_feature_list.h
@@ -17,7 +17,6 @@
 extern const base::Feature kWebViewBrotliSupport;
 extern const base::Feature kWebViewConnectionlessSafeBrowsing;
 extern const base::Feature kWebViewPageStartedOnCommit;
-extern const base::Feature kWebViewUmaLogAppPackageName;
 extern const base::Feature kWebViewWideColorGamutSupport;
 
 }  // namespace features
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index 5f6739c1..7b27757a 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -218,9 +218,6 @@
 }
 
 std::string AwMetricsServiceClient::GetAppPackageName() {
-  if (!base::FeatureList::IsEnabled(features::kWebViewUmaLogAppPackageName))
-    return std::string();
-
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jstring> j_app_name =
       Java_AwMetricsServiceClient_getAppPackageName(env);
diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc
index 3fd79eb..f1979fdc 100644
--- a/android_webview/browser/aw_permission_manager.cc
+++ b/android_webview/browser/aw_permission_manager.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_frame_host.h"
@@ -179,6 +180,11 @@
     }
     DCHECK(!IsCompleted());
     results[result->second] = status;
+    if (type == PermissionType::MIDI_SYSEX &&
+        status == PermissionStatus::GRANTED) {
+      content::ChildProcessSecurityPolicy::GetInstance()
+          ->GrantSendMidiSysExMessage(render_process_id);
+    }
     resolved_permissions_.insert(type);
   }
 
diff --git a/android_webview/browser/gfx/surfaces_instance.cc b/android_webview/browser/gfx/surfaces_instance.cc
index 395dbcb..434c112 100644
--- a/android_webview/browser/gfx/surfaces_instance.cc
+++ b/android_webview/browser/gfx/surfaces_instance.cc
@@ -374,9 +374,12 @@
   }
   display_->Resize(viewport);
   display_->DrawAndSwap();
-  // TODO(dlibby): Consider sending real swap timings here for webview (or
-  // prove why it is not needed).
-  display_->DidReceiveSwapBuffersAck(gfx::SwapTimings());
+  // SkiaRenderer generates DidReceiveSwapBuffersAck calls.
+  if (!features::IsUsingSkiaRenderer()) {
+    // TODO(dlibby): Consider sending real swap timings here for webview (or
+    // prove why it is not needed).
+    display_->DidReceiveSwapBuffersAck(gfx::SwapTimings());
+  }
   gl_surface_->MaybeDidPresent(gfx::PresentationFeedback(
       base::TimeTicks::Now(), base::TimeDelta(), 0 /* flags */));
 }
diff --git a/android_webview/browser/network_service/android_stream_reader_url_loader.h b/android_webview/browser/network_service/android_stream_reader_url_loader.h
index ba5bf2a3..285a921b 100644
--- a/android_webview/browser/network_service/android_stream_reader_url_loader.h
+++ b/android_webview/browser/network_service/android_stream_reader_url_loader.h
@@ -17,9 +17,15 @@
 class InputStream;
 class InputStreamReaderWrapper;
 
-// Custom URLLoader implementation for loading network responses from stream.
-// Current implementation is in particular for supporting shouldInterceptRequest
-// callback in webview.
+// Custom URLLoader implementation for loading responses from Android
+// InputStreams. Although this works generally for implementers of the
+// ResponseDelegate interface, this specifically aims to support:
+//
+//  - shouldInterceptRequest callback
+//  - content:// URLs, which load content from Android ContentProviders (which
+//    could be in-app or come from other apps)
+//  - file:///android_asset/ & file:///android_res/ URLs, which load in-app
+//    content from the app's asset/ and res/ folders
 class AndroidStreamReaderURLLoader : public network::mojom::URLLoader {
  public:
   // Delegate abstraction for obtaining input streams.
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 0efa517..1208e87 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
@@ -150,6 +150,7 @@
   DISALLOW_COPY_AND_ASSIGN(InterceptedRequest);
 };
 
+// A ResponseDelegate for responses returned by shouldInterceptRequest.
 class InterceptResponseDelegate
     : public AndroidStreamReaderURLLoader::ResponseDelegate {
  public:
@@ -203,6 +204,9 @@
   base::WeakPtr<InterceptedRequest> request_;
 };
 
+// A ResponseDelegate based on top of AndroidProtocolHandler for special
+// protocols, such as content://, file:///android_asset, and file:///android_res
+// URLs.
 class ProtocolResponseDelegate
     : public AndroidStreamReaderURLLoader::ResponseDelegate {
  public:
diff --git a/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java b/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java
new file mode 100644
index 0000000..3bac1d8e
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.android_webview.ui.util;
+
+import org.json.JSONException;
+
+import org.chromium.base.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parses WebView crash JSON log files which contain crash info keys extracted from crash minidump.
+ */
+public class WebViewCrashLogParser extends CrashInfoLoader {
+    private static final String TAG = "WebViewCrashUI";
+
+    private File mLogDir;
+
+    /**
+     * @param logDir the directory where WebView store crash logs.
+     */
+    public WebViewCrashLogParser(File logDir) {
+        mLogDir = logDir;
+    }
+
+    /**
+     * Load crash info from WebView crash logs under WebView crash log directory.
+     *
+     * @return list of crashes info
+     */
+    @Override
+    public List<CrashInfo> loadCrashesInfo() {
+        List<CrashInfo> infoList = new ArrayList<>();
+
+        if (!mLogDir.exists() || !mLogDir.isDirectory()) return infoList;
+
+        File[] logFiles = mLogDir.listFiles();
+        for (File logFile : logFiles) {
+            // Ignore non-json files.
+            if (!logFile.isFile() || !logFile.getName().endsWith(".json")) continue;
+            try {
+                CrashInfo crashInfo = CrashInfo.readFromJsonString(readEntireFile(logFile));
+                // TODO(987806) remove the null check.
+                if (crashInfo.localId != null) infoList.add(crashInfo);
+            } catch (JSONException e) {
+                Log.e(TAG, "Error while reading JSON", e);
+            } catch (IOException e) {
+                Log.e(TAG, "Error while reading log file", e);
+            }
+        }
+
+        return infoList;
+    }
+
+    private static String readEntireFile(File file) throws IOException {
+        try (FileInputStream fileInputStream = new FileInputStream(file)) {
+            byte[] data = new byte[(int) file.length()];
+            fileInputStream.read(data);
+            return new String(data);
+        }
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java
new file mode 100644
index 0000000..08fc773
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.test.ui.util;
+
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.empty;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+import org.chromium.android_webview.test.AwJUnit4ClassRunner;
+import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
+import org.chromium.android_webview.ui.util.WebViewCrashLogParser;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for WebViewCrashLogParser.
+ */
+@RunWith(AwJUnit4ClassRunner.class)
+public class WebViewCrashLogParserTest {
+    private static final String TEST_LOG_ENTRY =
+            "{\"crash-local-id\":\"123456abc\",\"crash-capture-time\":1234567890,"
+            + "\"app-package-name\":\"test.package\",\"variations\":[\"123456\",\"7890\"]}";
+
+    @Rule
+    public TemporaryFolder mTestLogDir = new TemporaryFolder();
+
+    // Write the given string to a new file in the temp test folder.
+    private void writeLogFile(String fileName, String content) throws IOException {
+        File logFile = mTestLogDir.newFile(fileName);
+        FileWriter writer = new FileWriter(logFile);
+        writer.write(content);
+        writer.close();
+    }
+
+    @Test
+    @MediumTest
+    public void testParseMultipleFiles() throws Exception {
+        final String[] expectedLocalIds = new String[] {"crash1", "crash2", "crash3", "crash4"};
+        for (String localId : expectedLocalIds) {
+            writeLogFile("crash_file_" + localId + ".json",
+                    "{\"crash-local-id\":\"" + localId
+                            + "\",\"app-package-name\":\"test.package\"}");
+        }
+
+        List<CrashInfo> crashInfoList =
+                new WebViewCrashLogParser(mTestLogDir.getRoot()).loadCrashesInfo();
+        List<String> actualLocalIds = new ArrayList<>();
+        for (CrashInfo crashInfo : crashInfoList) actualLocalIds.add(crashInfo.localId);
+        // Only asserting local Ids to make sure it's loaded correctly.
+        Assert.assertThat(actualLocalIds, containsInAnyOrder(expectedLocalIds));
+    }
+
+    @Test
+    @MediumTest
+    public void testParseInvalidFiles() throws Exception {
+        writeLogFile("crash_file_json.log", TEST_LOG_ENTRY);
+        writeLogFile("crash_file_json", TEST_LOG_ENTRY);
+        writeLogFile("crash_log", TEST_LOG_ENTRY);
+        writeLogFile("crash_log.txt", TEST_LOG_ENTRY);
+        writeLogFile("crash_log.json", "{\"invalid_json\":\"value\"");
+        List<CrashInfo> crashInfoList =
+                new WebViewCrashLogParser(mTestLogDir.getRoot()).loadCrashesInfo();
+        Assert.assertThat(crashInfoList, empty());
+    }
+
+    @Test
+    @MediumTest
+    public void testParseNonExistDir() throws Exception {
+        List<CrashInfo> crashInfoList =
+                new WebViewCrashLogParser(new File("non_exsiting_dir")).loadCrashesInfo();
+        Assert.assertThat(crashInfoList, empty());
+    }
+}
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 1dbb1ab..9eadcdd 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -171,16 +171,16 @@
         autofill::features::kAutofillSkipComparingInferredLabels);
 
     if (cl->HasSwitch(switches::kWebViewLogJsConsoleMessages)) {
-      features.EnableIfNotSet(features::kLogJsConsoleMessages);
+      features.EnableIfNotSet(::features::kLogJsConsoleMessages);
     }
 
-    features.DisableIfNotSet(features::kWebPayments);
+    features.DisableIfNotSet(::features::kWebPayments);
 
     // WebView does not and should not support WebAuthN.
-    features.DisableIfNotSet(features::kWebAuth);
+    features.DisableIfNotSet(::features::kWebAuth);
 
     // WebView isn't compatible with OOP-D.
-    features.DisableIfNotSet(features::kVizDisplayCompositor);
+    features.DisableIfNotSet(::features::kVizDisplayCompositor);
 
     // WebView does not support AndroidOverlay yet for video overlays.
     features.DisableIfNotSet(media::kUseAndroidOverlay);
@@ -196,13 +196,13 @@
     features.DisableIfNotSet(
         autofill::features::kAutofillRestrictUnownedFieldsToFormlessCheckout);
 
-    features.DisableIfNotSet(features::kBackgroundFetch);
+    features.DisableIfNotSet(::features::kBackgroundFetch);
 
-    features.DisableIfNotSet(features::kAndroidSurfaceControl);
+    features.DisableIfNotSet(::features::kAndroidSurfaceControl);
 
     // TODO(https://crbug.com/963653): SmsReceiver is not yet supported on
     // WebView.
-    features.DisableIfNotSet(features::kSmsReceiver);
+    features.DisableIfNotSet(::features::kSmsReceiver);
   }
 
   android_webview::RegisterPathProvider();
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index ce5f236..f2f31b73 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -281,6 +281,7 @@
     "../javatests/src/org/chromium/android_webview/test/services/VariationsSeedServerTest.java",
     "../javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java",
     "../javatests/src/org/chromium/android_webview/test/ui/util/UploadedCrashesInfoLoaderTest.java",
+    "../javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java",
     "../javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java",
     "../javatests/src/org/chromium/android_webview/test/util/AwTestTouchUtils.java",
     "../javatests/src/org/chromium/android_webview/test/util/CommonResources.java",
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 764da39..ee1d369c 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1935,6 +1935,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos:test_support",
+    "//chromeos/strings:strings_grit",
 
     # TODO(https://crbug.com/644336): Make CrasAudioHandler Chrome or Ash only.
     "//chromeos/audio",
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index 9eb834c..c0adf96 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -641,6 +641,12 @@
     client_->TriggerAccessibilityAlert(alert);
 }
 
+void AccessibilityControllerImpl::TriggerAccessibilityAlertWithMessage(
+    const std::string& message) {
+  if (client_)
+    client_->TriggerAccessibilityAlertWithMessage(message);
+}
+
 void AccessibilityControllerImpl::PlayEarcon(int32_t sound_key) {
   if (client_)
     client_->PlayEarcon(sound_key);
diff --git a/ash/accessibility/accessibility_controller_impl.h b/ash/accessibility/accessibility_controller_impl.h
index 3a065c9a..6c237d1 100644
--- a/ash/accessibility/accessibility_controller_impl.h
+++ b/ash/accessibility/accessibility_controller_impl.h
@@ -135,6 +135,9 @@
   // Triggers an accessibility alert to give the user feedback.
   void TriggerAccessibilityAlert(AccessibilityAlert alert);
 
+  // Triggers an accessibility alert with the given |message|.
+  void TriggerAccessibilityAlertWithMessage(const std::string& message);
+
   // Plays an earcon. Earcons are brief and distinctive sounds that indicate
   // that their mapped event has occurred. The |sound_key| enums can be found in
   // chromeos/audio/chromeos_sounds.h.
diff --git a/ash/accessibility/test_accessibility_controller_client.cc b/ash/accessibility/test_accessibility_controller_client.cc
index 0c08653a..cf5fc02a 100644
--- a/ash/accessibility/test_accessibility_controller_client.cc
+++ b/ash/accessibility/test_accessibility_controller_client.cc
@@ -24,6 +24,9 @@
   last_a11y_alert_ = alert;
 }
 
+void TestAccessibilityControllerClient::TriggerAccessibilityAlertWithMessage(
+    const std::string& message) {}
+
 void TestAccessibilityControllerClient::PlayEarcon(int32_t sound_key) {
   sound_key_ = sound_key;
 }
diff --git a/ash/accessibility/test_accessibility_controller_client.h b/ash/accessibility/test_accessibility_controller_client.h
index 42868d8..e1ff73f 100644
--- a/ash/accessibility/test_accessibility_controller_client.h
+++ b/ash/accessibility/test_accessibility_controller_client.h
@@ -23,8 +23,10 @@
   static constexpr base::TimeDelta kShutdownSoundDuration =
       base::TimeDelta::FromMilliseconds(1000);
 
-  // mojom::AccessibilityControllerClient:
+  // AccessibilityControllerClient:
   void TriggerAccessibilityAlert(AccessibilityAlert alert) override;
+  void TriggerAccessibilityAlertWithMessage(
+      const std::string& message) override;
   void PlayEarcon(int32_t sound_key) override;
   base::TimeDelta PlayShutdownSound() override;
   void HandleAccessibilityGesture(ax::mojom::Gesture gesture) override;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 84253700..061dd7e86 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1990,6 +1990,23 @@
       <message name="IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_ACCESSIBLE_NAME" desc="The accessible name for the lock screen media controls view.">
         Media Controls
       </message>
+      <message name="IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE" desc="The button to dismiss the media controls and pause playback.">
+        Close
+      </message>
+
+      <!-- Virtual desks -->
+      <message name="IDS_ASH_VIRTUAL_DESKS_ALERT_NEW_DESK_CREATED" desc="A new desk is created in virtual desks.">
+        Desk <ph name="DESK_TITILE">$1<ex>1</ex></ph> created
+      </message>
+      <message name="IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED" desc="A desk is removed in virtual desks.">
+        Desk <ph name="REMOVED_DESK">$1</ph> removed and merged with Desk <ph  name="RECEIVE_DESK">$2</ph>
+      </message>
+      <message name="IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED" desc="A desk in virtual desks is activated.">
+        Desk <ph name="DESK_TITILE">$1<ex>1</ex></ph> activated
+      </message>
+      <message name="IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK" desc="A window in active desk got be moved to another virtual desk.">
+        Window <ph name="WINDOW_TITLE">$1<ex>Files</ex></ph> moved from Desk <ph name="ACTIVE_DESK">$2</ph> to Desk <ph name="TARGET_DESK">$3</ph>
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/ash/ash_strings_grd/IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE.png.sha1
new file mode 100644
index 0000000..894e887
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE.png.sha1
@@ -0,0 +1 @@
+5b713ae9c0b4aeeeffdd44a78f1bf1c0e7dfaa8a
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1
new file mode 100644
index 0000000..1e88e90
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED.png.sha1
@@ -0,0 +1 @@
+8f947c92a5b23810ff5cd966d12aed537a18aed2
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1
new file mode 100644
index 0000000..dc28629
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED.png.sha1
@@ -0,0 +1 @@
+d60dbf64ba69bf06c2e14938aca74aa7d1de9f59
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_NEW_DESK_CREATED.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_NEW_DESK_CREATED.png.sha1
new file mode 100644
index 0000000..f87907f
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_NEW_DESK_CREATED.png.sha1
@@ -0,0 +1 @@
+45b46f0e5040d9cf871a398d350e59daf2cc8bd2
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1
new file mode 100644
index 0000000..f69f638
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK.png.sha1
@@ -0,0 +1 @@
+84e439c808c3505a80cb16d9c06aecb49074992a
\ No newline at end of file
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index d35e576..246f5a78 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1210,10 +1210,10 @@
 }
 
 bool LockContentsView::AreMediaControlsEnabled() const {
-  return Shell::Get()->media_controller()->AreLockScreenMediaKeysEnabled() &&
-         base::FeatureList::IsEnabled(features::kLockScreenMediaControls) &&
-         screen_type_ == LockScreen::ScreenType::kLock &&
-         !expanded_view_->GetVisible();
+  return screen_type_ == LockScreen::ScreenType::kLock &&
+         !expanded_view_->GetVisible() &&
+         Shell::Get()->media_controller()->AreLockScreenMediaKeysEnabled() &&
+         base::FeatureList::IsEnabled(features::kLockScreenMediaControls);
 }
 
 void LockContentsView::HideMediaControlsLayout() {
diff --git a/ash/login/ui/lock_screen_media_controls_view.cc b/ash/login/ui/lock_screen_media_controls_view.cc
index 4692774..61704d87 100644
--- a/ash/login/ui/lock_screen_media_controls_view.cc
+++ b/ash/login/ui/lock_screen_media_controls_view.cc
@@ -11,6 +11,7 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "components/media_message_center/media_notification_util.h"
+#include "components/vector_icons/vector_icons.h"
 #include "services/media_session/public/cpp/util.h"
 #include "services/media_session/public/mojom/constants.mojom.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
@@ -24,6 +25,7 @@
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/grid_layout.h"
 
 namespace ash {
 
@@ -41,22 +43,25 @@
 constexpr int kMediaControlsTotalWidthDp = 320;
 constexpr int kMediaControlsTotalHeightDp = 400;
 constexpr int kMediaControlsCornerRadius = 8;
-constexpr gfx::Insets kMediaControlsInsets = gfx::Insets(25, 25, 30, 25);
-constexpr int kMediaControlsChildSpacing = 30;
 constexpr int kMinimumIconSize = 16;
 constexpr int kDesiredIconSize = 20;
 constexpr int kIconSize = 20;
+constexpr gfx::Insets kArtworkInsets = gfx::Insets(0, 25, 0, 25);
 constexpr int kMinimumArtworkSize = 200;
 constexpr int kDesiredArtworkSize = 300;
 constexpr int kArtworkViewWidth = 270;
 constexpr int kArtworkViewHeight = 200;
 constexpr gfx::Size kMediaButtonSize = gfx::Size(45, 45);
+constexpr int kMediaButtonRowSeparator = 10;
+constexpr gfx::Insets kButtonRowInsets = gfx::Insets(25, 25, 30, 25);
 constexpr int kPlayPauseIconSize = 32;
 constexpr int kChangeTrackIconSize = 16;
 constexpr int kSeekingIconsSize = 28;
 constexpr gfx::Size kMediaControlsButtonRowSize =
     gfx::Size(270, kMediaButtonSize.height());
-constexpr int kMediaButtonRowSeparator = 10;
+constexpr int kCloseButtonOffset = 290;
+constexpr gfx::Size kCloseButtonSize = gfx::Size(24, 24);
+constexpr int kCloseButtonIconSize = 20;
 
 // How long to wait (in milliseconds) for a new media session to begin.
 constexpr base::TimeDelta kNextMediaDelay =
@@ -129,6 +134,11 @@
   DCHECK(callbacks.hide_media_controls);
   DCHECK(callbacks.show_media_controls);
 
+  set_notify_enter_exit_on_child(true);
+
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+
   SetBackground(views::CreateRoundedRectBackground(kMediaControlsBackground,
                                                    kMediaControlsCornerRadius));
   middle_spacing_ = std::make_unique<NonAccessibleView>();
@@ -137,22 +147,45 @@
   // Media controls have not been dismissed initially.
   Shell::Get()->media_controller()->SetMediaControlsDismissed(false);
 
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, kMediaControlsInsets,
-      kMediaControlsChildSpacing));
+  // |close_button_row| contains the close button to dismiss the controls.
+  auto close_button_row = std::make_unique<NonAccessibleView>();
+  views::GridLayout* close_button_layout =
+      close_button_row->SetLayoutManager(std::make_unique<views::GridLayout>());
+  views::ColumnSet* columns = close_button_layout->AddColumnSet(0);
 
+  columns->AddPaddingColumn(0, kCloseButtonOffset);
+  columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
+                     views::GridLayout::USE_PREF, 0, 0);
+  close_button_layout->StartRowWithPadding(
+      0, 0, 0, 5 /* padding between close button and top of view */);
+
+  auto close_button = CreateVectorImageButton(this);
+  SetImageFromVectorIcon(close_button.get(), vector_icons::kCloseRoundedIcon,
+                         kCloseButtonIconSize, gfx::kGoogleGrey700);
+  close_button->SetPreferredSize(kCloseButtonSize);
+  close_button->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+  base::string16 close_button_label(
+      l10n_util::GetStringUTF16(IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE));
+  close_button->SetAccessibleName(close_button_label);
+  close_button_ = close_button_layout->AddView(std::move(close_button));
+  close_button_->SetVisible(false);
+  AddChildView(std::move(close_button_row));
+
+  // |header_row_| contains the app icon and source title of the current media
+  // session.
   header_row_ = AddChildView(std::make_unique<MediaControlsHeaderView>());
 
   auto session_artwork = std::make_unique<views::ImageView>();
   session_artwork->SetPreferredSize(
       gfx::Size(kArtworkViewWidth, kArtworkViewHeight));
+  session_artwork->SetBorder(views::CreateEmptyBorder(kArtworkInsets));
   session_artwork_ = AddChildView(std::move(session_artwork));
 
   // |button_row_| contains the buttons for controlling playback.
   auto button_row = std::make_unique<NonAccessibleView>();
   auto* button_row_layout =
       button_row->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
+          views::BoxLayout::Orientation::kHorizontal, kButtonRowInsets,
           kMediaButtonRowSeparator));
   button_row_layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
@@ -259,6 +292,14 @@
     node_data->SetName(accessible_name_);
 }
 
+void LockScreenMediaControlsView::OnMouseEntered(const ui::MouseEvent& event) {
+  close_button_->SetVisible(true);
+}
+
+void LockScreenMediaControlsView::OnMouseExited(const ui::MouseEvent& event) {
+  close_button_->SetVisible(false);
+}
+
 views::View* LockScreenMediaControlsView::GetMiddleSpacingView() {
   return middle_spacing_.get();
 }
@@ -340,15 +381,28 @@
   if (hide_controls_timer_->IsRunning())
     return;
 
+  // Convert the bitmap to kN32_SkColorType if necessary.
+  SkBitmap converted_bitmap;
+  if (bitmap.colorType() == kN32_SkColorType) {
+    converted_bitmap = bitmap;
+  } else {
+    SkImageInfo info = bitmap.info().makeColorType(kN32_SkColorType);
+    if (converted_bitmap.tryAllocPixels(info)) {
+      bitmap.readPixels(info, converted_bitmap.getPixels(),
+                        converted_bitmap.rowBytes(), 0, 0);
+    }
+  }
+
   switch (type) {
     case media_session::mojom::MediaSessionImageType::kArtwork: {
       base::Optional<gfx::ImageSkia> session_artwork =
-          gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+          gfx::ImageSkia::CreateFrom1xBitmap(converted_bitmap);
       SetArtwork(session_artwork);
       break;
     }
     case media_session::mojom::MediaSessionImageType::kSourceIcon: {
-      gfx::ImageSkia session_icon = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+      gfx::ImageSkia session_icon =
+          gfx::ImageSkia::CreateFrom1xBitmap(converted_bitmap);
       if (session_icon.isNull()) {
         session_icon = gfx::CreateVectorIcon(message_center::kProductIcon,
                                              kIconSize, gfx::kChromeIconGrey);
@@ -360,6 +414,12 @@
 
 void LockScreenMediaControlsView::ButtonPressed(views::Button* sender,
                                                 const ui::Event& event) {
+  if (sender == close_button_) {
+    media_controller_ptr_->Stop();
+    hide_media_controls_.Run();
+    return;
+  }
+
   if (!base::Contains(enabled_actions_,
                       media_message_center::GetActionFromButtonTag(*sender)) ||
       !media_session_id_.has_value()) {
diff --git a/ash/login/ui/lock_screen_media_controls_view.h b/ash/login/ui/lock_screen_media_controls_view.h
index b0b83bca..9301cf1 100644
--- a/ash/login/ui/lock_screen_media_controls_view.h
+++ b/ash/login/ui/lock_screen_media_controls_view.h
@@ -21,6 +21,7 @@
 namespace views {
 class ImageView;
 class ToggleImageButton;
+class ImageButton;
 }  // namespace views
 
 namespace ash {
@@ -61,6 +62,8 @@
   const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
 
   views::View* GetMiddleSpacingView();
 
@@ -74,6 +77,8 @@
       override;
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override;
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   // media_session::mojom::MediaControllerImageObserver:
   void MediaControllerImageChanged(
@@ -155,6 +160,7 @@
   views::ImageView* session_artwork_ = nullptr;
   NonAccessibleView* button_row_ = nullptr;
   views::ToggleImageButton* play_pause_button_ = nullptr;
+  views::ImageButton* close_button_ = nullptr;
 
   // Callbacks.
   const MediaControlsEnabled media_controls_enabled_;
diff --git a/ash/login/ui/lock_screen_media_controls_view_unittest.cc b/ash/login/ui/lock_screen_media_controls_view_unittest.cc
index 862525e..d72d68f7 100644
--- a/ash/login/ui/lock_screen_media_controls_view_unittest.cc
+++ b/ash/login/ui/lock_screen_media_controls_view_unittest.cc
@@ -167,7 +167,11 @@
     return media_controls_view_->session_artwork_;
   }
 
-  const gfx::ImageSkia& GetAppIcon() const {
+  views::ImageButton* close_button() const {
+    return media_controls_view_->close_button_;
+  }
+
+  const views::ImageView* icon_view() const {
     return header_row()->app_icon_for_testing();
   }
 
@@ -211,7 +215,7 @@
       message_center::kProductIcon, kAppIconSize, gfx::kChromeIconGrey);
 
   // Verify that the default icon is not drawn.
-  EXPECT_FALSE(GetAppIcon().BackedBySameObjectAs(default_icon));
+  EXPECT_FALSE(icon_view()->GetImage().BackedBySameObjectAs(default_icon));
 
   // Set artwork for new media session.
   SkBitmap artwork;
@@ -266,13 +270,16 @@
   views::FocusManager* focus_manager = media_controls_view_->GetFocusManager();
 
   {
-    // Focus the first action button.
-    auto* button = GetButtonForAction(MediaSessionAction::kPreviousTrack);
-    focus_manager->SetFocusedView(button);
-    EXPECT_EQ(button, focus_manager->GetFocusedView());
+    // Focus the first action button - the close button.
+    focus_manager->SetFocusedView(close_button());
+    EXPECT_EQ(close_button(), focus_manager->GetFocusedView());
   }
 
   SimulateTab();
+  EXPECT_EQ(GetButtonForAction(MediaSessionAction::kPreviousTrack),
+            focus_manager->GetFocusedView());
+
+  SimulateTab();
   EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekBackward),
             focus_manager->GetFocusedView());
 
@@ -308,6 +315,49 @@
   EXPECT_NE(tooltip, new_tooltip);
 }
 
+TEST_F(LockScreenMediaControlsViewTest, CloseButtonVisibility) {
+  EXPECT_TRUE(media_controls_view_->IsDrawn());
+  EXPECT_FALSE(close_button()->IsDrawn());
+
+  // Move the mouse inside |media_controls_view_|.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->MoveMouseTo(
+      media_controls_view_->GetBoundsInScreen().CenterPoint());
+
+  // Verify that the close button is shown.
+  EXPECT_TRUE(close_button()->IsDrawn());
+
+  // Move the mouse outside |media_controls_view_|.
+  generator->MoveMouseBy(500, 500);
+
+  // Verify that the close button is hidden.
+  EXPECT_TRUE(media_controls_view_->IsDrawn());
+  EXPECT_FALSE(close_button()->IsDrawn());
+}
+
+TEST_F(LockScreenMediaControlsViewTest, CloseButtonClick) {
+  EXPECT_TRUE(media_controls_view_->IsDrawn());
+
+  // Move the mouse inside |media_controls_view_|.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->MoveMouseTo(
+      media_controls_view_->GetBoundsInScreen().CenterPoint());
+
+  EXPECT_TRUE(close_button()->IsDrawn());
+  EXPECT_EQ(0, media_controller()->stop_count());
+
+  // Send event to click the close button.
+  generator->MoveMouseTo(close_button()->GetBoundsInScreen().CenterPoint());
+  generator->ClickLeftButton();
+
+  // Verify that the media was stopped.
+  media_controls_view_->FlushForTesting();
+  EXPECT_EQ(1, media_controller()->stop_count());
+
+  // Verify that the controls were hidden.
+  EXPECT_FALSE(media_controls_view_->IsDrawn());
+}
+
 TEST_F(LockScreenMediaControlsViewTest, PreviousTrackButtonClick) {
   EnableAction(MediaSessionAction::kPreviousTrack);
 
@@ -385,17 +435,17 @@
       message_center::kProductIcon, kAppIconSize, gfx::kChromeIconGrey);
 
   // Verify that the icon is initialized to the default.
-  EXPECT_TRUE(GetAppIcon().BackedBySameObjectAs(default_icon));
-  EXPECT_EQ(kAppIconSize, GetAppIcon().width());
-  EXPECT_EQ(kAppIconSize, GetAppIcon().height());
+  EXPECT_TRUE(icon_view()->GetImage().BackedBySameObjectAs(default_icon));
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().width());
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().height());
 
   media_controls_view_->MediaControllerImageChanged(
       media_session::mojom::MediaSessionImageType::kSourceIcon, SkBitmap());
 
   // Verify that the default icon is used if no icon is provided.
-  EXPECT_TRUE(GetAppIcon().BackedBySameObjectAs(default_icon));
-  EXPECT_EQ(kAppIconSize, GetAppIcon().width());
-  EXPECT_EQ(kAppIconSize, GetAppIcon().height());
+  EXPECT_TRUE(icon_view()->GetImage().BackedBySameObjectAs(default_icon));
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().width());
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().height());
 
   SkBitmap bitmap;
   bitmap.allocN32Pixels(kAppIconSize, kAppIconSize);
@@ -403,9 +453,9 @@
       media_session::mojom::MediaSessionImageType::kSourceIcon, bitmap);
 
   // Verify that the provided icon is used.
-  EXPECT_FALSE(GetAppIcon().BackedBySameObjectAs(default_icon));
-  EXPECT_EQ(kAppIconSize, GetAppIcon().width());
-  EXPECT_EQ(kAppIconSize, GetAppIcon().height());
+  EXPECT_FALSE(icon_view()->GetImage().BackedBySameObjectAs(default_icon));
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().width());
+  EXPECT_EQ(kAppIconSize, icon_view()->GetImage().height());
 }
 
 TEST_F(LockScreenMediaControlsViewTest, UpdateAppName) {
@@ -429,6 +479,36 @@
   EXPECT_EQ(kTestAppName, GetAppName());
 }
 
+TEST_F(LockScreenMediaControlsViewTest, UpdateImagesConvertColors) {
+  SkBitmap artwork;
+  SkImageInfo artwork_info =
+      SkImageInfo::Make(200, 200, kAlpha_8_SkColorType, kOpaque_SkAlphaType);
+  artwork.allocPixels(artwork_info);
+
+  media_controls_view_->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, artwork);
+
+  // Verify the artwork color was converted.
+  EXPECT_EQ(artwork_view()->GetImage().bitmap()->colorType(), kN32_SkColorType);
+
+  // Verify the artwork is visible.
+  EXPECT_TRUE(artwork_view()->GetVisible());
+
+  SkBitmap icon;
+  SkImageInfo icon_info =
+      SkImageInfo::Make(20, 20, kAlpha_8_SkColorType, kOpaque_SkAlphaType);
+  artwork.allocPixels(icon_info);
+
+  media_controls_view_->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kSourceIcon, icon);
+
+  // Verify the icon color was converted.
+  EXPECT_EQ(icon_view()->GetImage().bitmap()->colorType(), kN32_SkColorType);
+
+  // Verify the icon is visible.
+  EXPECT_TRUE(icon_view()->GetVisible());
+}
+
 TEST_F(LockScreenMediaControlsViewTest, UpdateArtwork) {
   // Verify that the artwork is initially empty.
   EXPECT_TRUE(artwork_view()->GetImage().isNull());
diff --git a/ash/login/ui/login_expanded_public_account_view.cc b/ash/login/ui/login_expanded_public_account_view.cc
index 8272fee..b5cd47d 100644
--- a/ash/login/ui/login_expanded_public_account_view.cc
+++ b/ash/login/ui/login_expanded_public_account_view.cc
@@ -220,7 +220,9 @@
 // Container for the device monitoring warning.
 class MonitoringWarningView : public NonAccessibleView {
  public:
-  MonitoringWarningView() : NonAccessibleView(kMonitoringWarningClassName) {
+  MonitoringWarningView()
+      : NonAccessibleView(kMonitoringWarningClassName),
+        warning_type_(WarningType::kNone) {
     SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
         kSpacingBetweenMonitoringWarningIconAndLabelDp));
@@ -241,27 +243,46 @@
     AddChildView(label_);
   }
 
-  enum class WarningType { kSoftWarning, kFullWarning };
+  enum class WarningType { kNone, kSoftWarning, kFullWarning };
+
+  void UpdateForUser(const LoginUserInfo& user) {
+    enterprise_domain_ = user.public_account_info->enterprise_domain;
+    UpdateLabel();
+  }
 
   void SetWarningType(WarningType warning_type) {
-    base::string16 label_text;
-    if (warning_type == WarningType::kFullWarning) {
-      label_text = l10n_util::GetStringUTF16(
-          IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING);
-      image_->SetVisible(true);
-    } else if (warning_type == WarningType::kSoftWarning) {
-      label_text = l10n_util::GetStringUTF16(
-          IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING);
-      image_->SetVisible(false);
-    }
-    label_->SetText(label_text);
+    warning_type_ = warning_type;
+    UpdateLabel();
   }
 
   ~MonitoringWarningView() override = default;
 
  private:
+  void UpdateLabel() {
+    // Call sequence of UpdateForUser() and SetWarningType() is not clear.
+    // In case SetWarningType is called first there is a need to wait for
+    // enterprise_domain_ is set.
+    if (warning_type_ == WarningType::kNone || !enterprise_domain_.has_value())
+      return;
+    base::string16 label_text;
+    if (warning_type_ == WarningType::kFullWarning) {
+      label_text = l10n_util::GetStringFUTF16(
+          IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING,
+          base::UTF8ToUTF16(enterprise_domain_.value()));
+      image_->SetVisible(true);
+    } else {
+      label_text = l10n_util::GetStringFUTF16(
+          IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING,
+          base::UTF8ToUTF16(enterprise_domain_.value()));
+      image_->SetVisible(false);
+    }
+    label_->SetText(label_text);
+  }
+
   friend class LoginExpandedPublicAccountView::TestApi;
 
+  WarningType warning_type_;
+  base::Optional<std::string> enterprise_domain_;
   views::ImageView* image_;
   views::Label* label_;
 
@@ -458,6 +479,7 @@
   void UpdateForUser(const LoginUserInfo& user) {
     DCHECK_EQ(user.basic_user_info.type,
               user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+    monitoring_warning_view_->UpdateForUser(user);
     current_user_ = user;
     if (!language_changed_by_user_)
       selected_language_item_.value = user.public_account_info->default_locale;
@@ -666,6 +688,15 @@
   return view_->right_pane_->monitoring_warning_view_->image_;
 }
 
+views::Label*
+LoginExpandedPublicAccountView::TestApi::monitoring_warning_label() {
+  return view_->right_pane_->monitoring_warning_view_->label_;
+}
+
+void LoginExpandedPublicAccountView::TestApi::ResetUserForTest() {
+  view_->right_pane_->monitoring_warning_view_->enterprise_domain_.reset();
+}
+
 LoginExpandedPublicAccountView::LoginExpandedPublicAccountView(
     const OnPublicSessionViewDismissed& on_dismissed)
     : NonAccessibleView(kLoginExpandedPublicAccountViewClassName),
diff --git a/ash/login/ui/login_expanded_public_account_view.h b/ash/login/ui/login_expanded_public_account_view.h
index dfa6cb4..a44a6f87 100644
--- a/ash/login/ui/login_expanded_public_account_view.h
+++ b/ash/login/ui/login_expanded_public_account_view.h
@@ -12,6 +12,7 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ui/events/event_handler.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/view.h"
 
@@ -45,6 +46,8 @@
     LoginMenuView::Item selected_language_item();
     LoginMenuView::Item selected_keyboard_item();
     views::ImageView* monitoring_warning_icon();
+    views::Label* monitoring_warning_label();
+    void ResetUserForTest();
 
    private:
     LoginExpandedPublicAccountView* const view_;
diff --git a/ash/login/ui/login_expanded_public_account_view_unittest.cc b/ash/login/ui/login_expanded_public_account_view_unittest.cc
index 1cde40d3..8a504b64 100644
--- a/ash/login/ui/login_expanded_public_account_view_unittest.cc
+++ b/ash/login/ui/login_expanded_public_account_view_unittest.cc
@@ -11,8 +11,11 @@
 #include "ash/login/ui/public_account_warning_dialog.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/login_types.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "base/bind_helpers.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
@@ -293,6 +296,31 @@
   EXPECT_EQ(test_api.selected_keyboard_item().value, kKeyboardIdForItem1);
 }
 
+TEST_P(LoginExpandedPublicAccountViewTest, ChangeWarningLabel) {
+  LoginExpandedPublicAccountView::TestApi test_api(public_account_);
+  views::Label* label = test_api.monitoring_warning_label();
+  test_api.ResetUserForTest();
+  const base::string16 default_warning = l10n_util::GetStringUTF16(
+      IDS_ASH_LOGIN_PUBLIC_ACCOUNT_MONITORING_WARNING);
+  EXPECT_EQ(label->GetText(), default_warning);
+
+  public_account_->SetShowFullManagementDisclosure(false);
+  EXPECT_EQ(label->GetText(), default_warning);
+  const std::string domain =
+      user_.public_account_info->enterprise_domain.value();
+  public_account_->UpdateForUser(user_);
+  const base::string16 soft_warning = l10n_util::GetStringFUTF16(
+      IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING,
+      base::UTF8ToUTF16(domain));
+  EXPECT_EQ(label->GetText(), soft_warning);
+
+  public_account_->SetShowFullManagementDisclosure(true);
+  const base::string16 full_warning = l10n_util::GetStringFUTF16(
+      IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING,
+      base::UTF8ToUTF16(domain));
+  EXPECT_EQ(label->GetText(), full_warning);
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          LoginExpandedPublicAccountViewTest,
                          ::testing::Values("mouse", "touch"));
diff --git a/ash/login/ui/media_controls_header_view.cc b/ash/login/ui/media_controls_header_view.cc
index 103dd42738..b6fcda3 100644
--- a/ash/login/ui/media_controls_header_view.cc
+++ b/ash/login/ui/media_controls_header_view.cc
@@ -16,6 +16,7 @@
 
 namespace {
 
+constexpr gfx::Insets kMediaControlsHeaderInsets = gfx::Insets(0, 25, 25, 25);
 constexpr int kIconSize = 20;
 constexpr int kHeaderTextFontSize = 14;
 constexpr int kMediaControlsHeaderChildSpacing = 10;
@@ -26,7 +27,7 @@
 
 MediaControlsHeaderView::MediaControlsHeaderView() {
   SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
+      views::BoxLayout::Orientation::kHorizontal, kMediaControlsHeaderInsets,
       kMediaControlsHeaderChildSpacing));
 
   auto app_icon_view = std::make_unique<views::ImageView>();
@@ -71,8 +72,8 @@
   return app_name_view_->GetText();
 }
 
-const gfx::ImageSkia& MediaControlsHeaderView::app_icon_for_testing() const {
-  return app_icon_view_->GetImage();
+const views::ImageView* MediaControlsHeaderView::app_icon_for_testing() const {
+  return app_icon_view_;
 }
 
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/login/ui/media_controls_header_view.h b/ash/login/ui/media_controls_header_view.h
index 3d6154b..d3ef5dc1 100644
--- a/ash/login/ui/media_controls_header_view.h
+++ b/ash/login/ui/media_controls_header_view.h
@@ -27,7 +27,7 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   const base::string16& app_name_for_testing() const;
-  const gfx::ImageSkia& app_icon_for_testing() const;
+  const views::ImageView* app_icon_for_testing() const;
 
  private:
   views::ImageView* app_icon_view_;
diff --git a/ash/media/media_controller_impl.cc b/ash/media/media_controller_impl.cc
index 0b6315e..789a4ad 100644
--- a/ash/media/media_controller_impl.cc
+++ b/ash/media/media_controller_impl.cc
@@ -53,6 +53,8 @@
 bool MediaControllerImpl::AreLockScreenMediaKeysEnabled() const {
   PrefService* prefs =
       Shell::Get()->session_controller()->GetPrimaryUserPrefService();
+  DCHECK(prefs);
+
   return base::FeatureList::IsEnabled(features::kLockScreenMediaKeys) &&
          prefs->GetBoolean(prefs::kLockScreenMediaKeysEnabled) &&
          !media_controls_dismissed_;
diff --git a/ash/media/media_controller_impl.h b/ash/media/media_controller_impl.h
index 4e0e1a4..a611d77 100644
--- a/ash/media/media_controller_impl.h
+++ b/ash/media/media_controller_impl.h
@@ -81,6 +81,8 @@
       override;
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
  private:
   friend class MediaControllerTest;
diff --git a/ash/public/cpp/accessibility_controller_client.h b/ash/public/cpp/accessibility_controller_client.h
index c9cc1448..58a07d76 100644
--- a/ash/public/cpp/accessibility_controller_client.h
+++ b/ash/public/cpp/accessibility_controller_client.h
@@ -28,6 +28,10 @@
   // Triggers an accessibility alert to give the user feedback.
   virtual void TriggerAccessibilityAlert(AccessibilityAlert alert) = 0;
 
+  // Triggers an accessibility alert with the given |message|.
+  virtual void TriggerAccessibilityAlertWithMessage(
+      const std::string& message) = 0;
+
   // Plays an earcon. Earcons are brief and distinctive sounds that indicate
   // that their mapped event has occurred. The |sound_key| enums can be found in
   // chromeos/audio/chromeos_sounds.h. This method exists because the browser
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
index 835abe9..1de741e 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -177,6 +177,9 @@
       bubble_widget_->GetLayer()->GetAnimator());
   settings.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  settings.SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
+  settings.SetTweenType(gfx::Tween::EASE_OUT);
   bubble_widget_->SetBounds(resting_bounds);
 
   if (!scroll_bubble_controller_)
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.h b/ash/system/accessibility/autoclick_menu_bubble_controller.h
index 2dc3b4a..302c5086 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.h
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.h
@@ -20,6 +20,10 @@
     : public TrayBubbleView::Delegate,
       public LocaleChangeObserver {
  public:
+  // The duration of the position change animation for the menu and scroll
+  // bubbles in milliseconds.
+  static const int kAnimationDurationMs = 150;
+
   AutoclickMenuBubbleController();
   ~AutoclickMenuBubbleController() override;
 
diff --git a/ash/system/accessibility/autoclick_scroll_view.cc b/ash/system/accessibility/autoclick_scroll_view.cc
index 042ec38..0f3b3fc 100644
--- a/ash/system/accessibility/autoclick_scroll_view.cc
+++ b/ash/system/accessibility/autoclick_scroll_view.cc
@@ -8,6 +8,7 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/custom_shape_button.h"
 #include "ash/system/unified/top_shortcut_button.h"
@@ -334,6 +335,9 @@
       GetWidget()->GetLayer()->GetAnimator());
   settings.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
+      AutoclickMenuBubbleController::kAnimationDurationMs));
+  settings.SetTweenType(gfx::Tween::EASE_OUT);
   // SetAnchorRect will resize, so set the arrow without reizing to avoid a
   // double animation.
   SetArrowWithoutResizing(arrow);
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index cd1b92c..fba3711 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -207,7 +207,8 @@
 constexpr int kUnifiedFeaturePodCollapsedHorizontalPadding = 24;
 constexpr int kUnifiedFeaturePodArrowSpacing = 4;
 constexpr int kUnifiedFeaturePodItemsInRow = 3;
-constexpr int kUnifiedFeaturePodItemsRows = 3;
+constexpr int kUnifiedFeaturePodMaxRows = 3;
+constexpr int kUnifiedFeaturePodMinRows = 1;
 constexpr int kUnifiedFeaturePodMaxItemsInCollapsed = 5;
 constexpr int kUnifiedFeaturePodsPageSpacing = 48;
 constexpr int kUnifiedNotificationSeparatorThickness = 1;
diff --git a/ash/system/unified/feature_pods_container_view.cc b/ash/system/unified/feature_pods_container_view.cc
index 4b52c1a..c36c936eea 100644
--- a/ash/system/unified/feature_pods_container_view.cc
+++ b/ash/system/unified/feature_pods_container_view.cc
@@ -18,7 +18,8 @@
     bool initially_expanded)
     : controller_(controller),
       pagination_model_(controller->model()->pagination_model()),
-      expanded_amount_(initially_expanded ? 1.0 : 0.0) {
+      expanded_amount_(initially_expanded ? 1.0 : 0.0),
+      feature_pod_rows_(kUnifiedFeaturePodMaxRows) {
   pagination_model_->AddObserver(this);
 }
 
@@ -46,13 +47,12 @@
       button->SetExpandedAmount(1.0 - expanded_amount,
                                 true /* fade_icon_button */);
     } else if (visible_index > kUnifiedFeaturePodMaxItemsInCollapsed) {
-      int row = (visible_index / kUnifiedFeaturePodItemsInRow) %
-                kUnifiedFeaturePodItemsRows;
+      int row =
+          (visible_index / kUnifiedFeaturePodItemsInRow) % feature_pod_rows_;
       double button_expanded_amount =
           expanded_amount
-              ? std::min(1.0,
-                         expanded_amount +
-                             (0.25 * (kUnifiedFeaturePodItemsRows - row - 1)))
+              ? std::min(1.0, expanded_amount +
+                                  (0.25 * (feature_pod_rows_ - row - 1)))
               : expanded_amount;
       button->SetExpandedAmount(button_expanded_amount,
                                 true /* fade_icon_button */);
@@ -75,7 +75,7 @@
                         kUnifiedFeaturePodItemsInRow;
 
   if (features::IsSystemTrayFeaturePodsPaginationEnabled())
-    number_of_lines = std::min(number_of_lines, kUnifiedFeaturePodItemsRows);
+    number_of_lines = std::min(number_of_lines, feature_pod_rows_);
 
   return kUnifiedFeaturePodBottomPadding +
          (kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
@@ -208,6 +208,26 @@
       y * expanded_amount_ + collapsed_y * (1.0 - expanded_amount_));
 }
 
+void FeaturePodsContainerView::SetMaxHeight(int max_height) {
+  int feature_pod_rows =
+      (max_height - kUnifiedFeaturePodBottomPadding -
+       kUnifiedFeaturePodTopPadding) /
+      (kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding);
+
+  std::cout << (max_height - kUnifiedFeaturePodBottomPadding -
+                kUnifiedFeaturePodTopPadding) /
+                   (kUnifiedFeaturePodSize.height() +
+                    kUnifiedFeaturePodVerticalPadding)
+            << std::endl;
+  feature_pod_rows = std::min(feature_pod_rows, kUnifiedFeaturePodMaxRows);
+  feature_pod_rows = std::max(feature_pod_rows, kUnifiedFeaturePodMinRows);
+
+  if (feature_pod_rows_ != feature_pod_rows) {
+    feature_pod_rows_ = feature_pod_rows;
+    UpdateTotalPages();
+  }
+}
+
 void FeaturePodsContainerView::UpdateCollapsedSidePadding() {
   const int visible_count =
       std::min(GetVisibleCount(), kUnifiedFeaturePodMaxItemsInCollapsed);
@@ -301,7 +321,7 @@
 
 int FeaturePodsContainerView::GetTilesPerPage() const {
   if (features::IsSystemTrayFeaturePodsPaginationEnabled())
-    return kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows;
+    return kUnifiedFeaturePodItemsInRow * feature_pod_rows_;
   else
     return children().size();
 }
diff --git a/ash/system/unified/feature_pods_container_view.h b/ash/system/unified/feature_pods_container_view.h
index e41589a..36b7a5c5 100644
--- a/ash/system/unified/feature_pods_container_view.h
+++ b/ash/system/unified/feature_pods_container_view.h
@@ -37,6 +37,10 @@
   // horizontally placed.
   void SetExpandedAmount(double expanded_amount);
 
+  // Set the number of rows of feature pods based on the max height the
+  // container can have.
+  void SetMaxHeight(int max_height);
+
   // Get height of the view when |expanded_amount| is set to 1.0.
   int GetExpandedHeight() const;
 
@@ -55,7 +59,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   void OnScrollEvent(ui::ScrollEvent* event) override;
   bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
-
   const char* GetClassName() const override;
 
  private:
@@ -97,6 +100,10 @@
   // The last |expanded_amount| passed to SetExpandedAmount().
   double expanded_amount_;
 
+  // Number of rows of feature pods to display. Updated based on the available
+  // max height for FeaturePodsContainer.
+  int feature_pod_rows_ = 0;
+
   // Horizontal side padding in dip for collapsed state.
   int collapsed_side_padding_ = 0;
 
diff --git a/ash/system/unified/feature_pods_container_view_unittest.cc b/ash/system/unified/feature_pods_container_view_unittest.cc
index 0d387c0b..df0319b4 100644
--- a/ash/system/unified/feature_pods_container_view_unittest.cc
+++ b/ash/system/unified/feature_pods_container_view_unittest.cc
@@ -197,7 +197,7 @@
   const int kNumberOfPages = 8;
 
   EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
+  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
              kNumberOfPages);
 
   // Adding buttons to fill kNumberOfPages should cause the the same number of
@@ -213,7 +213,7 @@
   const int kNumberOfPages = 8;
 
   EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
+  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
              kNumberOfPages);
 
   // Position of a button should slide to the left during a page
@@ -248,11 +248,46 @@
   EXPECT_EQ(final_bounds, buttons_[0]->bounds());
 }
 
+TEST_F(FeaturePodsContainerViewTest, PaginationDynamicRows) {
+  const int kNumberOfFeaturePods = kUnifiedFeaturePodItemsInRow * 3;
+  const int padding =
+      kUnifiedFeaturePodTopPadding + kUnifiedFeaturePodBottomPadding;
+
+  EnablePagination();
+  AddButtons(kNumberOfFeaturePods);
+
+  // Expect 1 row of feature pods even if there is 0 height.
+  container()->SetMaxHeight(0);
+  int expected_number_of_pages =
+      kNumberOfFeaturePods / kUnifiedFeaturePodItemsInRow;
+  if (kNumberOfFeaturePods % kUnifiedFeaturePodItemsInRow)
+    expected_number_of_pages += 1;
+  EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
+
+  // Expect 2 rows of feature pods when there is enough height to display them.
+  container()->SetMaxHeight(padding +
+                            (2 * (kUnifiedFeaturePodSize.height() +
+                                  kUnifiedFeaturePodVerticalPadding)));
+  expected_number_of_pages =
+      kNumberOfFeaturePods / (2 * kUnifiedFeaturePodItemsInRow);
+  if (kNumberOfFeaturePods % (2 * kUnifiedFeaturePodItemsInRow))
+    expected_number_of_pages += 1;
+  EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
+
+  // Expect 3 rows of feature pods at max even when the max height is very
+  // large.
+  container()->SetMaxHeight(100 * (kUnifiedFeaturePodSize.height()));
+  expected_number_of_pages =
+      kNumberOfFeaturePods / (3 * kUnifiedFeaturePodItemsInRow);
+  if (kNumberOfFeaturePods % (3 * kUnifiedFeaturePodItemsInRow))
+    expected_number_of_pages += 1;
+  EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
+}
 TEST_F(FeaturePodsContainerViewTest, PaginationGestureHandling) {
   const int kNumberOfPages = 8;
 
   EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
+  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
              kNumberOfPages);
 
   gfx::Point container_origin = container()->GetBoundsInScreen().origin();
@@ -320,7 +355,7 @@
   const int num_fingers = 2;
 
   EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
+  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
              kNumberOfPages);
 
   EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
@@ -371,7 +406,7 @@
   const int kNumberOfPages = 8;
 
   EnablePagination();
-  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
+  AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
              kNumberOfPages);
 
   gfx::Point container_origin = container()->GetBoundsInScreen().origin();
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index e6a48aff..8dbb582 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -298,6 +298,15 @@
   max_height_ = max_height;
   message_center_view_->SetMaxHeight(max_height_);
 
+  // FeaturePodsContainer can adjust it's height by reducing the number of rows
+  // it uses. It will calculate how many rows to use based on the max height
+  // passed here.
+  feature_pods_container_->SetMaxHeight(
+      max_height - top_shortcuts_view_->GetPreferredSize().height() -
+      page_indicator_view_->GetPreferredSize().height() -
+      sliders_container_->GetPreferredSize().height() -
+      system_info_view_->GetPreferredSize().height());
+
   // Because the message center view requires a certain height to be usable, it
   // will be hidden if there isn't sufficient remaining height.
   int system_tray_height = expanded_amount_ > 0.0
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index 7f32810b..e44f2d9 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -170,12 +170,16 @@
   // The windows on this root are about to be destroyed. We already stopped
   // observing the container above, so we won't get a call to
   // DeskContainerObserver::OnWindowRemoved(). Therefore, we must remove those
-  // windows manually. This happens when the last root window is destroyed (i.e.
-  // there are no more roots to move those windows to). This typically happen
-  // when shutting down.
-  const auto roots = Shell::GetAllRootWindows();
-  if (roots.size() == 1u && roots[0] == root)
-    windows_.clear();
+  // windows manually. If this is part of shutdown (i.e. when the
+  // RootWindowController is being destroyed), then we're done with those
+  // windows. If this is due to a display being removed, then the
+  // WindowTreeHostManager will move those windows to another host/root, and
+  // they will be added again to the desk container on the new root.
+  const auto windows = windows_;
+  for (auto* window : windows) {
+    if (window->GetRootWindow() == root)
+      base::Erase(windows_, window);
+  }
 }
 
 void Desk::AddWindowToDesk(aura::Window* window) {
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index e38b1d1..def8041 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -6,8 +6,10 @@
 
 #include <utility>
 
+#include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/desks/root_window_desk_switch_animator.h"
@@ -22,6 +24,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/compositor.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -146,6 +149,11 @@
 
   UMA_HISTOGRAM_ENUMERATION(kNewDeskHistogramName, source);
   ReportDesksCountHistogram();
+  Shell::Get()
+      ->accessibility_controller()
+      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
+          IDS_ASH_VIRTUAL_DESKS_ALERT_NEW_DESK_CREATED,
+          base::NumberToString16(desks_.size())));
 
   for (auto& observer : observers_)
     observer.OnDeskAdded(desks_.back().get());
@@ -162,6 +170,9 @@
       [desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
   DCHECK(iter != desks_.end());
 
+  // Used by accessibility to indicate the desk that has been removed.
+  const int removed_desk_number = std::distance(desks_.begin(), iter) + 1;
+
   // Keep the removed desk alive until the end of this function.
   std::unique_ptr<Desk> removed_desk = std::move(*iter);
   DCHECK_EQ(removed_desk.get(), desk);
@@ -269,6 +280,16 @@
   ReportDesksCountHistogram();
   ReportNumberOfWindowsPerDeskHistogram();
 
+  int active_desk_number = GetDeskIndex(active_desk_) + 1;
+  if (active_desk_number == removed_desk_number)
+    active_desk_number++;
+  Shell::Get()
+      ->accessibility_controller()
+      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
+          IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED,
+          base::NumberToString16(removed_desk_number),
+          base::NumberToString16(active_desk_number)));
+
   DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
 }
 
@@ -293,6 +314,12 @@
     return;
   }
 
+  Shell::Get()
+      ->accessibility_controller()
+      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
+          IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_ACTIVATED,
+          base::NumberToString16(GetDeskIndex(desk) + 1)));
+
   // New desks are always added at the end of the list to the right of existing
   // desks. Therefore, desks at lower indices are located on the left of desks
   // with higher indices.
@@ -324,6 +351,14 @@
 
   active_desk_->MoveWindowToDesk(window, target_desk);
 
+  Shell::Get()
+      ->accessibility_controller()
+      ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
+          IDS_ASH_VIRTUAL_DESKS_ALERT_WINDOW_MOVED_FROM_ACTIVE_DESK,
+          window->GetTitle(),
+          base::NumberToString16(GetDeskIndex(active_desk_) + 1),
+          base::NumberToString16(GetDeskIndex(target_desk) + 1)));
+
   if (in_overview) {
     DCHECK(overview_controller->InOverviewSession());
     auto* overview_session = overview_controller->overview_session();
diff --git a/base/allocator/partition_allocator/memory_reclaimer.cc b/base/allocator/partition_allocator/memory_reclaimer.cc
index 1a135c7..991576c 100644
--- a/base/allocator/partition_allocator/memory_reclaimer.cc
+++ b/base/allocator/partition_allocator/memory_reclaimer.cc
@@ -15,8 +15,6 @@
 
 namespace internal {
 
-const Feature kNoPartitionAllocDecommit{"NoPartitionAllocDecommit",
-                                        FEATURE_DISABLED_BY_DEFAULT};
 // TODO(crbug.com/942512): Remove the feature after the M77 branch.
 const Feature kPartitionAllocPeriodicDecommit{"PartitionAllocPeriodicDecommit",
                                               FEATURE_ENABLED_BY_DEFAULT};
@@ -26,8 +24,7 @@
 namespace {
 
 bool IsDeprecatedDecommitEnabled() {
-  return !(FeatureList::IsEnabled(internal::kNoPartitionAllocDecommit) ||
-           FeatureList::IsEnabled(internal::kPartitionAllocPeriodicDecommit));
+  return !FeatureList::IsEnabled(internal::kPartitionAllocPeriodicDecommit);
 }
 
 }  // namespace
diff --git a/base/allocator/partition_allocator/memory_reclaimer.h b/base/allocator/partition_allocator/memory_reclaimer.h
index 87987f58..eca30a7 100644
--- a/base/allocator/partition_allocator/memory_reclaimer.h
+++ b/base/allocator/partition_allocator/memory_reclaimer.h
@@ -25,7 +25,6 @@
 
 struct PartitionRootBase;
 
-BASE_EXPORT extern const Feature kNoPartitionAllocDecommit;
 BASE_EXPORT extern const Feature kPartitionAllocPeriodicDecommit;
 
 }  // namespace internal
diff --git a/base/memory/memory_pressure_monitor_chromeos.cc b/base/memory/memory_pressure_monitor_chromeos.cc
index 91a2dd3..043d43ba 100644
--- a/base/memory/memory_pressure_monitor_chromeos.cc
+++ b/base/memory/memory_pressure_monitor_chromeos.cc
@@ -93,7 +93,7 @@
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::WILL_BLOCK);
 
-  pollfd pfd = {available_fd, POLLPRI | POLLERR, 0};
+  pollfd pfd = {available_fd, POLLPRI | POLLERR | POLLIN, 0};
   int res = HANDLE_EINTR(poll(&pfd, 1, -1));  // Wait indefinitely.
   PCHECK(res != -1);
 
diff --git a/base/system/sys_info_fuchsia.cc b/base/system/sys_info_fuchsia.cc
index 8206aa52..7a6a8a7 100644
--- a/base/system/sys_info_fuchsia.cc
+++ b/base/system/sys_info_fuchsia.cc
@@ -20,9 +20,8 @@
 
 // static
 int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
-  // TODO(https://crbug.com/706592): This method doesn't have an Fuchsia API to
-  // consume it
-  NOTREACHED();
+  // TODO(https://crbug.com/986608): Implement this.
+  NOTIMPLEMENTED_LOG_ONCE();
   return 0;
 }
 
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index e23a5579..de8671c1 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -10,7 +10,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_param_associator.h"
-#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -66,6 +65,19 @@
   std::vector<StringPiece> disabled_feature_list;
 };
 
+// Features in |feature_vector| came from |merged_features| in
+// OverrideFeatures() and contains linkage with field trial is case when they
+// have parameters (with '<' simbol). In |feature_name| name is already cleared
+// with GetFeatureName() and also could be without parameters.
+bool ContainsFeature(const std::vector<StringPiece>& feature_vector,
+                     StringPiece feature_name) {
+  auto iter = std::find_if(feature_vector.begin(), feature_vector.end(),
+                           [&feature_name](const StringPiece& a) {
+                             return GetFeatureName(a) == feature_name;
+                           });
+  return iter != feature_vector.end();
+}
+
 // Merges previously-specified feature overrides with those passed into one of
 // the Init() methods. |features| should be a list of features previously
 // overridden to be in the |override_state|. |merged_features| should contain
@@ -80,8 +92,8 @@
   for (StringPiece feature : features_list) {
     StringPiece feature_name = GetFeatureName(feature);
 
-    if (Contains(merged_features->enabled_feature_list, feature_name) ||
-        Contains(merged_features->disabled_feature_list, feature_name)) {
+    if (ContainsFeature(merged_features->enabled_feature_list, feature_name) ||
+        ContainsFeature(merged_features->disabled_feature_list, feature_name)) {
       continue;
     }
 
@@ -102,6 +114,8 @@
 
 // Inverse of HexEncodeString().
 std::string HexDecodeString(const std::string& input) {
+  if (input.empty())
+    return std::string();
   std::vector<uint8_t> bytes;
   bool result = HexStringToBytes(input, &bytes);
   DCHECK(result);
diff --git a/base/test/scoped_feature_list_unittest.cc b/base/test/scoped_feature_list_unittest.cc
index 7e251f6..46b7cb17 100644
--- a/base/test/scoped_feature_list_unittest.cc
+++ b/base/test/scoped_feature_list_unittest.cc
@@ -275,6 +275,27 @@
   EXPECT_EQ(kValue, GetFieldTrialParamValueByFeature(kTestFeature1, kParam));
 }
 
+TEST_F(ScopedFeatureListTest, ParamsWithEmptyValue) {
+  const char kParam[] = "p";
+  const char kEmptyValue[] = "";
+  FieldTrialParams params = {{kParam, kEmptyValue}};
+
+  test::ScopedFeatureList feature_list0;
+  feature_list0.InitWithFeaturesAndParameters({{kTestFeature1, params}}, {});
+  EXPECT_EQ(kEmptyValue,
+            GetFieldTrialParamValueByFeature(kTestFeature1, kParam));
+  {
+    const char kValue1[] = "normal";
+    FieldTrialParams params1 = {{kParam, kValue1}};
+    test::ScopedFeatureList feature_list1;
+    feature_list1.InitWithFeaturesAndParameters({{kTestFeature1, params1}}, {});
+
+    EXPECT_EQ(kValue1, GetFieldTrialParamValueByFeature(kTestFeature1, kParam));
+  }
+  EXPECT_EQ(kEmptyValue,
+            GetFieldTrialParamValueByFeature(kTestFeature1, kParam));
+}
+
 TEST_F(ScopedFeatureListTest, EnableFeatureOverrideDisable) {
   test::ScopedFeatureList feature_list1;
   feature_list1.InitWithFeatures({}, {kTestFeature1});
@@ -398,5 +419,16 @@
   ExpectFeatures("*TestFeature1", std::string());
 }
 
+TEST(ScopedFeatureListTestWithMemberList, ScopedFeatureListLocalOverride) {
+  test::ScopedFeatureList initial_feature_list;
+  initial_feature_list.InitAndDisableFeature(kTestFeature1);
+  {
+    base::test::ScopedFeatureList scoped_features;
+    scoped_features.InitAndEnableFeatureWithParameters(kTestFeature1,
+                                                       {{"mode", "nobugs"}});
+    ASSERT_TRUE(FeatureList::IsEnabled(kTestFeature1));
+  }
+}
+
 }  // namespace test
 }  // namespace base
diff --git a/base/win/i18n.cc b/base/win/i18n.cc
index eb44c496..04f533a 100644
--- a/base/win/i18n.cc
+++ b/base/win/i18n.cc
@@ -7,146 +7,40 @@
 #include <windows.h>
 
 #include "base/logging.h"
-#include "base/stl_util.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 
 namespace {
 
-// Keep this enum in sync with kLanguageFunctionNames.
-enum LanguageFunction {
-  SYSTEM_LANGUAGES,
-  USER_LANGUAGES,
-  PROCESS_LANGUAGES,
-  THREAD_LANGUAGES,
-  NUM_FUNCTIONS
-};
+typedef decltype(::GetSystemPreferredUILanguages)* GetPreferredUILanguages_Fn;
 
-const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages";
-const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages";
-const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages";
-const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages";
-
-// Keep this array in sync with enum LanguageFunction.
-const char *const kLanguageFunctionNames[] = {
-  &kSystemLanguagesFunctionName[0],
-  &kUserLanguagesFunctionName[0],
-  &kProcessLanguagesFunctionName[0],
-  &kThreadLanguagesFunctionName[0]
-};
-
-static_assert(NUM_FUNCTIONS == base::size(kLanguageFunctionNames),
-              "LanguageFunction enum and kLanguageFunctionNames array must be "
-              "kept in sync");
-
-// Calls one of the MUI Get*PreferredUILanguages functions, placing the result
-// in |languages|.  |function| identifies the function to call and |flags| is
-// the function-specific flags (callers must not specify MUI_LANGUAGE_ID or
-// MUI_LANGUAGE_NAME).  Returns true if at least one language is placed in
-// |languages|.
-bool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags,
-                                   std::vector<wchar_t>* languages) {
-  DCHECK(0 <= function && NUM_FUNCTIONS > function);
-  DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)));
-  DCHECK(languages);
-
-  HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
-  if (NULL != kernel32) {
-    typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)(
-        DWORD, PULONG, PZZWSTR, PULONG);
-    GetPreferredUILanguages_Fn get_preferred_ui_languages =
-        reinterpret_cast<GetPreferredUILanguages_Fn>(
-            GetProcAddress(kernel32, kLanguageFunctionNames[function]));
-    if (NULL != get_preferred_ui_languages) {
-      const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
-      ULONG language_count = 0;
-      ULONG buffer_length = 0;
-      if (get_preferred_ui_languages(call_flags, &language_count, NULL,
-                                     &buffer_length) &&
-          0 != buffer_length) {
-        languages->resize(buffer_length);
-        if (get_preferred_ui_languages(call_flags, &language_count,
-                                       &(*languages)[0], &buffer_length) &&
-            0 != language_count) {
-          DCHECK(languages->size() == buffer_length);
-          return true;
-        } else {
-          DPCHECK(0 == language_count)
-              << "Failed getting preferred UI languages.";
-        }
-      } else {
-        DPCHECK(0 == buffer_length)
-            << "Failed getting size of preferred UI languages.";
-      }
-    } else {
-      DVLOG(2) << "MUI not available.";
-    }
-  } else {
-    NOTREACHED() << "kernel32.dll not found.";
-  }
-
-  return false;
-}
-
-bool GetUserDefaultUILanguage(base::string16* language,
-                              base::string16* region) {
-  DCHECK(language);
-
-  LANGID lang_id = ::GetUserDefaultUILanguage();
-  if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) {
-    const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT);
-    // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9
-    base::char16 result_buffer[9];
-    int result_length = GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME,
-                                      base::as_writable_wcstr(result_buffer),
-                                      base::size(result_buffer));
-    DPCHECK(0 != result_length) << "Failed getting language id";
-    if (1 < result_length) {
-      language->assign(&result_buffer[0], result_length - 1);
-      region->clear();
-      if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
-        result_length = GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME,
-                                      base::as_writable_wcstr(result_buffer),
-                                      base::size(result_buffer));
-        DPCHECK(0 != result_length) << "Failed getting region id";
-        if (1 < result_length)
-          region->assign(&result_buffer[0], result_length - 1);
-      }
-      return true;
-    }
-  } else {
-    // This is entirely unexpected on pre-Vista, which is the only time we
-    // should try GetUserDefaultUILanguage anyway.
-    NOTREACHED() << "Cannot determine language for a supplemental locale.";
-  }
-  return false;
-}
-
-bool GetPreferredUILanguageList(LanguageFunction function,
+bool GetPreferredUILanguageList(GetPreferredUILanguages_Fn function,
                                 ULONG flags,
                                 std::vector<base::string16>* languages) {
-  std::vector<wchar_t> buffer;
-  base::string16 language;
-  base::string16 region;
-
-  if (GetMUIPreferredUILanguageList(function, flags, &buffer)) {
-    std::vector<wchar_t>::const_iterator scan = buffer.begin();
-    language = base::WideToUTF16(&*scan);
-    while (!language.empty()) {
-      languages->push_back(language);
-      scan += language.size() + 1;
-      language = base::WideToUTF16(&*scan);
-    }
-  } else if (GetUserDefaultUILanguage(&language, &region)) {
-    // Mimic the MUI behavior of putting the neutral version of the lang after
-    // the regional one (e.g., "fr-CA, fr").
-    if (!region.empty())
-      languages->push_back(language + base::string16(1, '-') + region);
-    languages->push_back(language);
-  } else {
+  DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)));
+  const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
+  ULONG language_count = 0;
+  ULONG buffer_length = 0;
+  if (!function(call_flags, &language_count, nullptr, &buffer_length) ||
+      0 == buffer_length) {
+    DPCHECK(0 == buffer_length)
+        << "Failed getting size of preferred UI languages.";
     return false;
   }
 
+  base::string16 buffer(buffer_length, '\0');
+  if (!function(call_flags, &language_count, base::as_writable_wcstr(buffer),
+                &buffer_length) ||
+      0 == language_count) {
+    DPCHECK(0 == language_count) << "Failed getting preferred UI languages.";
+    return false;
+  }
+
+  // Split string on NUL characters.
+  *languages =
+      base::SplitString(buffer, base::string16(1, '\0'), base::KEEP_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+  DCHECK_EQ(languages->size(), language_count);
   return true;
 }
 
@@ -158,14 +52,15 @@
 
 bool GetUserPreferredUILanguageList(std::vector<base::string16>* languages) {
   DCHECK(languages);
-  return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
+  return GetPreferredUILanguageList(::GetUserPreferredUILanguages, 0,
+                                    languages);
 }
 
 bool GetThreadPreferredUILanguageList(std::vector<base::string16>* languages) {
   DCHECK(languages);
   return GetPreferredUILanguageList(
-      THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK,
-      languages);
+      ::GetThreadPreferredUILanguages,
+      MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK, languages);
 }
 
 }  // namespace i18n
diff --git a/base/win/i18n_unittest.cc b/base/win/i18n_unittest.cc
index 0188838..b7973c3e 100644
--- a/base/win/i18n_unittest.cc
+++ b/base/win/i18n_unittest.cc
@@ -4,12 +4,13 @@
 
 // This file contains unit tests for Windows internationalization funcs.
 
-#include "testing/gtest/include/gtest/gtest.h"
-
 #include <stddef.h>
+#include <string.h>
 
+#include "base/strings/string_util.h"
 #include "base/win/i18n.h"
 #include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
 namespace win {
@@ -22,6 +23,8 @@
   EXPECT_FALSE(languages.empty());
   for (const auto& language : languages) {
     EXPECT_FALSE(language.empty());
+    // Ensure there's no extra trailing 0 characters.
+    EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language)));
   }
 }
 
@@ -32,6 +35,7 @@
   EXPECT_FALSE(languages.empty());
   for (const auto& language : languages) {
     EXPECT_FALSE(language.empty());
+    EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language)));
   }
 }
 
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index ce52ad64..1d3721944 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -27,10 +27,6 @@
 #error Windows 10.0.18362.0 SDK or higher required.
 #endif
 
-namespace {
-typedef BOOL(WINAPI* GetProductInfoPtr)(DWORD, DWORD, DWORD, DWORD, PDWORD);
-}  // namespace
-
 namespace base {
 namespace win {
 
@@ -79,14 +75,8 @@
     ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info));
 
     DWORD os_type = 0;
-    if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) {
-      // Only present on Vista+.
-      GetProductInfoPtr get_product_info =
-          reinterpret_cast<GetProductInfoPtr>(::GetProcAddress(
-              ::GetModuleHandle(L"kernel32.dll"), "GetProductInfo"));
-      get_product_info(version_info.dwMajorVersion, version_info.dwMinorVersion,
-                       0, 0, &os_type);
-    }
+    ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion,
+                     0, 0, &os_type);
 
     return new OSInfo(version_info, GetSystemInfoStorage(), os_type);
   }();
@@ -252,13 +242,8 @@
 
 // static
 OSInfo::WOW64Status OSInfo::GetWOW64StatusForProcess(HANDLE process_handle) {
-  typedef BOOL(WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL);
-  IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>(
-      GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
-  if (!is_wow64_process)
-    return WOW64_DISABLED;
   BOOL is_wow64 = FALSE;
-  if (!(*is_wow64_process)(process_handle, &is_wow64))
+  if (!::IsWow64Process(process_handle, &is_wow64))
     return WOW64_UNKNOWN;
   return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
 }
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 7ab955a..f4bc5e4 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -2,8 +2,17 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//build/config/chrome_build.gni")
+
 source_set("buildflag_header_h") {
   sources = [
     "buildflag.h",
   ]
 }
+
+buildflag_header("branding_buildflags") {
+  header = "branding_buildflags.h"
+
+  flags = [ "CHROMIUM_BRANDING=!is_chrome_branded" ]
+}
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index b5eefd0..624f734 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -297,12 +297,6 @@
           './' + os.path.relpath(self._on_device_script, self._path_to_outdir)
       ]
     else:
-      self._test_cmd += [
-          # Since we're not in a chroot, the tast bin won't automatically handle
-          # ssh auth. So point it to the ssh keys in chromite.
-          '--private-key',
-          os.path.join(CHROMITE_PATH, 'ssh_keys', 'testing_rsa'),
-      ]
       # Capture tast's results in the logs dir as well.
       if self._logs_dir:
         self._test_cmd += [
@@ -643,6 +637,12 @@
   if args.verbose:
     cros_run_test_cmd.append('--debug')
 
+  if args.logs_dir:
+    cros_run_test_cmd += [
+        '--results-src', '/var/log/',
+        '--results-dest-dir', args.logs_dir,
+    ]
+
   test_env = setup_env()
   if args.deploy_chrome:
     cros_run_test_cmd += [
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 514ab7ba..d44c925 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -95,10 +95,7 @@
     defines += [ "NO_TCMALLOC" ]
   }
   if (is_asan || is_hwasan || is_lsan || is_tsan || is_msan) {
-    defines += [
-      "MEMORY_TOOL_REPLACES_ALLOCATOR",
-      "MEMORY_SANITIZER_INITIAL_SIZE",
-    ]
+    defines += [ "MEMORY_TOOL_REPLACES_ALLOCATOR" ]
   }
   if (is_asan) {
     defines += [ "ADDRESS_SANITIZER" ]
@@ -130,9 +127,12 @@
   if (is_official_build) {
     defines += [ "OFFICIAL_BUILD" ]
   }
+
+  # TODO(thakis): Remove these in favor of build/branding_buildflags.h.
   if (is_chrome_branded) {
     defines += [ "GOOGLE_CHROME_BUILD" ]
   } else {
+    # TODO(thakis): All uses for this are gone, remove this in a follow-up.
     defines += [ "CHROMIUM_BUILD" ]
   }
 
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index db7ab02..23695f672 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -574,8 +574,10 @@
       # On Android, write shared library output file to metadata. We will use
       # this information to, for instance, collect all shared libraries that
       # should be packaged into an APK.
-      if (!defined(invoker.metadata) && is_android &&
-          _target_type == "shared_library") {
+      if (!defined(invoker.metadata) && is_android && (_target_type ==
+                                                       "shared_library" ||
+                                                       _target_type ==
+                                                       "loadable_module")) {
         _output_name = _target_name
         if (defined(invoker.output_name)) {
           _output_name = invoker.output_name
@@ -590,7 +592,7 @@
         _output_name = string_replace(_output_name, _magic_prefix, "", 1)
 
         _shlib_extension = ".so"
-        if (is_component_build) {
+        if (is_component_build && _target_type != "loadable_module") {
           _shlib_extension = ".cr.so"
         }
 
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 260ed0b7..648455c 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -30,7 +30,7 @@
   assert(
       is_official_build,
       "Linker map files should only be generated when is_official_build = true")
-  assert(current_os == "android" || target_os == "linux",
+  assert(target_os == "android" || target_os == "linux",
          "Linker map files should only be generated for Android and Linux")
 }
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 0612c46..23c71d9 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -233,7 +233,11 @@
   }
 
   float device_scale_factor = layer_tree_impl()->device_scale_factor();
-  float max_contents_scale = MaximumTilingContentsScale();
+  // If we don't have tilings, we're likely going to append a checkerboard quad
+  // the size of the layer. In that case, use scale 1 for more stable
+  // to-screen-space mapping.
+  float max_contents_scale =
+      tilings_->num_tilings() ? MaximumTilingContentsScale() : 1.f;
   PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale,
                                 contents_opaque());
   Occlusion scaled_occlusion;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 4276fd2..1949b352 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -5588,5 +5588,35 @@
   EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input3));
 }
 
+TEST_F(PictureLayerImplTest, NoTilingsUsesScaleOne) {
+  std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create();
+
+  gfx::Size layer_bounds(1000, 10000);
+  scoped_refptr<FakeRasterSource> active_raster_source =
+      FakeRasterSource::CreateEmpty(layer_bounds);
+  SetupPendingTree(active_raster_source);
+  ActivateTree();
+
+  active_layer()->SetContentsOpaque(true);
+  active_layer()->draw_properties().visible_layer_rect =
+      gfx::Rect(0, 0, 1000, 1000);
+  active_layer()->UpdateTiles();
+
+  ASSERT_FALSE(active_layer()->HighResTiling());
+
+  AppendQuadsData data;
+  active_layer()->WillDraw(DRAW_MODE_HARDWARE, nullptr);
+  active_layer()->AppendQuads(render_pass.get(), &data);
+  active_layer()->DidDraw(nullptr);
+
+  // One checkerboard quad.
+  EXPECT_EQ(1u, render_pass->quad_list.size());
+
+  auto* shared_quad_state = render_pass->quad_list.begin()->shared_quad_state;
+  // We should use scale 1 here, so the layer rect should be full layer bounds
+  // and the transform should be identity.
+  EXPECT_RECT_EQ(gfx::Rect(1000, 10000), shared_quad_state->quad_layer_rect);
+  EXPECT_TRUE(shared_quad_state->quad_to_target_transform.IsIdentity());
+}
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 983c6be..f91deac 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -833,7 +833,6 @@
   // when we actually encounter a masking child.
   node->has_masking_child = false;
   if (node->blend_mode == SkBlendMode::kDstIn) {
-    DCHECK(parent_node->HasRenderSurface());
     parent_node->has_masking_child = true;
   }
 }
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index e5b48fd..e107dd9 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1862,14 +1862,17 @@
       "jnigraphics",
     ]
 
-    deps = [
+    public_deps = [
       "//chrome/browser",
+      "//chrome/utility",
+    ]
+
+    deps = [
       "//chrome/browser/ui",
       "//chrome/child",
       "//chrome/common",
       "//chrome/gpu",
       "//chrome/renderer",
-      "//chrome/utility",
       "//components/gwp_asan/buildflags",
       "//components/minidump_uploader",
       "//components/safe_browsing/android:safe_browsing_mobile",
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 7760b121..35dd742 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -903,6 +903,7 @@
     "//chrome/test/data/push_messaging/",
     "//chrome/test/data/translate/",
     "//chrome/test/media_router/resources/",
+    "//components/test/data/autofill/",
     "//components/test/data/payments/",
     "//content/test/data/browsing_data/",
     "//content/test/data/android/authenticator.html",
@@ -1549,6 +1550,7 @@
     ":test_support_jni_headers",
     "//chrome/browser",
     "//components/offline_pages/core/background:test_support",
+    "//content/test:test_support",
   ]
 }
 
@@ -2624,6 +2626,7 @@
     "java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java",
+    "java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
     "java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 9174c89..675990f4 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1117,7 +1117,6 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java",
-  "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextClassic.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayout.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionHost.java",
@@ -1224,30 +1223,22 @@
   "java/src/org/chromium/chrome/browser/preferences/AboutChromePreferenceOSVersion.java",
   "java/src/org/chromium/chrome/browser/preferences/AboutChromePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java",
-  "java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreferenceCompat.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ExpandablePreferenceGroup.java",
   "java/src/org/chromium/chrome/browser/preferences/HomepageEditor.java",
   "java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java",
-  "java/src/org/chromium/chrome/browser/preferences/LearnMorePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/LearnMorePreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/LocationSettings.java",
   "java/src/org/chromium/chrome/browser/preferences/MainPreferences.java",
-  "java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java",
   "java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java",
   "java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java",
@@ -1270,7 +1261,6 @@
   "java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java",
   "java/src/org/chromium/chrome/browser/preferences/sync/SyncedAccountPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextAndButtonPreference.java",
-  "java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/TextScalePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppPreference.java",
@@ -1699,6 +1689,7 @@
   "java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkSplashNetworkErrorObserver.java",
+  "java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
   "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateTask.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index c5ca794..f51954cba 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -52,6 +52,7 @@
   "javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java",
+  "javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java",
   "javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java",
@@ -445,8 +446,9 @@
   "javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java",
   "javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
+  "javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
-  "javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java",
+  "javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java",
   "javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
index 1537e25..8469d73 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
@@ -198,7 +198,11 @@
         View.OnClickListener clickListener = unusedView
                 -> setChecked(
                         view, mAllowMultipleChoices ? !item.mCompoundButton.isChecked() : true);
-        radioButton.setOnClickListener(clickListener);
+        radioButton.setOnCheckedChangeListener((unusedView, isChecked) -> {
+            if (item.mOnSelectedListener != null) {
+                item.mOnSelectedListener.onResult(isChecked);
+            }
+        });
         view.setOnClickListener(clickListener);
         mItems.add(item);
     }
@@ -262,21 +266,24 @@
      */
     public void setChecked(View content, boolean checked) {
         for (Item item : mItems) {
-            boolean notifyListener = false;
             if (item.mContent == content) {
                 item.mCompoundButton.setChecked(checked);
-                notifyListener = true;
             } else if (checked && !mAllowMultipleChoices) {
                 item.mCompoundButton.setChecked(false);
-                notifyListener = true;
-            }
-
-            if (notifyListener && item.mOnSelectedListener != null) {
-                item.mOnSelectedListener.onResult(item.mCompoundButton.isChecked());
             }
         }
     }
 
+    /** Returns whether {@code content} is checked or not. */
+    public boolean isChecked(View content) {
+        for (Item mItem : mItems) {
+            if (mItem.mContent == content) {
+                return mItem.mCompoundButton.isChecked();
+            }
+        }
+        return false;
+    }
+
     /**
      * Allows to change the label of the 'add' button.
      */
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
index ba8f5a4b..aaae7a7 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestBinder.java
@@ -10,6 +10,7 @@
 
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
+import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestTermsSection.Delegate;
 import org.chromium.chrome.browser.payments.AddressEditor;
 import org.chromium.chrome.browser.payments.AutofillPaymentApp;
 import org.chromium.chrome.browser.payments.AutofillPaymentInstrument;
@@ -96,6 +97,7 @@
         private final AssistantPaymentRequestPaymentMethodSection mPaymentMethodSection;
         private final AssistantPaymentRequestShippingAddressSection mShippingAddressSection;
         private final AssistantPaymentRequestTermsSection mTermsSection;
+        private final AssistantPaymentRequestTermsSection mTermsAsCheckboxSection;
         private final Object mDividerTag;
         private final Activity mActivity;
         private PersonalDataManager.PersonalDataManagerObserver mPersonalDataManagerObserver;
@@ -105,7 +107,8 @@
                 AssistantPaymentRequestContactDetailsSection contactDetailsSection,
                 AssistantPaymentRequestPaymentMethodSection paymentMethodSection,
                 AssistantPaymentRequestShippingAddressSection shippingAddressSection,
-                AssistantPaymentRequestTermsSection termsSection, Object dividerTag,
+                AssistantPaymentRequestTermsSection termsSection,
+                AssistantPaymentRequestTermsSection termsAsCheckboxSection, Object dividerTag,
                 Activity activity) {
             mRootView = rootView;
             mPaymentRequestExpanderAccordion = accordion;
@@ -114,6 +117,7 @@
             mPaymentMethodSection = paymentMethodSection;
             mShippingAddressSection = shippingAddressSection;
             mTermsSection = termsSection;
+            mTermsAsCheckboxSection = termsAsCheckboxSection;
             mDividerTag = dividerTag;
             mActivity = activity;
         }
@@ -156,8 +160,20 @@
         if (propertyKey == AssistantPaymentRequestModel.DELEGATE) {
             AssistantPaymentRequestDelegate delegate =
                     model.get(AssistantPaymentRequestModel.DELEGATE);
-            view.mTermsSection.setListener(
-                    delegate != null ? delegate::onTermsAndConditionsChanged : null);
+
+            Delegate termsDelegate = delegate == null ? null : new Delegate() {
+                @Override
+                public void onStateChanged(@AssistantTermsAndConditionsState int state) {
+                    delegate.onTermsAndConditionsChanged(state);
+                }
+
+                @Override
+                public void onLinkClicked(int link) {
+                    delegate.onTermsAndConditionsLinkClicked(link);
+                }
+            };
+            view.mTermsSection.setDelegate(termsDelegate);
+            view.mTermsAsCheckboxSection.setDelegate(termsDelegate);
             view.mContactDetailsSection.setListener(
                     delegate != null ? delegate::onContactInfoChanged : null);
             view.mPaymentMethodSection.setListener(
@@ -232,9 +248,20 @@
             view.mPaymentMethodSection.setVisible(
                     (model.get(AssistantPaymentRequestModel.REQUEST_PAYMENT)));
             return true;
-        } else if (propertyKey == AssistantPaymentRequestModel.REQUEST_TERMS_AND_CONDITIONS) {
-            view.mTermsSection.setTermsListVisible(
-                    model.get(AssistantPaymentRequestModel.REQUEST_TERMS_AND_CONDITIONS));
+        } else if (propertyKey == AssistantPaymentRequestModel.ACCEPT_TERMS_AND_CONDITIONS_TEXT) {
+            view.mTermsSection.setAcceptTermsAndConditionsText(
+                    model.get(AssistantPaymentRequestModel.ACCEPT_TERMS_AND_CONDITIONS_TEXT));
+            view.mTermsAsCheckboxSection.setAcceptTermsAndConditionsText(
+                    model.get(AssistantPaymentRequestModel.ACCEPT_TERMS_AND_CONDITIONS_TEXT));
+            return true;
+        } else if (propertyKey == AssistantPaymentRequestModel.SHOW_TERMS_AS_CHECKBOX) {
+            if (model.get(AssistantPaymentRequestModel.SHOW_TERMS_AS_CHECKBOX)) {
+                view.mTermsSection.getView().setVisibility(View.GONE);
+                view.mTermsAsCheckboxSection.getView().setVisibility(View.VISIBLE);
+            } else {
+                view.mTermsSection.getView().setVisibility(View.VISIBLE);
+                view.mTermsAsCheckboxSection.getView().setVisibility(View.GONE);
+            }
             return true;
         }
         return false;
@@ -259,6 +286,9 @@
                 if (webContents != null) {
                     view.mTermsSection.setOrigin(UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
                             webContents.getLastCommittedUrl()));
+                    view.mTermsAsCheckboxSection.setOrigin(
+                            UrlFormatter.formatUrlForSecurityDisplayOmitScheme(
+                                    webContents.getLastCommittedUrl()));
                 }
                 view.startListenToPersonalDataManager(() -> {
                     AssistantPaymentRequestBinder.this.updateAvailableProfiles(model, view);
@@ -291,7 +321,9 @@
                     model.get(AssistantPaymentRequestModel.CONTACT_DETAILS), true);
             return true;
         } else if (propertyKey == AssistantPaymentRequestModel.TERMS_STATUS) {
-            view.mTermsSection.setTermsStatus(model.get(AssistantPaymentRequestModel.TERMS_STATUS));
+            int termsStatus = model.get(AssistantPaymentRequestModel.TERMS_STATUS);
+            view.mTermsSection.setTermsStatus(termsStatus);
+            view.mTermsAsCheckboxSection.setTermsStatus(termsStatus);
             return true;
         }
         return false;
@@ -339,6 +371,7 @@
             view.mShippingAddressSection.setPaddings(0, view.mSectionToSectionPadding);
         }
         view.mTermsSection.setPaddings(view.mSectionToSectionPadding, 0);
+        view.mTermsAsCheckboxSection.setPaddings(view.mSectionToSectionPadding, 0);
 
         // Hide dividers for currently invisible sections and after the expanded section, if any.
         boolean prevSectionIsExpandedOrInvisible = false;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
index 7884733..726274b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java
@@ -64,8 +64,11 @@
                 new AssistantPaymentRequestShippingAddressSection(
                         mActivity, paymentRequestExpanderAccordion);
         createSeparator(paymentRequestExpanderAccordion);
-        AssistantPaymentRequestTermsSection termsSection =
-                new AssistantPaymentRequestTermsSection(mActivity, paymentRequestExpanderAccordion);
+        AssistantPaymentRequestTermsSection termsSection = new AssistantPaymentRequestTermsSection(
+                mActivity, paymentRequestExpanderAccordion, /* showAsSingleCheckbox= */ false);
+        AssistantPaymentRequestTermsSection termsAsCheckboxSection =
+                new AssistantPaymentRequestTermsSection(mActivity, paymentRequestExpanderAccordion,
+                        /* showAsSingleCheckbox= */ true);
 
         paymentRequestExpanderAccordion.setTag(
                 AssistantTagsForTesting.PAYMENT_REQUEST_ACCORDION_TAG);
@@ -79,7 +82,8 @@
         // Bind view and mediator through the model.
         mViewHolder = new AssistantPaymentRequestBinder.ViewHolder(mPaymentRequestUI,
                 paymentRequestExpanderAccordion, sectionToSectionPadding, contactDetailsSection,
-                paymentMethodSection, shippingAddressSection, termsSection, DIVIDER_TAG, activity);
+                paymentMethodSection, shippingAddressSection, termsSection, termsAsCheckboxSection,
+                DIVIDER_TAG, activity);
         AssistantPaymentRequestBinder binder = new AssistantPaymentRequestBinder();
         PropertyModelChangeProcessor.create(model, mViewHolder, binder);
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
index 68a0eeb..f3c67672 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java
@@ -26,6 +26,9 @@
     /** The currently selected payment method has changed. */
     void onPaymentMethodChanged(@Nullable AutofillPaymentInstrument paymentInstrument);
 
-    /** the currently selected terms & conditions state has changed. */
+    /** The currently selected terms & conditions state has changed. */
     void onTermsAndConditionsChanged(@AssistantTermsAndConditionsState int state);
+
+    /** Called when a link on the terms and conditions message is clicked. */
+    void onTermsAndConditionsLinkClicked(int link);
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
index e579265..0c1716e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java
@@ -57,7 +57,9 @@
             new WritableBooleanPropertyKey();
     public static final WritableBooleanPropertyKey REQUEST_PAYMENT =
             new WritableBooleanPropertyKey();
-    public static final WritableBooleanPropertyKey REQUEST_TERMS_AND_CONDITIONS =
+    public static final WritableObjectPropertyKey<String> ACCEPT_TERMS_AND_CONDITIONS_TEXT =
+            new WritableObjectPropertyKey<>();
+    public static final WritableBooleanPropertyKey SHOW_TERMS_AS_CHECKBOX =
             new WritableBooleanPropertyKey();
 
     public static final WritableObjectPropertyKey<List<PersonalDataManager.AutofillProfile>>
@@ -80,9 +82,9 @@
     public AssistantPaymentRequestModel() {
         super(DELEGATE, WEB_CONTENTS, VISIBLE, SHIPPING_ADDRESS, PAYMENT_METHOD, CONTACT_DETAILS,
                 TERMS_STATUS, REQUEST_NAME, REQUEST_EMAIL, REQUEST_PHONE, REQUEST_SHIPPING_ADDRESS,
-                REQUEST_PAYMENT, REQUEST_TERMS_AND_CONDITIONS, AVAILABLE_PROFILES,
-                AVAILABLE_AUTOFILL_PAYMENT_METHODS, SUPPORTED_BASIC_CARD_NETWORKS,
-                SUPPORTED_PAYMENT_METHODS, EXPANDED_SECTION);
+                REQUEST_PAYMENT, ACCEPT_TERMS_AND_CONDITIONS_TEXT, SHOW_TERMS_AS_CHECKBOX,
+                AVAILABLE_PROFILES, AVAILABLE_AUTOFILL_PAYMENT_METHODS,
+                SUPPORTED_BASIC_CARD_NETWORKS, SUPPORTED_PAYMENT_METHODS, EXPANDED_SECTION);
 
         /**
          * Set initial state for basic type properties (others are implicitly null).
@@ -123,8 +125,13 @@
     }
 
     @CalledByNative
-    private void setRequestTermsAndConditions(boolean requestTermsAndConditions) {
-        set(REQUEST_TERMS_AND_CONDITIONS, requestTermsAndConditions);
+    private void setAcceptTermsAndConditionsText(String text) {
+        set(ACCEPT_TERMS_AND_CONDITIONS_TEXT, text);
+    }
+
+    @CalledByNative
+    private void setShowTermsAsCheckbox(boolean showTermsAsCheckbox) {
+        set(SHOW_TERMS_AS_CHECKBOX, showTermsAsCheckbox);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
index 456d5fe..a583578 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestNativeDelegate.java
@@ -68,6 +68,13 @@
         }
     }
 
+    @Override
+    public void onTermsAndConditionsLinkClicked(int link) {
+        if (mNativeAssistantPaymentRequestDelegate != 0) {
+            nativeOnTermsAndConditionsLinkClicked(mNativeAssistantPaymentRequestDelegate, link);
+        }
+    }
+
     @CalledByNative
     private void clearNativePtr() {
         mNativeAssistantPaymentRequestDelegate = 0;
@@ -81,4 +88,6 @@
             @Nullable PersonalDataManager.CreditCard card);
     private native void nativeOnTermsAndConditionsChanged(
             long nativeAssistantPaymentRequestDelegate, int state);
+    private native void nativeOnTermsAndConditionsLinkClicked(
+            long nativeAssistantPaymentRequestDelegate, int link);
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
index 565f9ac..37e24d4 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestTermsSection.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.autofill_assistant.payment;
 
 import android.content.Context;
+import android.support.annotation.Nullable;
+import android.text.method.LinkMovementMethod;
 import android.text.style.StyleSpan;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -14,22 +16,45 @@
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
+import org.chromium.ui.text.SpanApplier.SpanInfo;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * The third party terms and conditions section of the Autofill Assistant payment request.
  */
 public class AssistantPaymentRequestTermsSection {
+    private static final Pattern LINK_PATTERN = Pattern.compile("<link(\\d+)>");
+
+    interface Delegate {
+        void onStateChanged(@AssistantTermsAndConditionsState int state);
+
+        void onLinkClicked(int link);
+    }
+
     private final View mView;
     private final AssistantChoiceList mTermsList;
     private final TextView mTermsAgree;
+    @Nullable
     private final TextView mTermsRequiresReview;
     private final TextView mThirdPartyPrivacyNotice;
-    private Callback<Integer> mListener;
+    private Delegate mDelegate;
 
-    AssistantPaymentRequestTermsSection(Context context, ViewGroup parent) {
+    private final SpanInfo mBoldSpanInfo =
+            new SpanInfo("<b>", "</b>", new StyleSpan(android.graphics.Typeface.BOLD));
+    private String mOrigin = "";
+    private String mAcceptTermsText = "";
+
+    AssistantPaymentRequestTermsSection(
+            Context context, ViewGroup parent, boolean showAsSingleCheckbox) {
         mView = LayoutInflater.from(context).inflate(
                 R.layout.autofill_assistant_payment_request_terms_and_conditions, parent, false);
         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@@ -37,43 +62,88 @@
         parent.addView(mView, lp);
         mTermsList = mView.findViewById(R.id.third_party_terms_list);
 
+        // This will display the terms as a checkbox instead of radio buttons.
+        mTermsList.setAllowMultipleChoices(showAsSingleCheckbox);
+
         mTermsAgree = new TextView(context);
         ApiCompatibilityUtils.setTextAppearance(mTermsAgree, R.style.TextAppearance_BlackCaption);
         mTermsAgree.setGravity(Gravity.CENTER_VERTICAL);
         mTermsList.addItem(mTermsAgree, /*hasEditButton=*/false, selected -> {
-            if (selected && mListener != null) {
-                mListener.onResult(AssistantTermsAndConditionsState.ACCEPTED);
+            if (selected) {
+                if (mDelegate != null) {
+                    mDelegate.onStateChanged(AssistantTermsAndConditionsState.ACCEPTED);
+                }
+            } else if (showAsSingleCheckbox && mDelegate != null) {
+                mDelegate.onStateChanged(AssistantTermsAndConditionsState.NOT_SELECTED);
             }
         }, /* itemEditedListener=*/null);
 
-        mTermsRequiresReview = new TextView(context);
-        ApiCompatibilityUtils.setTextAppearance(
-                mTermsRequiresReview, R.style.TextAppearance_BlackCaption);
-        mTermsRequiresReview.setGravity(Gravity.CENTER_VERTICAL);
-        mTermsList.addItem(mTermsRequiresReview, /*hasEditButton=*/false, selected -> {
-            if (selected && mListener != null) {
-                mListener.onResult(AssistantTermsAndConditionsState.REQUIRES_REVIEW);
-            }
-        }, /* itemEditedListener=*/null);
+        if (showAsSingleCheckbox) {
+            mTermsRequiresReview = null;
+        } else {
+            mTermsRequiresReview = new TextView(context);
+            ApiCompatibilityUtils.setTextAppearance(
+                    mTermsRequiresReview, R.style.TextAppearance_BlackCaption);
+            mTermsRequiresReview.setGravity(Gravity.CENTER_VERTICAL);
+            mTermsList.addItem(mTermsRequiresReview, /*hasEditButton=*/false, selected -> {
+                if (selected && mDelegate != null) {
+                    mDelegate.onStateChanged(AssistantTermsAndConditionsState.REQUIRES_REVIEW);
+                }
+            }, /* itemEditedListener=*/null);
+        }
 
         mThirdPartyPrivacyNotice =
                 mView.findViewById(R.id.payment_request_3rd_party_privacy_notice);
     }
 
     public void setOrigin(String origin) {
-        StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
-        Context context = mView.getContext();
-        mTermsAgree.setText(SpanApplier.applySpans(
-                context.getString(R.string.autofill_assistant_3rd_party_terms_accept, origin),
-                new SpanApplier.SpanInfo("<b>", "</b>", boldSpan)));
+        mOrigin = origin;
 
-        mTermsRequiresReview.setText(SpanApplier.applySpans(
-                context.getString(R.string.autofill_assistant_3rd_party_terms_review, origin),
-                new SpanApplier.SpanInfo("<b>", "</b>", boldSpan)));
+        setAcceptTermsText();
+
+        Context context = mView.getContext();
+        if (mTermsRequiresReview != null) {
+            mTermsRequiresReview.setText(SpanApplier.applySpans(
+                    context.getString(R.string.autofill_assistant_3rd_party_terms_review, origin),
+                    mBoldSpanInfo));
+        }
 
         mThirdPartyPrivacyNotice.setText(SpanApplier.applySpans(
                 context.getString(R.string.autofill_assistant_3rd_party_privacy_notice, origin),
-                new SpanApplier.SpanInfo("<b>", "</b>", boldSpan)));
+                mBoldSpanInfo));
+    }
+
+    private void setAcceptTermsText() {
+        if (mAcceptTermsText.isEmpty()) return;
+
+        List<SpanInfo> spans = new ArrayList<>();
+        if (mAcceptTermsText.contains("<b>")) {
+            spans.add(mBoldSpanInfo);
+        }
+
+        // We first collect the link IDs into a set to allow multiple links with same ID.
+        Set<Integer> linkIds = new HashSet<>();
+        Matcher matcher = LINK_PATTERN.matcher(mAcceptTermsText);
+        while (matcher.find()) {
+            linkIds.add(Integer.parseInt(matcher.group(1)));
+        }
+
+        for (Integer linkId : linkIds) {
+            spans.add(new SpanInfo("<link" + linkId + ">", "</link" + linkId + ">",
+                    new NoUnderlineClickableSpan(mView.getContext().getResources(),
+                            (unusedView) -> onTermsAndConditionsLinkClicked(linkId))));
+        }
+
+        mTermsAgree.setText(SpanApplier.applySpans(String.format(mAcceptTermsText, mOrigin),
+                spans.toArray(new SpanInfo[spans.size()])));
+        mTermsAgree.setMovementMethod(linkIds.isEmpty() ? null : LinkMovementMethod.getInstance());
+    }
+
+    private void onTermsAndConditionsLinkClicked(int link) {
+        // Ignore first click if the option is not selected yet.
+        if (!mTermsList.isChecked(mTermsAgree) || mDelegate == null) return;
+
+        mDelegate.onLinkClicked(link);
     }
 
     public void setTermsStatus(@AssistantTermsAndConditionsState int status) {
@@ -85,7 +155,9 @@
                 mTermsList.setCheckedItem(mTermsAgree);
                 break;
             case AssistantTermsAndConditionsState.REQUIRES_REVIEW:
-                mTermsList.setCheckedItem(mTermsRequiresReview);
+                if (mTermsRequiresReview != null) {
+                    mTermsList.setCheckedItem(mTermsRequiresReview);
+                }
                 break;
         }
     }
@@ -95,12 +167,18 @@
                 mView.getPaddingLeft(), topPadding, mView.getPaddingRight(), bottomPadding);
     }
 
-    public void setListener(Callback<Integer> listener) {
-        mListener = listener;
+    public void setDelegate(Delegate delegate) {
+        mDelegate = delegate;
     }
 
-    public void setTermsListVisible(boolean visible) {
-        mTermsList.setVisibility(visible ? View.VISIBLE : View.GONE);
+    void setAcceptTermsAndConditionsText(String text) {
+        if (text.isEmpty()) {
+            mTermsList.setVisibility(View.GONE);
+        } else {
+            mTermsList.setVisibility(View.VISIBLE);
+            mAcceptTermsText = text;
+            setAcceptTermsText();
+        }
     }
 
     View getView() {
diff --git a/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd b/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
index 45eecf7..59f9fab 100644
--- a/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
+++ b/chrome/android/features/autofill_assistant/java/strings/android_chrome_autofill_assistant_strings.grd
@@ -124,9 +124,6 @@
       <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_URL" desc="URL for Google Autofill Assistant Terms of Service" translateable="false">
         http://support.google.com/assistant?p=fast_checkout
       </message>
-      <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_ACCEPT" desc="Message that indicates that the user agrees to the terms and conditions of a 3rd party's domain, e.g., 'odeon.co.uk'.">
-        I agree to the terms &amp; conditions, privacy policy, and right of withdrawal of <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>
-      </message>
       <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_REVIEW" desc="Message that indicates that the user wants to review the terms and conditions of a 3rd party's domain, e.g., 'odeon.co.uk'.">
         Read and agree to the terms &amp; conditions on <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph> later
       </message>
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
index 12c5d3a..b3daec3 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java
@@ -102,6 +102,11 @@
         public void onTermsAndConditionsChanged(@AssistantTermsAndConditionsState int state) {
             mTermsStatus = state;
         }
+
+        @Override
+        public void onTermsAndConditionsLinkClicked(int link) {
+            // TODO(crbug.com/860868): Add tests that use this method.
+        }
     }
 
     public AutofillAssistantPaymentRequestTestHelper()
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 421df94c..2bf62b9 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -27,6 +27,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridView.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridViewHolder.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabCarouselViewHolder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml b/chrome/android/features/tab_ui/java/res/drawable/hovered_tab_grid_card_background.xml
similarity index 67%
rename from chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml
rename to chrome/android/features/tab_ui/java/res/drawable/hovered_tab_grid_card_background.xml
index 0bd6ef9..12c08b26 100644
--- a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml
+++ b/chrome/android/features/tab_ui/java/res/drawable/hovered_tab_grid_card_background.xml
@@ -4,6 +4,6 @@
      found in the LICENSE file. -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@color/modern_grey_200"/>
-    <corners android:radius="@dimen/default_rounded_corner_radius" />
+    <solid android:color="@color/tab_list_mini_card_default_background_color"/>
+    <corners android:radius="@dimen/tab_list_card_radius"/>
 </shape>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml b/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
index 6c49104..75f65f2 100644
--- a/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
+++ b/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
@@ -7,5 +7,5 @@
     <stroke
         android:width="2dp"
         android:color="@color/light_active_color" />
-    <corners android:radius="@dimen/default_rounded_corner_radius" />
+    <corners android:radius="@dimen/tab_list_card_radius" />
 </shape>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
index e4de6a38..0a43dbd5 100644
--- a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
+++ b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
@@ -4,6 +4,6 @@
      found in the LICENSE file. -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@android:color/white"/>
-    <corners android:radius="@dimen/default_rounded_corner_radius" />
+    <solid android:color="@color/tab_grid_dialog_background_color"/>
+    <corners android:radius="@dimen/tab_list_card_radius" />
 </shape>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
index 445c96ca..58aacb0f 100644
--- a/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
+++ b/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
@@ -12,7 +12,7 @@
         android:bottom="8dp">
         <shape
             android:shape="oval">
-            <solid android:color="@color/modern_grey_100" />
+            <solid android:color="@color/favicon_background_color" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
index 5c4d46a..da41db1 100644
--- a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
@@ -8,7 +8,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/modern_primary_color">
+    android:background="@drawable/tab_grid_card_background">
     <LinearLayout
         android:id="@+id/main_content"
         android:layout_width="match_parent"
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 ca2b033..d2f80507 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
@@ -6,12 +6,19 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
     <View
+        android:id="@+id/background_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:id="@+id/background_view"
-        android:background="@drawable/popup_bg"
-        android:layout_margin="3dp"
-        android:visibility="visible"/>
+        android:background="@drawable/hovered_tab_grid_card_background"
+        android:layout_margin="8dp"
+        android:visibility="gone"/>
+    <View
+        android:id="@+id/selected_view_below_lollipop"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/selected_tab_background"
+        android:layout_margin="6dp"
+        android:visibility="gone"/>
     <FrameLayout
         android:id="@+id/content_view"
         android:layout_width="match_parent"
@@ -20,8 +27,8 @@
             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">
+            android:background="@drawable/popup_bg_tinted"
+            android:layout_margin="2dp">
             <ImageView
                 android:id="@+id/tab_favicon"
                 android:layout_width="@dimen/tab_grid_favicon_size"
@@ -52,12 +59,13 @@
                 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"/>
+                app:cornerRadiusBottomStart="@dimen/tab_list_card_radius"
+                app:cornerRadiusBottomEnd="@dimen/tab_list_card_radius"/>
             <View
+                android:id="@+id/divider_view"
                 style="@style/HorizontalDivider"
-                android:layout_below="@id/tab_title"/>
+                android:layout_below="@id/tab_title"
+                android:background="@color/divider_bg_color"/>
             <org.chromium.ui.widget.ButtonCompat
                 android:id="@+id/create_group_button"
                 android:layout_width="wrap_content"
@@ -76,6 +84,6 @@
             android:layout_height="48dp"
             android:scaleType="center"
             android:layout_gravity="end"
-            android:tint="@color/modern_grey_800"/>
+            android:tint="@color/default_icon_color"/>
     </FrameLayout>
 </merge>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml
index edf63d9..136d503 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml
@@ -19,7 +19,7 @@
             android:id="@+id/dialog_ungroup_bar"
             android:text="@string/tab_grid_dialog_remove_from_group"
             android:textAppearance="@style/TextAppearance.WhiteHeadline"
-            android:background="@color/modern_blue_600"
+            android:background="@color/light_active_color"
             android:gravity="center"
             android:layout_alignParentBottom="true"
             android:layout_centerHorizontal="true"
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml
new file mode 100644
index 0000000..c93dcbc
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?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. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <color name="tab_list_mini_card_default_background_color">@color/modern_secondary_color</color>
+    <color name="tab_grid_dialog_background_color">@color/default_bg_color_elev_1</color>
+    <color name="favicon_background_color">@color/modern_secondary_color</color>
+</resources>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index 9ecafbb..cc8d0ace 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -5,9 +5,8 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
     <dimen name="tab_grid_favicon_size">32dp</dimen>
     <dimen name="tab_list_selected_inset">7dp</dimen>
-    <dimen name="tab_list_selected_inset_kitkat">2dp</dimen>
     <dimen name="tab_list_card_padding">8dp</dimen>
-    <dimen name="tab_list_card_background_margin">3dp</dimen>
+    <dimen name="tab_list_card_radius">4dp</dimen>
     <dimen name="tab_list_mini_card_radius">4dp</dimen>
     <dimen name="tab_list_mini_card_frame_size">1dp</dimen>
     <dimen name="tab_grid_close_button_size">18dp</dimen>
@@ -24,4 +23,6 @@
     <dimen name="tab_group_toolbar_topic_margin">8dp</dimen>
     <dimen name="swipe_to_dismiss_threshold">72dp</dimen>
     <dimen name="selection_tab_grid_toggle_button_inset">14dp</dimen>
+    <dimen name="tab_carousel_height">192dp</dimen>
+    <dimen name="tab_carousel_card_width">168dp</dimen>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ClosableTabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ClosableTabGridViewHolder.java
index 5b7abd7..43cd17f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ClosableTabGridViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ClosableTabGridViewHolder.java
@@ -8,16 +8,11 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.support.annotation.IntDef;
-import android.support.v7.content.res.AppCompatResources;
 import android.view.View;
-import android.view.ViewGroup;
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.tab_ui.R;
@@ -72,20 +67,9 @@
         assert status < AnimationStatus.NUM_ENTRIES;
 
         View view = itemView;
-        Context context = view.getContext();
         final View backgroundView = view.findViewById(R.id.background_view);
         final View contentView = view.findViewById(R.id.content_view);
-        final int cardNormalMargin =
-                (int) context.getResources().getDimension(R.dimen.tab_list_card_padding);
-        final int cardBackgroundMargin =
-                (int) context.getResources().getDimension(R.dimen.tab_list_card_background_margin);
-        final Drawable greyBackground =
-                AppCompatResources.getDrawable(context, R.drawable.tab_grid_card_background_grey);
-        final Drawable normalBackground =
-                AppCompatResources.getDrawable(context, R.drawable.popup_bg);
-        final Drawable selectedBackground = new InsetDrawable(
-                AppCompatResources.getDrawable(context, R.drawable.selected_tab_background),
-                (int) context.getResources().getDimension(R.dimen.tab_list_selected_inset_kitkat));
+        final View selectedViewBelowLollipop = view.findViewById(R.id.selected_view_below_lollipop);
         boolean isZoomIn = status == AnimationStatus.SELECTED_CARD_ZOOM_IN
                 || status == AnimationStatus.HOVERED_CARD_ZOOM_IN;
         boolean isHovered = status == AnimationStatus.HOVERED_CARD_ZOOM_IN
@@ -94,14 +78,10 @@
         long duration = isRestore ? RESTORE_ANIMATION_DURATION_MS
                                   : TabListRecyclerView.BASE_ANIMATION_DURATION_MS;
         float scale = isZoomIn ? ZOOM_IN_SCALE : 1f;
-        ViewGroup.MarginLayoutParams backgroundParams =
-                (ViewGroup.MarginLayoutParams) backgroundView.getLayoutParams();
         View animateView = isHovered ? contentView : view;
 
         if (status == AnimationStatus.HOVERED_CARD_ZOOM_IN) {
-            backgroundParams.setMargins(
-                    cardNormalMargin, cardNormalMargin, cardNormalMargin, cardNormalMargin);
-            backgroundView.setBackground(greyBackground);
+            backgroundView.setVisibility(View.VISIBLE);
         }
 
         AnimatorSet scaleAnimator = new AnimatorSet();
@@ -109,13 +89,10 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (!isZoomIn) {
-                    backgroundParams.setMargins(cardBackgroundMargin, cardBackgroundMargin,
-                            cardBackgroundMargin, cardBackgroundMargin);
+                    backgroundView.setVisibility(View.GONE);
                     if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-                        backgroundView.setBackground(
-                                isSelected ? selectedBackground : normalBackground);
-                    } else {
-                        backgroundView.setBackground(normalBackground);
+                        selectedViewBelowLollipop.setVisibility(
+                                isSelected ? View.VISIBLE : View.GONE);
                     }
                 }
                 mIsAnimating = false;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index c07ff4e..cf97591 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -74,7 +74,6 @@
         mTabSelectionEditorCoordinator = new TabSelectionEditorCoordinator(
                 context, compositorViewHolder, tabModelSelector, tabContentManager);
 
-        TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider;
         mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector,
                 fullscreenManager, compositorViewHolder,
                 mTabSelectionEditorCoordinator.getController());
@@ -228,6 +227,9 @@
         if (mTabGridDialogCoordinator != null) {
             mTabGridDialogCoordinator.destroy();
         }
+        if (mUndoGroupSnackbarController != null) {
+            mUndoGroupSnackbarController.destroy();
+        }
         mMediator.destroy();
         mLifecycleDispatcher.unregister(this);
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index dfc38ca..d24cf5a2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -45,6 +46,7 @@
     private final Paint mThumbnailFramePaint;
     private final Paint mTextPaint;
     private final Paint mFaviconBackgroundPaint;
+    private final int mFaviconBackgroundPaintColor;
     private final List<Rect> mFaviconRects = new ArrayList<>(4);
     private final List<RectF> mThumbnailRects = new ArrayList<>(4);
     private final List<RectF> mFaviconBackgroundRects = new ArrayList<>(4);
@@ -190,55 +192,55 @@
 
     MultiThumbnailCardProvider(Context context, TabContentManager tabContentManager,
             TabModelSelector tabModelSelector) {
+        Resources resource = context.getResources();
+
         mTabContentManager = tabContentManager;
         mTabModelSelector = tabModelSelector;
-        mRadius = context.getResources().getDimension(R.dimen.tab_list_mini_card_radius);
-        mSize = (int) context.getResources().getDimension(
-                R.dimen.tab_grid_thumbnail_card_default_size);
-        mFaviconCirclePadding = context.getResources().getDimension(
-                R.dimen.tab_grid_thumbnail_favicon_background_padding);
+        mRadius = resource.getDimension(R.dimen.tab_list_mini_card_radius);
+        mSize = (int) resource.getDimension(R.dimen.tab_grid_thumbnail_card_default_size);
+        mFaviconCirclePadding =
+                resource.getDimension(R.dimen.tab_grid_thumbnail_favicon_background_padding);
         mTabListFaviconProvider = new TabListFaviconProvider(context, Profile.getLastUsedProfile());
 
         // Initialize Paints to use.
         mEmptyThumbnailPaint = new Paint();
         mEmptyThumbnailPaint.setStyle(Paint.Style.FILL);
-        mEmptyThumbnailPaint.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_100));
+        mEmptyThumbnailPaint.setColor(ApiCompatibilityUtils.getColor(
+                resource, R.color.tab_list_mini_card_default_background_color));
         mEmptyThumbnailPaint.setAntiAlias(true);
 
         mThumbnailFramePaint = new Paint();
         mThumbnailFramePaint.setStyle(Paint.Style.STROKE);
         mThumbnailFramePaint.setStrokeWidth(
-                context.getResources().getDimension(R.dimen.tab_list_mini_card_frame_size));
+                resource.getDimension(R.dimen.tab_list_mini_card_frame_size));
         mThumbnailFramePaint.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_300));
+                ApiCompatibilityUtils.getColor(resource, R.color.divider_bg_color));
         mThumbnailFramePaint.setAntiAlias(true);
 
         mTextPaint = new Paint();
-        mTextPaint.setTextSize(
-                context.getResources().getDimension(R.dimen.compositor_tab_title_text_size));
+        mTextPaint.setTextSize(resource.getDimension(R.dimen.compositor_tab_title_text_size));
         mTextPaint.setFakeBoldText(true);
         mTextPaint.setAntiAlias(true);
         mTextPaint.setTextAlign(Paint.Align.CENTER);
+        mTextPaint.setColor(ApiCompatibilityUtils.getColor(resource, R.color.default_text_color));
 
+        mFaviconBackgroundPaintColor =
+                ApiCompatibilityUtils.getColor(resource, R.color.favicon_background_color);
         mFaviconBackgroundPaint = new Paint();
         mFaviconBackgroundPaint.setAntiAlias(true);
-        mFaviconBackgroundPaint.setColor(Color.WHITE);
+        mFaviconBackgroundPaint.setColor(mFaviconBackgroundPaintColor);
         mFaviconBackgroundPaint.setStyle(Paint.Style.FILL);
         mFaviconBackgroundPaint.setShadowLayer(
-                context.getResources().getDimension(
-                        R.dimen.tab_grid_thumbnail_favicon_background_radius),
-                0,
-                context.getResources().getDimension(
-                        R.dimen.tab_grid_thumbnail_favicon_background_down_shift),
-                context.getResources().getColor(R.color.modern_grey_800_alpha_38));
+                resource.getDimension(R.dimen.tab_grid_thumbnail_favicon_background_radius), 0,
+                resource.getDimension(R.dimen.tab_grid_thumbnail_favicon_background_down_shift),
+                resource.getColor(R.color.modern_grey_800_alpha_38));
 
         // Initialize Rects for thumbnails.
-        float thumbnailPadding = context.getResources().getDimension(R.dimen.tab_list_card_padding);
+        float thumbnailPadding = resource.getDimension(R.dimen.tab_list_card_padding);
         float thumbnailFaviconPadding =
-                context.getResources().getDimension(R.dimen.tab_grid_thumbnail_favicon_padding);
-        float thumbnailFaviconBackgroundPadding = context.getResources().getDimension(
-                R.dimen.tab_grid_thumbnail_favicon_frame_padding);
+                resource.getDimension(R.dimen.tab_grid_thumbnail_favicon_padding);
+        float thumbnailFaviconBackgroundPadding =
+                resource.getDimension(R.dimen.tab_grid_thumbnail_favicon_frame_padding);
 
         mThumbnailRects.add(new RectF(thumbnailPadding, thumbnailPadding,
                 mSize / 2 - thumbnailPadding / 2, mSize / 2 - thumbnailPadding / 2));
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCarouselViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCarouselViewHolder.java
new file mode 100644
index 0000000..926735e
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCarouselViewHolder.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.tab_ui.R;
+
+/**
+ * {@link TabGriViewHolder} for tab carousel. Owns the tab info card
+ * and the associated view hierarchy.
+ */
+final class TabCarouselViewHolder extends TabGridViewHolder {
+    protected TabCarouselViewHolder(View itemView) {
+        super(itemView);
+    }
+
+    public static TabGridViewHolder create(ViewGroup parent, int itemViewType) {
+        final TabGridViewHolder viewHolder = TabGridViewHolder.create(parent, itemViewType);
+        // TODO(mattsimmons): Make this more dynamic based on parent/recycler view height.
+        final Context context = parent.getContext();
+        viewHolder.itemView.getLayoutParams().width =
+                context.getResources().getDimensionPixelSize(R.dimen.tab_carousel_card_width);
+
+        return viewHolder;
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java
index 627e617..3782d5f6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java
@@ -14,7 +14,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_PADDING;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER;
 
-import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.browser.util.ColorUtils;
@@ -47,7 +47,7 @@
         } else if (INITIAL_SCROLL_INDEX == propertyKey) {
             // RecyclerView#scrollToPosition(int) behaves incorrectly first time after cold start.
             int index = (Integer) model.get(INITIAL_SCROLL_INDEX);
-            ((GridLayoutManager) view.getLayoutManager()).scrollToPositionWithOffset(index, 0);
+            ((LinearLayoutManager) view.getLayoutManager()).scrollToPositionWithOffset(index, 0);
         } else if (TOP_CONTROLS_HEIGHT == propertyKey) {
             FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
             params.topMargin = model.get(TOP_CONTROLS_HEIGHT);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index ec811e8..f67bd7e4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -45,13 +45,11 @@
             Resources res = holder.itemView.getResources();
             Resources.Theme theme = holder.itemView.getContext().getTheme();
             if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-                Drawable selectedDrawable = new InsetDrawable(
-                        ResourcesCompat.getDrawable(res, R.drawable.selected_tab_background, theme),
-                        (int) res.getDimension(R.dimen.tab_list_selected_inset_kitkat));
-                Drawable elevationDrawable =
-                        ResourcesCompat.getDrawable(res, R.drawable.popup_bg, theme);
-                holder.backgroundView.setBackground(
-                        item.get(TabProperties.IS_SELECTED) ? selectedDrawable : elevationDrawable);
+                if (item.get(TabProperties.IS_SELECTED)) {
+                    holder.selectedViewBelowLollipop.setVisibility(View.VISIBLE);
+                } else {
+                    holder.selectedViewBelowLollipop.setVisibility(View.GONE);
+                }
             } else {
                 Drawable drawable = new InsetDrawable(
                         ResourcesCompat.getDrawable(res, R.drawable.selected_tab_background, theme),
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
index a492e97..c4047c07 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.support.annotation.IntDef;
-import android.support.v4.content.ContextCompat;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -19,6 +18,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+// TODO(mattsimmons): Rename this to be less `grid` specific since it's used by both GRID and
+//  CAROUSEL modes. Do the same for the grid layouts inflated in `create`.
 /**
  * {@link RecyclerView.ViewHolder} for tab grid. Owns the tab info card
  * and the associated view hierarchy.
@@ -37,6 +38,8 @@
     public final ImageView thumbnail;
     public final ButtonCompat createGroupButton;
     public final View backgroundView;
+    public final View selectedViewBelowLollipop;
+    public final View dividerView;
     public final ImageView actionButton;
 
     private int mTabId;
@@ -45,13 +48,12 @@
         super(itemView);
         this.thumbnail = itemView.findViewById(R.id.tab_thumbnail);
         this.title = itemView.findViewById(R.id.tab_title);
-        // TODO(yuezhanggg): Remove this when the strip is properly tinted. (crbug/939915)
-        title.setTextColor(
-                ContextCompat.getColor(itemView.getContext(), R.color.default_text_color_dark));
         this.favicon = itemView.findViewById(R.id.tab_favicon);
         this.actionButton = itemView.findViewById(R.id.action_button);
         this.createGroupButton = itemView.findViewById(R.id.create_group_button);
         this.backgroundView = itemView.findViewById(R.id.background_view);
+        this.selectedViewBelowLollipop = itemView.findViewById(R.id.selected_view_below_lollipop);
+        this.dividerView = itemView.findViewById(R.id.divider_view);
     }
 
     public static TabGridViewHolder create(ViewGroup parent, int itemViewType) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index d626a23e..85bcfa3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.support.v4.widget.TextViewCompat;
-import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -89,7 +88,6 @@
      */
     void setupDialogToolbarLayout() {
         Context context = getContext();
-        setBackground(AppCompatResources.getDrawable(context, R.drawable.tab_grid_card_background));
         mLeftButton.setImageResource(org.chromium.chrome.R.drawable.ic_arrow_back_24dp);
         int topicMargin =
                 (int) context.getResources().getDimension(R.dimen.tab_group_toolbar_topic_margin);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 8c6eb5e8..a5653f4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -37,20 +37,27 @@
  * Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes.
  */
 public class TabListCoordinator implements Destroyable {
-    /** Modes of showing the list of tabs */
-    @IntDef({TabListMode.GRID, TabListMode.STRIP})
+    /**
+     * Modes of showing the list of tabs.
+     *
+     * NOTE: CAROUSEL mode currently uses a fixed height and card width set in dimens.xml with names
+     *  tab_carousel_height and tab_carousel_card_width.
+     *
+     *  STRIP and GRID modes will have height equal to that of the container view.
+     * */
+    @IntDef({TabListMode.GRID, TabListMode.STRIP, TabListMode.CAROUSEL})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TabListMode {
         int GRID = 0;
         int STRIP = 1;
-        int NUM_ENTRIES = 2;
+        int CAROUSEL = 2;
+        int NUM_ENTRIES = 3;
     }
 
     static final int GRID_LAYOUT_SPAN_COUNT_PORTRAIT = 2;
     static final int GRID_LAYOUT_SPAN_COUNT_LANDSCAPE = 3;
     private final SimpleRecyclerViewMcpBase mModelChangeProcessor;
     private final TabListMediator mMediator;
-    private final TabModelSelector mTabModelSelector;
     private final TabListRecyclerView mRecyclerView;
     private final @TabListMode int mMode;
     private final Rect mThumbnailLocationOfCurrentTab = new Rect();
@@ -96,10 +103,9 @@
             boolean attachToParent, String componentName) {
         TabListModel tabListModel = new TabListModel();
         mMode = mode;
-        mTabModelSelector = tabModelSelector;
 
         RecyclerViewAdapter adapter;
-        if (mMode == TabListMode.GRID) {
+        if (mMode == TabListMode.GRID || mMode == TabListMode.CAROUSEL) {
             if (itemViewTypeCallback == null) {
                 itemViewTypeCallback = mGridDefaultItemViewTypeCallback;
             }
@@ -113,7 +119,11 @@
                             viewHolder.resetThumbnail();
                         }
                     };
-            adapter = new RecyclerViewAdapter<>(mcp, TabGridViewHolder::create);
+            if (mode == TabListMode.CAROUSEL) {
+                adapter = new RecyclerViewAdapter<>(mcp, TabCarouselViewHolder::create);
+            } else {
+                adapter = new RecyclerViewAdapter<>(mcp, TabGridViewHolder::create);
+            }
             mModelChangeProcessor = mcp;
         } else if (mMode == TabListMode.STRIP) {
             SimpleRecyclerViewMcpBase<PropertyModel, TabStripViewHolder, PropertyKey> mcp =
@@ -135,6 +145,14 @@
             mRecyclerView = parentView.findViewById(R.id.tab_list_view);
         }
 
+        if (mode == TabListMode.CAROUSEL) {
+            // TODO(mattsimmons): Remove this height and let the parent determine the correct
+            //  height. This can be done once the width is dynamic as well in
+            //  TabCarouselViewHolder.
+            mRecyclerView.getLayoutParams().height =
+                    context.getResources().getDimensionPixelSize(R.dimen.tab_carousel_height);
+        }
+
         mRecyclerView.setAdapter(adapter);
 
         if (mMode == TabListMode.GRID) {
@@ -143,7 +161,7 @@
                                     == Configuration.ORIENTATION_PORTRAIT
                             ? GRID_LAYOUT_SPAN_COUNT_PORTRAIT
                             : GRID_LAYOUT_SPAN_COUNT_LANDSCAPE));
-        } else if (mMode == TabListMode.STRIP) {
+        } else if (mMode == TabListMode.STRIP || mMode == TabListMode.CAROUSEL) {
             mRecyclerView.setLayoutManager(
                     new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
         }
@@ -170,9 +188,6 @@
             touchHelper.attachToRecyclerView(mRecyclerView);
             mMediator.registerOrientationListener(
                     (GridLayoutManager) mRecyclerView.getLayoutManager());
-            touchHelper.attachToRecyclerView(mRecyclerView);
-            mMediator.registerOrientationListener(
-                    (GridLayoutManager) mRecyclerView.getLayoutManager());
         }
 
         if (actionOnRelatedTabs) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
index 45457ec..4404768 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
@@ -8,7 +8,9 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.v7.content.res.AppCompatResources;
 
 import org.chromium.base.Callback;
@@ -27,6 +29,9 @@
     private final int mFaviconSize;
     private final Profile mProfile;
     private final FaviconHelper mFaviconHelper;
+    private final Context mContext;
+    @ColorInt
+    private final int mDefaultIconColor;
 
     /**
      * Construct the provider that provides favicons for tab list.
@@ -34,6 +39,7 @@
      * @param profile The profile to use for getting favicons.
      */
     public TabListFaviconProvider(Context context, Profile profile) {
+        mContext = context;
         mFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.default_favicon_size);
         mProfile = profile;
         mFaviconHelper = new FaviconHelper();
@@ -52,6 +58,7 @@
                     BitmapFactory.decodeResource(context.getResources(), R.drawable.chromelogo16);
             sRoundedChromeDrawable = processBitmap(chromeBitmap);
         }
+        mDefaultIconColor = mContext.getResources().getColor(R.color.default_icon_color);
     }
 
     private Drawable processBitmap(Bitmap bitmap) {
@@ -64,7 +71,7 @@
      * @return The scaled rounded Globe Drawable as default favicon.
      */
     public Drawable getDefaultFaviconDrawable() {
-        return sRoundedGlobeDrawable;
+        return getRoundedGlobeDrawable();
     }
 
     /**
@@ -76,12 +83,12 @@
     public void getFaviconForUrlAsync(
             String url, boolean isIncognito, Callback<Drawable> faviconCallback) {
         if (NativePageFactory.isNativePageUrl(url, isIncognito)) {
-            faviconCallback.onResult(sRoundedChromeDrawable);
+            faviconCallback.onResult(getRoundedChromeDrawable());
         } else {
             mFaviconHelper.getLocalFaviconImageForURL(
                     mProfile, url, mFaviconSize, (image, iconUrl) -> {
                         if (image == null) {
-                            faviconCallback.onResult(sRoundedGlobeDrawable);
+                            faviconCallback.onResult(getRoundedGlobeDrawable());
                         } else {
                             faviconCallback.onResult(processBitmap(image));
                         }
@@ -99,9 +106,25 @@
     public Drawable getFaviconForUrlSync(String url, boolean isIncognito, Bitmap icon) {
         if (icon == null) {
             boolean isNativeUrl = NativePageFactory.isNativePageUrl(url, isIncognito);
-            return isNativeUrl ? sRoundedChromeDrawable : sRoundedGlobeDrawable;
+            return isNativeUrl ? getRoundedChromeDrawable() : getRoundedGlobeDrawable();
         } else {
             return processBitmap(icon);
         }
     }
+
+    private Drawable getRoundedChromeDrawable() {
+        // Since static variable is still loaded when activity is destroyed due to configuration
+        // changes, e.g. light/dark theme changes, setColorFilter is needed when we retrieve the
+        // drawable. setColorFilter would be a no-op if color and the mode are the same.
+        sRoundedChromeDrawable.setColorFilter(mDefaultIconColor, PorterDuff.Mode.SRC_IN);
+        return sRoundedChromeDrawable;
+    }
+
+    private Drawable getRoundedGlobeDrawable() {
+        // Since static variable is still loaded when activity is destroyed due to configuration
+        // changes, e.g. light/dark theme changes, setColorFilter is needed when we retrieve the
+        // drawable. setColorFilter would be a no-op if color and the mode are the same.
+        sRoundedGlobeDrawable.setColorFilter(mDefaultIconColor, PorterDuff.Mode.SRC_IN);
+        return sRoundedGlobeDrawable;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
index 0825136c..b77e3bd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
@@ -9,7 +9,10 @@
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.tab_ui.R;
 
@@ -27,6 +30,7 @@
     private final TabModelSelector mTabModelSelector;
     private final SnackbarManager.SnackbarManageable mSnackbarManageable;
     private final TabGroupModelFilter.Observer mTabGroupModelFilterObserver;
+    private final TabModelSelectorObserver mTabModelSelectorObserver;
 
     private class TabUndoInfo {
         public final Tab tab;
@@ -82,6 +86,32 @@
         ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
                  true))
                 .addTabGroupObserver(mTabGroupModelFilterObserver);
+
+        mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() {
+            @Override
+            public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
+                mSnackbarManageable.getSnackbarManager().dismissSnackbars(
+                        UndoGroupSnackbarController.this);
+            }
+        };
+
+        mTabModelSelector.addObserver(mTabModelSelectorObserver);
+    }
+
+    /**
+     * Cleans up this class, removes {@link TabModelSelectorObserver} from {@link TabModelSelector}
+     * and {@link TabGroupModelFilter.Observer} from {@link TabGroupModelFilter}.
+     */
+    public void destroy() {
+        if (mTabModelSelector != null) {
+            mTabModelSelector.removeObserver(mTabModelSelectorObserver);
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     false))
+                    .removeTabGroupObserver(mTabGroupModelFilterObserver);
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     true))
+                    .removeTabGroupObserver(mTabGroupModelFilterObserver);
+        }
     }
 
     private void showUndoGroupSnackbar(List<TabUndoInfo> tabUndoInfo) {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 0ffca61..35dcd7b 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -12,10 +12,10 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -145,14 +145,10 @@
             Assert.assertFalse(((FrameLayout) (holder.itemView)).getForeground() != null);
         } else {
             model.set(TabProperties.IS_SELECTED, true);
-            Drawable selectedDrawable =
-                    holder.itemView.findViewById(R.id.background_view).getBackground();
-            Assert.assertTrue(selectedDrawable != null);
+            View selectedView = holder.itemView.findViewById(R.id.selected_view_below_lollipop);
+            Assert.assertTrue(selectedView.getVisibility() == View.VISIBLE);
             model.set(TabProperties.IS_SELECTED, false);
-            Drawable elevationDrawable =
-                    holder.itemView.findViewById(R.id.background_view).getBackground();
-            Assert.assertTrue(elevationDrawable != null);
-            Assert.assertNotSame(selectedDrawable, elevationDrawable);
+            Assert.assertTrue(selectedView.getVisibility() == View.GONE);
         }
         mStripModel.set(TabProperties.IS_SELECTED, true);
         Assert.assertTrue(((FrameLayout) (mTabStripViewHolder.itemView)).getForeground() != null);
@@ -186,6 +182,8 @@
     @Test
     @MediumTest
     public void testAnimationRestored() throws Exception {
+        View backgroundView = mTabGridViewHolder.itemView.findViewById(R.id.background_view);
+
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mGridModel.set(TabProperties.IS_SELECTED, true);
             mGridModel.set(TabProperties.CARD_ANIMATION_STATUS,
@@ -193,9 +191,16 @@
         });
         CriteriaHelper.pollUiThread(
                 () -> !((ClosableTabGridViewHolder) mTabGridViewHolder).getIsAnimatingForTesting());
-        Drawable selectedTabRestoredBackground =
-                mTabGridViewHolder.itemView.findViewById(R.id.background_view).getBackground();
-        Assert.assertNotNull(selectedTabRestoredBackground);
+
+        Assert.assertTrue(backgroundView.getVisibility() == View.GONE);
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            View selectedView =
+                    mTabGridViewHolder.itemView.findViewById(R.id.selected_view_below_lollipop);
+            Assert.assertTrue(selectedView.getVisibility() == View.VISIBLE);
+        } else {
+            Drawable selectedDrawable = mTabGridViewHolder.itemView.getForeground();
+            Assert.assertNotNull(selectedDrawable);
+        }
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mGridModel.set(TabProperties.IS_SELECTED, false);
@@ -204,19 +209,15 @@
         });
         CriteriaHelper.pollUiThread(
                 () -> !((ClosableTabGridViewHolder) mTabGridViewHolder).getIsAnimatingForTesting());
-        Drawable normalTabRestoredBackground =
-                mTabGridViewHolder.itemView.findViewById(R.id.background_view).getBackground();
-        Assert.assertNotNull(normalTabRestoredBackground);
+        Assert.assertTrue(backgroundView.getVisibility() == View.GONE);
 
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
-            // After restoring, the background should be the same because for post-M devices, the
-            // selected state is reflected through foreground and the background is always the same.
-            Assert.assertSame(selectedTabRestoredBackground, normalTabRestoredBackground);
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            View selectedView =
+                    mTabGridViewHolder.itemView.findViewById(R.id.selected_view_below_lollipop);
+            Assert.assertTrue(selectedView.getVisibility() == View.GONE);
         } else {
-            // For pre-M devices, after restoring, the background should still reflect the selection
-            // status.
-            Assert.assertTrue(selectedTabRestoredBackground instanceof InsetDrawable);
-            Assert.assertNotSame(normalTabRestoredBackground, selectedTabRestoredBackground);
+            Drawable selectedDrawable = mTabGridViewHolder.itemView.getForeground();
+            Assert.assertNull(selectedDrawable);
         }
     }
 
diff --git a/chrome/android/features/vr/java/AndroidManifest.xml b/chrome/android/features/vr/java/AndroidManifest.xml
index 4a2c233..388353f7 100644
--- a/chrome/android/features/vr/java/AndroidManifest.xml
+++ b/chrome/android/features/vr/java/AndroidManifest.xml
@@ -8,9 +8,17 @@
     featureSplit="vr">
 
     <dist:module
-        dist:onDemand="true"
-        dist:title="@string/vr_module_title">
-        <dist:fusing dist:include="false" />
+      dist:instant="false"
+      dist:title="@string/vr_module_title">
+      <dist:fusing dist:include="false" />
+      <dist:delivery>
+        <dist:install-time>
+          <dist:conditions>
+            <dist:device-feature dist:name="android.hardware.vr.high_performance" />
+          </dist:conditions>
+        </dist:install-time>
+        <dist:on-demand />
+      </dist:delivery>
     </dist:module>
 
     <application>
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index c186f5e..dd75dd5b 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -77,6 +77,9 @@
     <uses-feature android:name="android.hardware.screen.landscape" android:required="false"/>
     <!-- Indicates that head tracking should be done in 6DoF, if available -->
     <uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="false"/>
+    <!-- Feature declarations required to support conditional install for VR DFM -->
+    <uses-feature android:name="android.hardware.sensor.gyroscope" android:required="false"/>
+    <uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false"/>
     {% endif %}
 
     <permission android:name="{{ manifest_package }}.permission.CHILD_SERVICE" android:protectionLevel="signature" />
diff --git a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
index 7b7d0d25..7b9307d 100644
--- a/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_bundle.proguard_flags.expected
@@ -292,11 +292,6 @@
 # Fragments loaded by name via Fragment.instantiate(Context,String)
 # Not all fragments in this package are PreferenceFragments. E.g. HomepageEditor
 # is a normal Fragment.
-# TODO(crbug.com/967022): Remove the android.app.Fragment rule once all
-# Fragments have been migrated to the support library.
--keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
-  public <init>();
-}
 -keep public class org.chromium.chrome.browser.preferences.** extends android.support.v4.app.Fragment {
   public <init>();
 }
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 5eae1d0..441a2a3b4d 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -51,6 +51,8 @@
       android:required="false"
       android:version="1"/>
   <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+  <uses-feature android:name="android.hardware.sensor.gyroscope" android:required="false"/>
+  <uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false"/>
   <uses-feature android:name="android.hardware.screen.landscape" android:required="false"/>
   <uses-feature android:name="android.hardware.microphone" android:required="false"/>
   <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
diff --git a/chrome/android/java/proguard.flags b/chrome/android/java/proguard.flags
index e1d86756..0cd07a3 100644
--- a/chrome/android/java/proguard.flags
+++ b/chrome/android/java/proguard.flags
@@ -5,11 +5,6 @@
 # Fragments loaded by name via Fragment.instantiate(Context,String)
 # Not all fragments in this package are PreferenceFragments. E.g. HomepageEditor
 # is a normal Fragment.
-# TODO(crbug.com/967022): Remove the android.app.Fragment rule once all
-# Fragments have been migrated to the support library.
--keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
-  public <init>();
-}
 -keep public class org.chromium.chrome.browser.preferences.** extends android.support.v4.app.Fragment {
   public <init>();
 }
diff --git a/chrome/android/java/res/layout/context_menu_divider.xml b/chrome/android/java/res/layout/context_menu_divider.xml
index a1dd372..51785dc 100644
--- a/chrome/android/java/res/layout/context_menu_divider.xml
+++ b/chrome/android/java/res/layout/context_menu_divider.xml
@@ -5,6 +5,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:paddingTop="@dimen/revamped_context_menu_divider_padding"
     android:paddingBottom="@dimen/revamped_context_menu_divider_padding">
 
 
diff --git a/chrome/android/java/res/layout/preference_switch.xml b/chrome/android/java/res/layout/preference_switch.xml
deleted file mode 100644
index 4e8a4bc..0000000
--- a/chrome/android/java/res/layout/preference_switch.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<android.support.v7.widget.SwitchCompat
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/switch_widget"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:focusable="false"
-    android:clickable="false"
-    android:background="@null" />
diff --git a/chrome/android/java/res/layout/revamped_context_menu_header.xml b/chrome/android/java/res/layout/revamped_context_menu_header.xml
index 2cdbba33..2fe64d0 100644
--- a/chrome/android/java/res/layout/revamped_context_menu_header.xml
+++ b/chrome/android/java/res/layout/revamped_context_menu_header.xml
@@ -38,7 +38,7 @@
             android:scaleType="centerInside"
             android:importantForAccessibility="no"
             android:layout_marginTop="@dimen/revamped_context_menu_header_vertical_padding"
-            android:layout_marginBottom="@dimen/revamped_context_menu_header_vertical_padding"
+            android:layout_marginBottom="@dimen/revamped_context_menu_bottom_padding"
             app:cornerRadiusTopStart="@dimen/default_rounded_corner_radius"
             app:cornerRadiusTopEnd="@dimen/default_rounded_corner_radius"
             app:cornerRadiusBottomStart="@dimen/default_rounded_corner_radius"
@@ -53,7 +53,7 @@
         android:orientation="vertical"
         android:layout_gravity="center_vertical"
         android:paddingTop="@dimen/revamped_context_menu_header_vertical_padding"
-        android:paddingBottom="@dimen/revamped_context_menu_header_vertical_padding">
+        android:paddingBottom="@dimen/revamped_context_menu_bottom_padding">
 
         <org.chromium.ui.widget.TextViewWithLeading
             android:id="@+id/menu_header_title"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 1d52cede..5b41c992 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -962,31 +962,6 @@
         <item name="android:textColor">@color/suggestion_url_dark_modern</item>
     </style>
 
-    <style name="TextAppearance.OmniboxAnswerTextLarge">
-        <item name="android:textSize">24sp</item>
-        <item name="android:textColor">@color/answers_answer_text</item>
-    </style>
-
-    <style name="TextAppearance.OmniboxAnswerTextMedium">
-        <item name="android:textSize">20sp</item>
-        <item name="android:textColor">@color/answers_answer_text</item>
-    </style>
-
-    <style name="TextAppearance.OmniboxAnswerTextSmall">
-        <item name="android:textSize">@dimen/text_size_small</item>
-        <item name="android:textColor">@color/answers_answer_text</item>
-    </style>
-
-    <style name="TextAppearance.OmniboxAnswerDescriptionNegative">
-        <item name="android:textSize">@dimen/text_size_large</item>
-        <item name="android:textColor">@color/answers_description_text_negative</item>
-    </style>
-
-    <style name="TextAppearance.OmniboxAnswerDescriptionPositive">
-        <item name="android:textSize">@dimen/text_size_large</item>
-        <item name="android:textColor">@color/answers_description_text_positive</item>
-    </style>
-
     <style name="TextAppearance.OmniboxAnswerDescriptionNegativeSmall">
         <item name="android:textSize">@dimen/text_size_small</item>
         <item name="android:textColor">@color/answers_description_text_negative</item>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 94e8ca49..d2ca0cf 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -70,7 +70,6 @@
     <color name="suggestion_url_light_modern">@color/modern_blue_300</color>
     <color name="answers_description_text_negative">@color/default_red</color>
     <color name="answers_description_text_positive">@color/default_green</color>
-    <color name="answers_answer_text">#8A8A8A</color>
 
     <!--  Distilled Page Prefs colors -->
     <color name="distilled_page_prefs_selected">#999999</color>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 5f46ae4..46f734b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -2110,7 +2110,7 @@
         }
 
         if (id == R.id.preferences_id) {
-            PreferencesLauncher.launchSettingsPage(this, null);
+            PreferencesLauncher.launchSettingsPageCompat(this, null);
             RecordUserAction.record("MobileMenuSettings");
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 72784eb..39ff630 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -277,7 +277,6 @@
             "OmniboxUIExperimentHideSteadyStateUrlScheme";
     public static final String OMNIBOX_HIDE_TRIVIAL_SUBDOMAINS_IN_STEADY_STATE =
             "OmniboxUIExperimentHideSteadyStateUrlTrivialSubdomains";
-    public static final String OMNIBOX_NEW_ANSWER_LAYOUT = "OmniboxNewAnswerLayout";
     public static final String OMNIBOX_RICH_ENTITY_SUGGESTIONS = "OmniboxRichEntitySuggestions";
     public static final String OMNIBOX_SHOW_SUGGESTION_FAVICONS =
             "OmniboxUIExperimentShowSuggestionFavicons";
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 92bb9ec..9135e3e 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
@@ -144,6 +144,8 @@
     class BookmarkDragStateDelegate implements DragStateDelegate {
         private BookmarkDelegate mBookmarkDelegate;
         private SelectionDelegate<BookmarkId> mSelectionDelegate;
+        private AccessibilityManager mA11yManager;
+        private AccessibilityManager.AccessibilityStateChangeListener mA11yChangeListener;
         private boolean mA11yEnabled;
 
         void onBookmarkDelegateInitialized(BookmarkDelegate delegate) {
@@ -151,11 +153,12 @@
 
             mSelectionDelegate = delegate.getSelectionDelegate();
 
-            AccessibilityManager a11yManager =
+            mA11yManager =
                     (AccessibilityManager) getSelectableListLayout().getContext().getSystemService(
                             Context.ACCESSIBILITY_SERVICE);
-            mA11yEnabled = a11yManager.isEnabled();
-            a11yManager.addAccessibilityStateChangeListener(enabled -> mA11yEnabled = enabled);
+            mA11yEnabled = mA11yManager.isEnabled();
+            mA11yChangeListener = enabled -> mA11yEnabled = enabled;
+            mA11yManager.addAccessibilityStateChangeListener(mA11yChangeListener);
         }
 
         // DragStateDelegate implementation
@@ -169,6 +172,17 @@
         public boolean getDragActive() {
             return getDragEnabled() && mSelectionDelegate.isSelectionEnabled();
         }
+
+        @VisibleForTesting
+        @Override
+        public void setA11yStateForTesting(boolean a11yEnabled) {
+            if (mA11yManager != null) {
+                mA11yManager.removeAccessibilityStateChangeListener(mA11yChangeListener);
+            }
+            mA11yChangeListener = null;
+            mA11yManager = null;
+            mA11yEnabled = a11yEnabled;
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index 0dadf40..63ad015 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.support.annotation.IntDef;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ImageView;
 
 import org.chromium.base.VisibleForTesting;
@@ -39,6 +40,8 @@
     @Location
     private int mLocation;
 
+    private static final String TAG = "BookmarkRow";
+
     @IntDef({Location.TOP, Location.MIDDLE, Location.BOTTOM})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Location {
@@ -57,6 +60,7 @@
 
     /**
      * Updates this row for the given {@link BookmarkId}.
+     *
      * @return The {@link BookmarkItem} corresponding the given {@link BookmarkId}.
      */
     // TODO(crbug.com/160194): Clean up these 2 functions after bookmark reordering launches.
@@ -77,7 +81,7 @@
      * within the list of bookmarks.
      *
      * @param bookmarkId The BookmarkId that this BookmarkRow now contains.
-     * @param location The location of this BookmarkRow.
+     * @param location   The location of this BookmarkRow.
      * @return The BookmarkItem corresponding to BookmarkId.
      */
     BookmarkItem setBookmarkId(BookmarkId bookmarkId, @Location int location) {
@@ -87,26 +91,31 @@
 
     private void updateVisualState() {
         BookmarkItem bookmarkItem = mDelegate.getModel().getBookmarkById(mBookmarkId);
+        // This check is needed because updateVisualState is called when the item has been deleted
+        // in the model but not in the adapter. If we hit this if-block, the
+        // item is about to be deleted, and we don't need to do anything.
+        if (bookmarkItem == null) {
+            return;
+        }
+        // TODO(jhimawan): Look into using cleanup(). Perhaps unhook the selection state observer?
+
         // If the visibility of the drag handle or more icon is not set later, it will be gone.
         mDragHandle.setVisibility(GONE);
         mMoreIcon.setVisibility(GONE);
 
-        if (mReorderBookmarksEnabled) {
-            if (mDelegate.getDragStateDelegate().getDragActive()) {
-                mDragHandle.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
-                mDragHandle.setEnabled(isItemSelected());
-            } else {
-                mMoreIcon.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
-                mMoreIcon.setEnabled(isSelectionModeActive());
-            }
+        if (mReorderBookmarksEnabled && mDelegate.getDragStateDelegate().getDragActive()) {
+            mDragHandle.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
+            mDragHandle.setEnabled(isItemSelected());
         } else {
-            // Bookmark reordering is off
             mMoreIcon.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
+            mMoreIcon.setClickable(!isSelectionModeActive());
+            mMoreIcon.setEnabled(mMoreIcon.isClickable());
         }
     }
 
     /**
      * Sets the delegate to use to handle UI actions related to this view.
+     *
      * @param delegate A {@link BookmarkDelegate} instance to handle all backend interaction.
      */
     public void onDelegateInitialized(BookmarkDelegate delegate) {
@@ -117,7 +126,6 @@
 
     private void initialize() {
         mDelegate.addUIObserver(this);
-        updateSelectionState();
     }
 
     private void cleanup() {
@@ -125,10 +133,6 @@
         if (mDelegate != null) mDelegate.removeUIObserver(this);
     }
 
-    private void updateSelectionState() {
-        mMoreIcon.setEnabled(!mDelegate.getSelectionDelegate().isSelectionEnabled());
-    }
-
     // PopupMenuItem.Delegate implementation.
     @Override
     public Item[] getItems() {
@@ -226,7 +230,7 @@
     @Override
     public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {
         super.onSelectionStateChange(selectedBookmarks);
-        updateSelectionState();
+        updateVisualState();
     }
 
     // BookmarkUIObserver implementation.
@@ -253,4 +257,29 @@
     String getTitle() {
         return String.valueOf(mTitleView.getText());
     }
-}
+
+    private boolean isDragActive() {
+        if (mReorderBookmarksEnabled) {
+            return mDelegate.getDragStateDelegate().getDragActive();
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        // Override is needed in order to support long-press-to-drag on already-selected items.
+        if (isDragActive() && isItemSelected()) return true;
+        return super.onLongClick(view);
+    }
+
+    @Override
+    public void onClick(View view) {
+        // Override is needed in order to allow items to be selected / deselected with a click.
+        // Since we override #onLongClick(), we cannot rely on the base class for this behavior.
+        if (isDragActive()) {
+            toggleSelectionForItem(getItem());
+        } else {
+            super.onClick(view);
+        }
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
index 41428d84..348e68cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
@@ -121,13 +121,10 @@
     }
 
     private void setBookmarks(List<BookmarkId> bookmarks) {
-        // Update header in order to determine whether we have a Promo Header.
-        // Must be done before adding in all of our elements.
-        updateHeader();
         mElements.clear();
-        if (hasPromoHeader()) {
-            mElements.add(null);
-        }
+        // Restore the header, if it exists, then update it.
+        if (hasPromoHeader()) mElements.add(null);
+        updateHeader(false);
         for (BookmarkId bId : bookmarks) {
             mElements.add(mDelegate.getModel().getBookmarkById(bId));
         }
@@ -161,7 +158,7 @@
 
         // ViewHolder is abstract and it cannot be instantiated directly.
         ViewHolder holder = new ViewHolder(row) {};
-        ((BookmarkRow) holder.itemView).onDelegateInitialized(mDelegate);
+        ((BookmarkRow) row).onDelegateInitialized(mDelegate);
         return holder;
     }
 
@@ -223,28 +220,9 @@
         mDelegate = delegate;
         mDelegate.addUIObserver(this);
         mDelegate.getModel().addObserver(mBookmarkModelObserver);
-        // This must be registered directly with the selection delegate.
-        // addUIObserver (see above) is not sufficient.
-        // TODO(jhimawan): figure out why this is the case.
-        mDelegate.getSelectionDelegate().addObserver(ReorderBookmarkItemsAdapter.this);
 
         Runnable promoHeaderChangeAction = () -> {
-            assert mDelegate != null;
-            if (mDelegate.getCurrentState() != BookmarkUIState.STATE_FOLDER) {
-                return;
-            }
-
-            boolean wasShowingPromo = hasPromoHeader();
-            updateHeader();
-            boolean willShowPromo = hasPromoHeader();
-
-            if (!wasShowingPromo && willShowPromo) {
-                notifyItemInserted(0);
-            } else if (wasShowingPromo && willShowPromo) {
-                notifyItemChanged(0);
-            } else if (wasShowingPromo && !willShowPromo) {
-                notifyItemRemoved(0);
-            }
+            updateHeader(true);
         };
 
         mPromoHeaderManager = new BookmarkPromoHeader(mContext, promoHeaderChangeAction);
@@ -284,14 +262,14 @@
     @Override
     public void onSearchStateSet() {
         disableDrag();
-        updateHeader();
+        // Headers should not appear in Search mode
+        // Don't need to notify because we need to redraw everything in the next step
+        updateHeader(false);
         notifyDataSetChanged();
     }
 
     @Override
-    public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {
-        notifyDataSetChanged();
-    }
+    public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {}
 
     /**
      * Refresh the list of bookmarks within the currently visible folder.
@@ -350,33 +328,52 @@
         setOrder(mElements);
     }
 
-    private void updateHeader() {
+    /**
+     * Updates mPromoHeaderType. Makes sure that the 0th index of mElements is consistent with the
+     * promo header. This 0th index is null iff there is a promo header.
+     *
+     * @param shouldNotify True iff we should notify the RecyclerView of changes to the promoheader.
+     *                     (This should be false iff we are going to make further changes to the
+     *                     list of elements, as we do in setBookmarks, and true iff we are only
+     *                     changing the header, as we do in the promoHeaderChangeAction runnable).
+     */
+    private void updateHeader(boolean shouldNotify) {
         if (mDelegate == null) return;
 
-        int currentUIState = mDelegate.getCurrentState();
-        if (currentUIState == BookmarkUIState.STATE_LOADING) return;
+        boolean wasShowingPromo = hasPromoHeader();
 
-        // Reset the promo header and get rid of the Promo Header placeholder inside of mElements.
-        if (hasPromoHeader()) {
-            mElements.remove(0);
+        int currentUIState = mDelegate.getCurrentState();
+        if (currentUIState == BookmarkUIState.STATE_LOADING) {
+            return;
+        } else if (currentUIState == BookmarkUIState.STATE_SEARCHING) {
             mPromoHeaderType = ViewType.INVALID_PROMO;
+        } else {
+            switch (mPromoHeaderManager.getPromoState()) {
+                case BookmarkPromoHeader.PromoState.PROMO_NONE:
+                    mPromoHeaderType = ViewType.INVALID_PROMO;
+                    break;
+                case BookmarkPromoHeader.PromoState.PROMO_SIGNIN_PERSONALIZED:
+                    mPromoHeaderType = ViewType.PERSONALIZED_SIGNIN_PROMO;
+                    break;
+                case BookmarkPromoHeader.PromoState.PROMO_SYNC:
+                    mPromoHeaderType = ViewType.SYNC_PROMO;
+                    break;
+                default:
+                    assert false : "Unexpected value for promo state!";
+            }
         }
 
-        if (currentUIState == BookmarkUIState.STATE_SEARCHING) return;
+        boolean willShowPromo = hasPromoHeader();
 
-        assert currentUIState == BookmarkUIState.STATE_FOLDER : "Unexpected UI state";
-
-        switch (mPromoHeaderManager.getPromoState()) {
-            case BookmarkPromoHeader.PromoState.PROMO_NONE:
-                return;
-            case BookmarkPromoHeader.PromoState.PROMO_SIGNIN_PERSONALIZED:
-                mPromoHeaderType = ViewType.PERSONALIZED_SIGNIN_PROMO;
-                return;
-            case BookmarkPromoHeader.PromoState.PROMO_SYNC:
-                mPromoHeaderType = ViewType.SYNC_PROMO;
-                return;
-            default:
-                assert false : "Unexpected value for promo state!";
+        if (!wasShowingPromo && willShowPromo) {
+            // A null element at the 0th index represents a promo header.
+            mElements.add(0, null);
+            if (shouldNotify) notifyItemInserted(0);
+        } else if (wasShowingPromo && willShowPromo) {
+            if (shouldNotify) notifyItemChanged(0);
+        } else if (wasShowingPromo && !willShowPromo) {
+            mElements.remove(0);
+            if (shouldNotify) notifyItemRemoved(0);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceObservers.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceObservers.java
index 8e0e26f..78e2943 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceObservers.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceObservers.java
@@ -136,28 +136,11 @@
 
     @Nullable
     private static Observer getObserverFromClassName(String observerClassName) {
-        if (observerClassName == null) return null;
-
-        Class<?> observerClass;
         try {
-            observerClass = Class.forName(observerClassName);
-        } catch (ClassNotFoundException e) {
-            Log.w(TAG, "Unable to find observer class with name " + observerClassName);
-            return null;
-        }
-
-        if (!Observer.class.isAssignableFrom(observerClass)) {
-            Log.w(TAG, "Class " + observerClass + " is not an observer");
-            return null;
-        }
-
-        try {
+            Class<?> observerClass = Class.forName(observerClassName);
             return (Observer) observerClass.newInstance();
-        } catch (InstantiationException e) {
-            Log.w(TAG, "Unable to instantiate class (InstExc) " + observerClass);
-            return null;
-        } catch (IllegalAccessException e) {
-            Log.w(TAG, "Unable to instantiate class (IllAccExc) " + observerClass);
+        } catch (Throwable e) {
+            Log.w(TAG, "getObserverFromClassName(): %s", observerClassName, e);
             return null;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
index f40a7de5..555f389a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -167,9 +167,15 @@
                 .record(type);
         recordNotificationAgeHistogram("Mobile.SystemNotification.Content.Click.Age", createTime);
 
-        if (type == SystemNotificationType.SEND_TAB_TO_SELF) {
-            recordNotificationAgeHistogram(
-                    "Mobile.SystemNotification.Content.Click.Age.SendTabToSelf", createTime);
+        switch (type) {
+            case SystemNotificationType.SEND_TAB_TO_SELF:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Content.Click.Age.SendTabToSelf", createTime);
+                break;
+            case SystemNotificationType.CLICK_TO_CALL:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Content.Click.Age.ClickToCall", createTime);
+                break;
         }
     }
 
@@ -189,9 +195,15 @@
                 .record(type);
         recordNotificationAgeHistogram("Mobile.SystemNotification.Dismiss.Age", createTime);
 
-        if (type == SystemNotificationType.SEND_TAB_TO_SELF) {
-            recordNotificationAgeHistogram(
-                    "Mobile.SystemNotification.Dismiss.Age.SendTabToSelf", createTime);
+        switch (type) {
+            case SystemNotificationType.SEND_TAB_TO_SELF:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Dismiss.Age.SendTabToSelf", createTime);
+                break;
+            case SystemNotificationType.CLICK_TO_CALL:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Dismiss.Age.ClickToCall", createTime);
+                break;
         }
     }
 
@@ -213,9 +225,15 @@
                 .record(actionType);
         recordNotificationAgeHistogram("Mobile.SystemNotification.Action.Click.Age", createTime);
 
-        if (notificationType == SystemNotificationType.SEND_TAB_TO_SELF) {
-            recordNotificationAgeHistogram(
-                    "Mobile.SystemNotification.Action.Click.Age.SendTabToSelf", createTime);
+        switch (notificationType) {
+            case SystemNotificationType.SEND_TAB_TO_SELF:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Action.Click.Age.SendTabToSelf", createTime);
+                break;
+            case SystemNotificationType.CLICK_TO_CALL:
+                recordNotificationAgeHistogram(
+                        "Mobile.SystemNotification.Action.Click.Age.ClickToCall", createTime);
+                break;
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index f0472d1..81ef0c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -6,13 +6,9 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.view.View;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
 import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
 import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
@@ -25,9 +21,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionTextContainer;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.components.omnibox.AnswerType;
 import org.chromium.components.omnibox.SuggestionAnswer;
@@ -47,7 +40,6 @@
     private final SuggestionHost mSuggestionHost;
     private ImageFetcher mImageFetcher;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
-    private boolean mEnableNewAnswerLayout;
 
     /**
      * @param context An Android context.
@@ -76,22 +68,16 @@
     }
 
     @Override
-    public void onNativeInitialized() {
-        // Experiment: controls presence of certain answer icon types.
-        mEnableNewAnswerLayout =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT);
-    }
+    public void onNativeInitialized() {}
 
     @Override
     public int getViewTypeId() {
-        return mEnableNewAnswerLayout ? OmniboxSuggestionUiType.ANSWER_SUGGESTION
-                                      : OmniboxSuggestionUiType.DEFAULT;
+        return OmniboxSuggestionUiType.ANSWER_SUGGESTION;
     }
 
     @Override
     public PropertyModel createModelForSuggestion(OmniboxSuggestion suggestion) {
-        return mEnableNewAnswerLayout ? new PropertyModel(AnswerSuggestionViewProperties.ALL_KEYS)
-                                      : new PropertyModel(SuggestionViewProperties.ALL_KEYS);
+        return new PropertyModel(AnswerSuggestionViewProperties.ALL_KEYS);
     }
 
     @Override
@@ -100,11 +86,7 @@
         SuggestionViewDelegate delegate =
                 mSuggestionHost.createSuggestionViewDelegate(suggestion, position);
 
-        if (mEnableNewAnswerLayout) {
-            setStateForNewSuggestion(model, suggestion, delegate);
-        } else {
-            setStateForClassicSuggestion(model, suggestion, delegate);
-        }
+        setStateForNewSuggestion(model, suggestion, delegate);
     }
 
     @Override
@@ -169,12 +151,7 @@
                     for (int i = 0; i < currentModels.size(); i++) {
                         PropertyModel currentModel = currentModels.get(i);
                         if (!mSuggestionHost.isActiveModel(currentModel)) continue;
-
-                        if (mEnableNewAnswerLayout) {
-                            model.set(AnswerSuggestionViewProperties.ANSWER_IMAGE, bitmap);
-                        } else {
-                            model.set(SuggestionViewProperties.ANSWER_IMAGE, bitmap);
-                        }
+                        model.set(AnswerSuggestionViewProperties.ANSWER_IMAGE, bitmap);
                         didUpdate = true;
                     }
                     if (didUpdate) mSuggestionHost.notifyPropertyModelsChanged();
@@ -182,43 +159,6 @@
     }
 
     /**
-     * Sets both lines of the Omnibox suggestion in a basic Suggestion result.
-     */
-    private void setStateForClassicSuggestion(
-            PropertyModel model, OmniboxSuggestion suggestion, SuggestionViewDelegate delegate) {
-        AnswerText[] details = AnswerTextClassic.from(mContext, suggestion);
-
-        SuggestionAnswer answer = suggestion.getAnswer();
-        if (answer != null) {
-            model.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, answer.getSecondLine().hasImage());
-        }
-
-        model.set(SuggestionViewProperties.IS_ANSWER, true);
-        model.set(SuggestionViewProperties.DELEGATE, delegate);
-
-        model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_SP, details[0].mHeightSp));
-        model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
-                new SuggestionTextContainer(details[0].mText));
-        model.set(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES, details[0].mMaxLines);
-        model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT_DIRECTION, View.TEXT_DIRECTION_INHERIT);
-
-        if (details[1] != null) {
-            model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
-                    Pair.create(TypedValue.COMPLEX_UNIT_SP, details[1].mHeightSp));
-            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
-                    new SuggestionTextContainer(details[1].mText));
-            model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, details[1].mMaxLines);
-            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION,
-                    View.TEXT_DIRECTION_INHERIT);
-        }
-
-        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.MAGNIFIER);
-
-        model.set(SuggestionViewProperties.REFINABLE, true);
-    }
-
-    /**
      * Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
      */
     private void setStateForNewSuggestion(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextClassic.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextClassic.java
deleted file mode 100644
index 075ede0..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextClassic.java
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.omnibox.suggestions.answer;
-
-import android.content.Context;
-import android.graphics.Paint;
-import android.support.annotation.StyleRes;
-import android.text.TextPaint;
-import android.text.style.MetricAffectingSpan;
-import android.text.style.TextAppearanceSpan;
-
-import org.chromium.base.Log;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
-import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
-import org.chromium.components.omnibox.AnswerTextType;
-import org.chromium.components.omnibox.SuggestionAnswer;
-
-/**
- * AnswerTextNewLayout builds Omnibox styled Answer suggestion texts for classic answer layouts.
- */
-class AnswerTextClassic extends AnswerText {
-    private static final String TAG = "AnswerTextClassic";
-
-    /**
-     * Convert SuggestionAnswer to array of elements that directly translate to user-presented
-     * content.
-     *
-     * @param context Current context.
-     * @param suggestion Suggestion to be converted.
-     * @return array of AnswerText elements to use to construct suggestion item.
-     */
-    static AnswerText[] from(Context context, OmniboxSuggestion suggestion) {
-        AnswerText[] result = new AnswerText[2];
-        SuggestionAnswer answer = suggestion.getAnswer();
-        if (answer == null) {
-            // As an exception, we handle calculation suggestions, too, considering them an Answer,
-            // even if these are not one.
-            assert suggestion.getType() == OmniboxSuggestionType.CALCULATOR;
-            result[0] = new AnswerTextClassic(context, suggestion.getDisplayText());
-            result[1] = null;
-        } else {
-            result[0] = new AnswerTextClassic(context, answer.getFirstLine());
-            result[1] = new AnswerTextClassic(context, answer.getSecondLine());
-        }
-
-        // Trim number of presented query lines.
-        result[0].mMaxLines = 1;
-
-        return result;
-    }
-
-    /**
-     * Create new instance of AnswerTextClassic for answer suggestions.
-     *
-     * @param context Current context.
-     * @param line Suggestion line that will be converted to Answer Text.
-     */
-    AnswerTextClassic(Context context, SuggestionAnswer.ImageLine line) {
-        super(context);
-        build(line);
-    }
-
-    /**
-     * Create new instance of AnswerTextClassic for non-answer suggestions.
-     *
-     * @param context Current context.
-     * @param text Suggestion text.
-     */
-    AnswerTextClassic(Context context, String text) {
-        super(context);
-        appendAndStyleText(text, getAppearanceForText(AnswerTextType.SUGGESTION));
-    }
-
-    /**
-     * Return the TextAppearanceSpan array specifying text decorations for a given field type.
-     *
-     * @param type The answer type as specified at http://goto.google.com/ais_api.
-     * @return TextAppearanceSpan array specifying styles to be used to present text field.
-     */
-    @Override
-    protected MetricAffectingSpan[] getAppearanceForText(@AnswerTextType int type) {
-        @StyleRes
-        int res = 0;
-        switch (type) {
-            case AnswerTextType.TOP_ALIGNED:
-                TextAppearanceSpan style = new TextAppearanceSpan(
-                        mContext, R.style.TextAppearance_OmniboxAnswerTextSmall);
-                return new MetricAffectingSpan[] {style,
-                        new TopAlignedSpan(style.getTextSize(), (int) (mHeightSp * mDensity))};
-
-            case AnswerTextType.DESCRIPTION_NEGATIVE:
-                res = R.style.TextAppearance_OmniboxAnswerDescriptionNegative;
-                break;
-
-            case AnswerTextType.DESCRIPTION_POSITIVE:
-                res = R.style.TextAppearance_OmniboxAnswerDescriptionPositive;
-                break;
-
-            case AnswerTextType.SUGGESTION:
-                res = R.style.TextAppearance_BlackTitle1;
-                break;
-
-            case AnswerTextType.PERSONALIZED_SUGGESTION:
-                res = R.style.TextAppearance_BlackTitle1;
-                break;
-
-            case AnswerTextType.ANSWER_TEXT_MEDIUM:
-                res = R.style.TextAppearance_OmniboxAnswerTextMedium;
-                break;
-
-            case AnswerTextType.ANSWER_TEXT_LARGE:
-                res = R.style.TextAppearance_OmniboxAnswerTextLarge;
-                break;
-
-            case AnswerTextType.SUGGESTION_SECONDARY_TEXT_SMALL:
-                res = R.style.TextAppearance_BlackCaption;
-                break;
-
-            case AnswerTextType.SUGGESTION_SECONDARY_TEXT_MEDIUM:
-                res = R.style.TextAppearance_BlackHint2;
-                break;
-
-            default:
-                Log.w(TAG, "Unknown answer type: " + type);
-                res = R.style.TextAppearance_BlackHint2;
-                break;
-        }
-
-        return new TextAppearanceSpan[] {new TextAppearanceSpan(mContext, res)};
-    }
-
-    /**
-     * Aligns the top of the spanned text with the top of some other specified text height. This is
-     * done by calculating the ascent of both text heights and shifting the baseline of the spanned
-     * text by the difference.  As a result, "top aligned" means the top of the ascents are
-     * aligned, which looks as expected in most cases (some glyphs in some fonts are drawn above
-     * the top of the ascent).
-     */
-    private static class TopAlignedSpan extends MetricAffectingSpan {
-        private final int mTextHeightPx;
-        private final int mMaxTextHeightPx;
-
-        private Integer mBaselineShift;
-
-        /**
-         * Constructor for TopAlignedSpan.
-         *
-         * @param textHeightPx The total height in pixels of the text covered by this span.
-         * @param maxTextHeightPx The total height in pixels of the text we wish to top-align with.
-         */
-        public TopAlignedSpan(int textHeightPx, int maxTextHeightPx) {
-            mTextHeightPx = textHeightPx;
-            mMaxTextHeightPx = maxTextHeightPx;
-        }
-
-        @Override
-        public void updateDrawState(TextPaint tp) {
-            initBaselineShift(tp);
-            tp.baselineShift += mBaselineShift;
-        }
-
-        @Override
-        public void updateMeasureState(TextPaint tp) {
-            initBaselineShift(tp);
-            tp.baselineShift += mBaselineShift;
-        }
-
-        private void initBaselineShift(TextPaint tp) {
-            if (mBaselineShift != null) return;
-            Paint.FontMetrics metrics = tp.getFontMetrics();
-            float ascentProportion = metrics.ascent / (metrics.top - metrics.bottom);
-
-            int textAscentPx = (int) (mTextHeightPx * ascentProportion);
-            int maxTextAscentPx = (int) (mMaxTextHeightPx * ascentProportion);
-
-            mBaselineShift = -(maxTextAscentPx - textAscentPx); // Up is -y.
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index 91b0f0d..b660c44 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -41,7 +41,6 @@
     private final SuggestionHost mSuggestionHost;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
     private LargeIconBridge mLargeIconBridge;
-    private boolean mEnableNewAnswerLayout;
     private boolean mEnableSuggestionFavicons;
     private final int mDesiredFaviconWidthPx;
 
@@ -105,8 +104,6 @@
     @Override
     public void onNativeInitialized() {
         // Experiment: controls presence of certain answer icon types.
-        mEnableNewAnswerLayout =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT);
         mEnableSuggestionFavicons =
                 ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS);
     }
@@ -148,10 +145,6 @@
                     return mEnableSuggestionFavicons ? SuggestionIcon.MAGNIFIER
                                                      : SuggestionIcon.HISTORY;
 
-                case OmniboxSuggestionType.CALCULATOR:
-                    return mEnableNewAnswerLayout ? SuggestionIcon.CALCULATOR
-                                                  : SuggestionIcon.MAGNIFIER;
-
                 default:
                     return SuggestionIcon.MAGNIFIER;
             }
@@ -192,14 +185,6 @@
                                 ? R.color.default_text_color_dark
                                 : R.color.default_text_color_light);
                 textLine2Direction = View.TEXT_DIRECTION_INHERIT;
-            } else if (mEnableNewAnswerLayout
-                    && suggestionType == OmniboxSuggestionType.CALCULATOR) {
-                textLine2 = SpannableString.valueOf(
-                        mUrlBarEditingTextProvider.getTextWithAutocomplete());
-
-                textLine2Color = ApiCompatibilityUtils.getColor(
-                        mContext.getResources(), R.color.answers_answer_text);
-                textLine2Direction = View.TEXT_DIRECTION_INHERIT;
             } else {
                 textLine2 = null;
             }
@@ -303,16 +288,6 @@
                         new OmniboxSuggestion.MatchClassification(
                                 0, MatchClassificationStyle.NONE));
             }
-        } else if (mEnableNewAnswerLayout
-                && suggestion.getType() == OmniboxSuggestionType.CALCULATOR) {
-            // Trim preceding equal sign since we're going to present an icon instead.
-            // This is probably best placed in search_suggestion_parser.cc file, but at this point
-            // this would affect other devices that still want to present the sign (eg. iOS) so
-            // until these devices adopt the new entities we need to manage this here.
-            if (suggestedQuery.subSequence(0, 2).equals("= ")) {
-                suggestedQuery = suggestedQuery.substring(2);
-            }
-            shouldHighlight = false;
         }
 
         Spannable str = SpannableString.valueOf(suggestedQuery);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java
deleted file mode 100644
index 67fadc1..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java
+++ /dev/null
@@ -1,56 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-
-import org.chromium.chrome.R;
-
-/**
- * A {@link Preference} that provides button functionality.
- *
- * Preference.getOnPreferenceClickListener().onPreferenceClick() is called when the button is
- * clicked.
- */
-public class ButtonPreference extends Preference {
-
-    /**
-     * Constructor for inflating from XML
-     */
-    public ButtonPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutResource(R.layout.button_preference_layout);
-        setWidgetLayoutResource(R.layout.button_preference_button);
-        setSelectable(true);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        Button button = (Button) view.findViewById(R.id.button_preference);
-        button.setText(this.getTitle());
-        button.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (getOnPreferenceClickListener() != null) {
-                    getOnPreferenceClickListener().onPreferenceClick(ButtonPreference.this);
-                }
-            }
-        });
-
-        View viewFrame = view.findViewById(android.R.id.widget_frame);
-        viewFrame.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // This is intentionally left blank to prevent triggering an event after tapping
-                // any part of the view that is not the button.
-            }
-        });
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java
index 80bbabc7..e0de80c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java
@@ -17,9 +17,6 @@
  *
  * Preference.getOnPreferenceClickListener().onPreferenceClick() is called when the button is
  * clicked.
- *
- * TODO(crbug.com/967022): Remove {@link ButtonPreference} when Preference Support Library
- * migration is complete in favor of this class.
  */
 public class ButtonPreferenceCompat extends Preference {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java
deleted file mode 100644
index 7b9e1151..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.preference.CheckBoxPreference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Contains the basic functionality that should be shared by all CheckBoxPreference in Chrome.
- */
-public class ChromeBaseCheckBoxPreference extends CheckBoxPreference {
-
-    private ManagedPreferenceDelegate mManagedPrefDelegate;
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public ChromeBaseCheckBoxPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    /**
-     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
-     */
-    public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
-        mManagedPrefDelegate = delegate;
-        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        ((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
-        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, view);
-    }
-
-    @Override
-    protected void onClick() {
-        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
-        super.onClick();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreferenceCompat.java
index d371c4b..485f9fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreferenceCompat.java
@@ -12,11 +12,6 @@
 
 /**
  * Contains the basic functionality that should be shared by all CheckBoxPreference in Chrome.
- *
- * TODO(crbug.com/967022): This class is a duplicate of {@link ChromeBaseCheckBoxPreference} that
- * extends the Preference Support Library instead of the Framework Preference classes. {@link
- * ChromeBaseCheckBoxPreference} will be removed in favor of this class once migration to the
- * Preference Support library is complete.
  */
 public class ChromeBaseCheckBoxPreferenceCompat extends CheckBoxPreference {
     private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java
deleted file mode 100644
index f9847237..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java
+++ /dev/null
@@ -1,47 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.preference.ListPreference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Contains the basic functionality that should be shared by all ListPreference in Chrome.
- */
-public class ChromeBaseListPreference extends ListPreference {
-
-    private ManagedPreferenceDelegate mManagedPrefDelegate;
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public ChromeBaseListPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    /**
-     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
-     */
-    public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
-        mManagedPrefDelegate = delegate;
-        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        ((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
-        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, view);
-    }
-
-    @Override
-    protected void onClick() {
-        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
-        super.onClick();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java
index d55e4d8d..f031e5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java
@@ -11,9 +11,6 @@
 
 /**
  * Contains the basic functionality that should be shared by all ListPreference in Chrome.
- *
- * TODO(crbug.com/967022): Remove {@link ChromeBaseListPreference} when Preference Support Library
- * migration is complete in favor of this class.
  */
 public class ChromeBaseListPreferenceCompat extends ListPreference {
     private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java
deleted file mode 100644
index f0a8956..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java
+++ /dev/null
@@ -1,76 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-
-/**
- * A preference that supports some Chrome-specific customizations:
- *
- * 1. This preference supports being managed. If this preference is managed (as determined by its
- *    ManagedPreferenceDelegate), it updates its appearance and behavior appropriately: shows an
- *    enterprise icon, disables clicks, etc.
- *
- * 2. This preference can have a multiline title.
- * 3. This preference can set an icon color in XML through app:iconTint. Note that if a
- *    ColorStateList is set, only the default color will be used.
- */
-public class ChromeBasePreference extends Preference {
-    private ColorStateList mIconTint;
-    private ManagedPreferenceDelegate mManagedPrefDelegate;
-
-    /**
-     * Constructor for use in Java.
-     */
-    public ChromeBasePreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public ChromeBasePreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChromeBasePreference);
-        mIconTint = a.getColorStateList(R.styleable.ChromeBasePreference_iconTint);
-        a.recycle();
-    }
-
-    /**
-     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
-     */
-    public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
-        mManagedPrefDelegate = delegate;
-        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        ((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
-        Drawable icon = getIcon();
-        if (icon != null && mIconTint != null) {
-            icon.setColorFilter(mIconTint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
-        }
-        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, view);
-    }
-
-    @Override
-    protected void onClick() {
-        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
-        super.onClick();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java
index b2c3029..538d9f54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java
@@ -26,11 +26,6 @@
  * 2. This preference can have a multiline title.
  * 3. This preference can set an icon color in XML through app:iconTint. Note that if a
  *    ColorStateList is set, only the default color will be used.
- *
- * TODO(crbug.com/967022): This class is analogous to {@link ChromeBasePreference}, but extends the
- * Preference Support Library rather than the deprecated Framework preferences. Once all {@link
- * ChromeBasePreference}s have been migrated to the Support Library, {@link ChromeBasePreference}
- * will be removed in favor of {@link ChromeBasePreferenceCompat}.
  */
 public class ChromeBasePreferenceCompat extends Preference {
     private ColorStateList mIconTint;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java
deleted file mode 100644
index a955013..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java
+++ /dev/null
@@ -1,133 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.preference.Preference;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-
-/**
- * A preference that supports some Chrome-specific customizations:
- *
- * 1. This preference supports being managed. If this preference is managed (as determined by its
- *    ManagedPreferenceDelegate), it updates its appearance and behavior appropriately: shows an
- *    enterprise icon in its widget ImageView, disables clicks, etc.
- * 2. This preference can have a multiline title.
- * 3. This preference can have an onClick listener set for its ImageView widget.
- *
- * The preference includes the preference_chrome_image_view widget layout to provide these
- * customizations, however a custom widget may also be included as long as there is an ImageView
- * with the image_view_widget ID.
- */
-public class ChromeImageViewPreference extends Preference {
-    @Nullable
-    private ManagedPreferenceDelegate mManagedPrefDelegate;
-
-    // The onClick listener to handle click events for the ImageView widget.
-    @Nullable
-    private View.OnClickListener mListener;
-    // The image resource ID to use for the ImageView widget source.
-    @DrawableRes
-    private int mImageRes;
-    // The string resource ID to use for the ImageView widget content description.
-    @StringRes
-    private int mContentDescriptionRes;
-    // Whether the ImageView should be enabled.
-    private boolean mImageViewEnabled = true;
-
-    /**
-     * Constructor for use in Java.
-     */
-    public ChromeImageViewPreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public ChromeImageViewPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        setWidgetLayoutResource(R.layout.preference_chrome_image_view);
-    }
-
-    /**
-     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
-     */
-    public void setManagedPreferenceDelegate(@Nullable ManagedPreferenceDelegate delegate) {
-        mManagedPrefDelegate = delegate;
-        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        ((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
-
-        ImageView button = view.findViewById(R.id.image_view_widget);
-
-        if (mImageRes != 0) {
-            Drawable buttonImg = PreferenceUtils.getTintedIcon(view.getContext(), mImageRes);
-
-            button.setImageDrawable(buttonImg);
-            button.setBackgroundColor(Color.TRANSPARENT);
-            button.setVisibility(View.VISIBLE);
-            button.setEnabled(mImageViewEnabled);
-            if (mImageViewEnabled) button.setOnClickListener(mListener);
-
-            if (mContentDescriptionRes != 0) {
-                button.setContentDescription(view.getResources().getString(mContentDescriptionRes));
-            }
-        }
-
-        ManagedPreferencesUtils.onBindViewToImageViewPreference(mManagedPrefDelegate, this, view);
-    }
-
-    @Override
-    protected void onClick() {
-        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
-        super.onClick();
-    }
-
-    /**
-     * Sets the Drawable resource ID, the String resource ID, and the OnClickListener for the
-     * ImageView widget's source, content description, and onClick, respectively.
-     */
-    public void setImageView(@DrawableRes int imageRes, @StringRes int contentDescriptionRes,
-            @Nullable View.OnClickListener listener) {
-        mImageRes = imageRes;
-        mContentDescriptionRes = contentDescriptionRes;
-        mListener = listener;
-        notifyChanged();
-    }
-
-    /**
-     * Enables/Disables the ImageView, allowing for clicks to pass through (when disabled).
-     */
-    public void setImageViewEnabled(boolean enabled) {
-        mImageViewEnabled = enabled;
-    }
-
-    /**
-     * If a {@link ManagedPreferenceDelegate} has been set, check if this preference is managed.
-     * @return True if the preference is managed.
-     */
-    public boolean isManaged() {
-        if (mManagedPrefDelegate == null) return false;
-
-        return mManagedPrefDelegate.isPreferenceControlledByPolicy(this)
-                || mManagedPrefDelegate.isPreferenceControlledByCustodian(this);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreferenceCompat.java
index c9b9444..0cca33f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreferenceCompat.java
@@ -19,10 +19,6 @@
 import org.chromium.chrome.R;
 
 /**
- * TODO(crbug.com/967022): This class is a duplicate of {@link ChromeImageViewPreference}, but with
- * Support Library classes replacing deprecated Framework classes. When Preference Support Library
- * migration is complete remove {@link ChromeImageViewPreference} in favor of this class.
- *
  * A preference that supports some Chrome-specific customizations:
  *
  * 1. This preference supports being managed. If this preference is managed (as determined by its
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java
deleted file mode 100644
index c2cf7d9a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java
+++ /dev/null
@@ -1,109 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.preference.SwitchPreference;
-import android.support.v7.widget.SwitchCompat;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.ui.HorizontalListDividerDrawable;
-
-/**
- * A super-powered SwitchPreference designed especially for Chrome. Special features:
- *  - Supports managed preferences
- *  - Displays a material-styled switch, even on pre-L devices
- */
-public class ChromeSwitchPreference extends SwitchPreference {
-
-    private ManagedPreferenceDelegate mManagedPrefDelegate;
-
-    private boolean mDontUseSummaryAsTitle;
-    private boolean mDrawDivider;
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public ChromeSwitchPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setWidgetLayoutResource(R.layout.preference_switch);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            // Fix animations. Background: setWidgetLayout resource call above disables view
-            // recycling, thus breaking SwitchCompat animations. Views recycling is safe in this
-            // case, as ChromeSwitchPreference doesn't change view types on the fly.
-            setRecycleEnabled(true);
-        }
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChromeSwitchPreference);
-        mDontUseSummaryAsTitle =
-                a.getBoolean(R.styleable.ChromeSwitchPreference_dontUseSummaryAsTitle, false);
-        mDrawDivider = a.getBoolean(R.styleable.ChromeSwitchPreference_drawDivider, false);
-        a.recycle();
-    }
-
-    /**
-     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
-     */
-    public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
-        mManagedPrefDelegate = delegate;
-        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
-    }
-
-    /**
-     * Sets whether a horizontal divider line should be drawn at the bottom of this preference.
-     */
-    public void setDrawDivider(boolean drawDivider) {
-        if (mDrawDivider != drawDivider) {
-            mDrawDivider = drawDivider;
-            notifyChanged();
-        }
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-
-        if (mDrawDivider) {
-            int left = view.getPaddingLeft();
-            int right = view.getPaddingRight();
-            int top = view.getPaddingTop();
-            int bottom = view.getPaddingBottom();
-            view.setBackground(HorizontalListDividerDrawable.create(getContext()));
-            view.setPadding(left, top, right, bottom);
-        }
-
-        SwitchCompat switchView = (SwitchCompat) view.findViewById(R.id.switch_widget);
-        // On BLU Life Play devices SwitchPreference.setWidgetLayoutResource() does nothing. As a
-        // result, the user will see a non-material Switch and switchView will be null, hence the
-        // null check below. http://crbug.com/451447
-        if (switchView != null) {
-            switchView.setChecked(isChecked());
-        }
-
-        TextView title = (TextView) view.findViewById(android.R.id.title);
-        title.setSingleLine(false);
-        if (!mDontUseSummaryAsTitle && TextUtils.isEmpty(getTitle())) {
-            TextView summary = (TextView) view.findViewById(android.R.id.summary);
-            title.setText(summary.getText());
-            title.setVisibility(View.VISIBLE);
-            summary.setVisibility(View.GONE);
-        }
-
-        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, view);
-    }
-
-    @Override
-    protected void onClick() {
-        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
-        super.onClick();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreferenceCompat.java
index 44f8aba..234387a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreferenceCompat.java
@@ -14,9 +14,6 @@
 
 /**
  * A Chrome switch preference that supports managed preferences.
- *
- * TODO(crbug.com/967022): Remove {@link ChromeSwitchPreference} when Preference Support Library
- * migration is complete in favor of this class.
  */
 public class ChromeSwitchPreferenceCompat extends SwitchPreferenceCompat {
     private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepageEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepageEditor.java
index b0d94678..fb130ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepageEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepageEditor.java
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.browser.preferences;
 
-import android.app.Fragment;
 import android.os.Bundle;
+import android.support.v4.app.Fragment;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreference.java
deleted file mode 100644
index c40e6367..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreference.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.TextView;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.help.HelpAndFeedback;
-import org.chromium.chrome.browser.profiles.Profile;
-
-/**
- * A preference that opens a HelpAndFeedback activity to learn more about the specified context.
- */
-public class LearnMorePreference extends Preference {
-
-    private final int mHelpContext;
-    private final int mColor;
-
-    public LearnMorePreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.LearnMorePreference, 0, 0);
-        mHelpContext = a.getResourceId(R.styleable.LearnMorePreference_helpContext, 0);
-        mColor = ApiCompatibilityUtils.getColor(
-                context.getResources(), R.color.default_text_color_link);
-        a.recycle();
-        setTitle(R.string.learn_more);
-    }
-
-    @Override
-    protected void onClick() {
-        HelpAndFeedback.getInstance(getContext())
-                .show((Activity) getContext(), getContext().getString(mHelpContext),
-                        Profile.getLastUsedProfile(), null);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        TextView titleView = (TextView) view.findViewById(android.R.id.title);
-        titleView.setSingleLine(false);
-
-        setSelectable(false);
-
-        titleView.setClickable(true);
-        titleView.setTextColor(mColor);
-        titleView.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                LearnMorePreference.this.onClick();
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreferenceCompat.java
index c670c05..af2e34a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LearnMorePreferenceCompat.java
@@ -20,11 +20,6 @@
 
 /**
  * A preference that opens a HelpAndFeedback activity to learn more about the specified context.
- *
- * TODO(crbug.com/967022): This class is mostly a duplicate of {@link LearnMorePreference}, but is
- * implemented for Support Library preferences rather than the deprecated Framework preferences.
- * Once all {@link LearnMorePreference}s have been migrated to the Support Library, {@link
- * LearnMorePreference} will be removed in favor of this class.
  */
 public class LearnMorePreferenceCompat extends Preference {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java
deleted file mode 100644
index 0b6f31837..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java
+++ /dev/null
@@ -1,58 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.preference.Preference;
-
-/**
- * A delegate that determines whether a Preference is managed by enterprise policy. This is used
- * in various Preference subclasses (e.g. ChromeSwitchPreference) to determine whether to show
- * an enterprise icon next to the Preference and whether to disable clicks on the Preference.
- *
- * An implementation of this delegate should override isPreferenceControlledByPolicy() and,
- * optionally, isPreferenceClickDisabledByPolicy(). Example:
- *
- *   class RocketManagedPreferenceDelegate extends ManagedPreferenceDelegate {
- *       @Override
- *       public boolean isPreferenceControlledByPolicy(Preference preference) {
- *           if ("enable_rockets".equals(preference.getKey())) {
- *               return RocketUtils.isEnableRocketsManaged();
- *           }
- *           return false;
- *       }
- *   }
- *
- *   ChromeSwitchPreference enableRocketsPref = ...;
- *   enableRocketsPref.setManagedPreferenceDelegate(new RocketManagedPreferenceDelegate());
- */
-public interface ManagedPreferenceDelegate {
-    /**
-     * Returns whether the given Preference is controlled by an enterprise policy.
-     * @param preference the {@link Preference} under consideration.
-     * @return whether the given Preference is controlled by an enterprise policy.
-     */
-    boolean isPreferenceControlledByPolicy(Preference preference);
-
-    /**
-     * Returns whether the given Preference is controlled by the supervised user's custodian.
-     * @param preference the {@link Preference} under consideration.
-     * @return whether the given Preference is controlled by the supervised user's custodian.
-     */
-    default boolean isPreferenceControlledByCustodian(Preference preference) {
-        return false;
-    }
-
-    /**
-     * Returns whether clicking on the given Preference is disabled due to a policy. The default
-     * implementation just returns whether the preference is not modifiable by the user.
-     * However, some preferences that are controlled by policy may still be clicked to show an
-     * informational subscreen, in which case this method needs a custom implementation.
-     */
-    // TODO(bauerb): Rename to isPreferenceClickDisabled.
-    default boolean isPreferenceClickDisabledByPolicy(Preference preference) {
-        return isPreferenceControlledByPolicy(preference)
-                || isPreferenceControlledByCustodian(preference);
-    }
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java
index 193a856a..4810c016 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java
@@ -26,11 +26,6 @@
  *
  *   ChromeSwitchPreference enableRocketsPref = ...;
  *   enableRocketsPref.setManagedPreferenceDelegate(new RocketManagedPreferenceDelegate());
- *
- * TODO(crbug.com/967022): This class is analogous to {@link ManagedPreferenceDelegate}, but is
- * implemented for Support Library preferences rather than the deprecated Framework preferences.
- * Once all managed preferences have been migrated to the Support Library, {@link
- * ManagedPreferenceDelegate} will be removed in favor of this class.
  */
 public interface ManagedPreferenceDelegateCompat {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
index 11dcbf0..3e3802d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java
@@ -6,9 +6,9 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.preference.Preference;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
+import android.support.v7.preference.Preference;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.ImageView;
@@ -81,7 +81,7 @@
      *      a custodian.
      */
     public static Drawable getManagedIconDrawable(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference) {
+            @Nullable ManagedPreferenceDelegateCompat delegate, Preference preference) {
         if (delegate == null) return preference.getIcon();
 
         if (delegate.isPreferenceControlledByPolicy(preference)) {
@@ -96,67 +96,6 @@
     }
 
     /**
-     * TODO(crbug.com/967022): This method is a duplicate of {@link
-     * #getManagedIconDrawable(ManagedPreferenceDelegate, Preference)}, but with Support Library
-     * classes replacing deprecated Framework classes. When Preference Support Library migration is
-     * complete remove {@link #getManagedIconDrawable(ManagedPreferenceDelegate, Preference)} in
-     * favor of this method.
-     *
-     * @return The appropriate Drawable based on whether the preference is controlled by a policy or
-     *      a custodian.
-     */
-    public static Drawable getManagedIconDrawable(
-            @Nullable ManagedPreferenceDelegateCompat delegate,
-            android.support.v7.preference.Preference preference) {
-        if (delegate == null) return preference.getIcon();
-
-        if (delegate.isPreferenceControlledByPolicy(preference)) {
-            return PreferenceUtils.getTintedIcon(
-                    preference.getContext(), getManagedByEnterpriseIconId());
-        } else if (delegate.isPreferenceControlledByCustodian(preference)) {
-            return PreferenceUtils.getTintedIcon(
-                    preference.getContext(), getManagedByCustodianIconId());
-        }
-
-        return preference.getIcon();
-    }
-
-    /**
-     * Initializes the Preference based on the state of any policies that may affect it,
-     * e.g. by showing a managed icon or disabling clicks on the preference. If |preference| is an
-     * instance of ChromeImageViewPreference, the icon is not set since the ImageView widget will
-     * display the managed icons.
-     *
-     * This should be called once, before the preference is displayed.
-     *
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library in favor of {@link #initPreference(ManagedPreferenceDelegateCompat,
-     * android.support.v7.preference.Preference)}.
-     *
-     * @param delegate The delegate that controls whether the preference is managed. May be null,
-     *         then this method does nothing.
-     * @param preference The Preference that is being initialized
-     */
-    public static void initPreference(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference) {
-        if (delegate == null) return;
-
-        if (!(preference instanceof ChromeImageViewPreference)) {
-            preference.setIcon(getManagedIconDrawable(delegate, preference));
-        }
-
-        if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
-            // Disable the views and prevent the Preference from mucking with the enabled state.
-            preference.setShouldDisableView(false);
-
-            // Prevent default click behavior.
-            preference.setFragment(null);
-            preference.setIntent(null);
-            preference.setOnPreferenceClickListener(null);
-        }
-    }
-
-    /**
      * Initializes the Preference based on the state of any policies that may affect it,
      * e.g. by showing a managed icon or disabling clicks on the preference. If |preference| is an
      * instance of ChromeImageViewPreference, the icon is not set since the ImageView widget will
@@ -168,8 +107,8 @@
      *         then this method does nothing.
      * @param preference The Preference that is being initialized
      */
-    public static void initPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
-            android.support.v7.preference.Preference preference) {
+    public static void initPreference(
+            @Nullable ManagedPreferenceDelegateCompat delegate, Preference preference) {
         if (delegate == null) return;
 
         if (!(preference instanceof ChromeImageViewPreferenceCompat)) {
@@ -195,17 +134,13 @@
      *
      * This should be called from the Preference's onBindView() method.
      *
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library in favor of {@link #onBindViewToPreference(ManagedPreferenceDelegateCompat,
-     * android.support.v7.preference.Preference, View)}.
-     *
      * @param delegate The delegate that controls whether the preference is managed. May be null,
      *         then this method does nothing.
      * @param preference The Preference that owns the view
      * @param view The View that was bound to the Preference
      */
     public static void onBindViewToPreference(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference, View view) {
+            @Nullable ManagedPreferenceDelegateCompat delegate, Preference preference, View view) {
         if (delegate == null) return;
 
         if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
@@ -224,78 +159,6 @@
     }
 
     /**
-     * Disables the Preference's views if the preference is not clickable.
-     *
-     * Note: this disables the View instead of disabling the Preference, so that the Preference
-     * still receives click events, which will trigger a "Managed by your administrator" toast.
-     *
-     * This should be called from the Preference's onBindView() method.
-     *
-     * @param delegate The delegate that controls whether the preference is managed. May be null,
-     *         then this method does nothing.
-     * @param preference The Preference that owns the view
-     * @param view The View that was bound to the Preference
-     */
-    public static void onBindViewToPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
-            android.support.v7.preference.Preference preference, View view) {
-        if (delegate == null) return;
-
-        if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
-            ViewUtils.setEnabledRecursive(view, false);
-        }
-
-        // Append managed information to summary if necessary.
-        TextView summaryView = view.findViewById(android.R.id.summary);
-        CharSequence summary =
-                ManagedPreferencesUtils.getSummaryWithManagedInfo(delegate, preference,
-                        summaryView.getVisibility() == View.VISIBLE ? summaryView.getText() : null);
-        if (!TextUtils.isEmpty(summary)) {
-            summaryView.setText(summary);
-            summaryView.setVisibility(View.VISIBLE);
-        }
-    }
-
-    /**
-     * Calls onBindViewToPreference() above. Then, if the ChromeImageViewPreference is managed, the
-     * widget ImageView is set to the appropriate managed icon, and its onClick listener is set to
-     * show the appropriate managed message toast.
-     *
-     * This should be called from the Preference's onBindView() method.
-     *
-     * @param delegate The delegate that controls whether the preference is managed. May be null,
-     *                 then this method does nothing.
-     * @param preference The ChromeImageViewPreference that owns the view.
-     * @param view The View that was bound to the ChromeImageViewPreference.
-     */
-    public static void onBindViewToImageViewPreference(@Nullable ManagedPreferenceDelegate delegate,
-            ChromeImageViewPreference preference, View view) {
-        if (delegate == null) return;
-
-        onBindViewToPreference(delegate, preference, view);
-
-        if (!delegate.isPreferenceControlledByPolicy(preference)
-                && !delegate.isPreferenceControlledByCustodian(preference)) {
-            return;
-        }
-
-        ImageView button = view.findViewById(R.id.image_view_widget);
-        button.setImageDrawable(getManagedIconDrawable(delegate, preference));
-        button.setOnClickListener((View v) -> {
-            if (delegate.isPreferenceControlledByPolicy(preference)) {
-                showManagedByAdministratorToast(preference.getContext());
-            } else if (delegate.isPreferenceControlledByCustodian(preference)) {
-                showManagedByParentToast(preference.getContext());
-            }
-        });
-    }
-
-    /**
-     * TODO(crbug.com/967022): This method is a duplicate of {@link
-     * #onBindViewToPreference(ManagedPreferenceDelegate, Preference, View)}, but with Support
-     * Library classes replacing deprecated Framework classes. When Preference Support Library
-     * migration is complete remove {@link #onBindViewToPreference(ManagedPreferenceDelegate,
-     * Preference, View)} in favor of this method.
-     *
      * Calls onBindViewToPreference() above. Then, if the ChromeImageViewPreference is managed, the
      * widget ImageView is set to the appropriate managed icon, and its onClick listener is set to
      * show the appropriate managed message toast.
@@ -335,10 +198,6 @@
      *
      * This should be called from the Preference's onClick() method.
      *
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library in favor of {@link #onClickPreference(ManagedPreferenceDelegateCompat,
-     * android.support.v7.preference.Preference)}.
-     *
      * @param delegate The delegate that controls whether the preference is managed. May be null,
      *         then this method does nothing and returns false.
      * @param preference The Preference that was clicked.
@@ -346,7 +205,7 @@
      *         propagated; false otherwise.
      */
     public static boolean onClickPreference(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference) {
+            @Nullable ManagedPreferenceDelegateCompat delegate, Preference preference) {
         if (delegate == null || !delegate.isPreferenceClickDisabledByPolicy(preference)) {
             return false;
         }
@@ -364,76 +223,18 @@
     }
 
     /**
-     * Intercepts the click event if the given Preference is managed and shows a toast in that case.
-     *
-     * This should be called from the Preference's onClick() method.
-     *
-     * @param delegate The delegate that controls whether the preference is managed. May be null,
-     *         then this method does nothing and returns false.
-     * @param preference The Preference that was clicked.
-     * @return true if the click event was handled by this helper and shouldn't be further
-     *         propagated; false otherwise.
-     */
-    public static boolean onClickPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
-            android.support.v7.preference.Preference preference) {
-        if (delegate == null || !delegate.isPreferenceClickDisabledByPolicy(preference)) {
-            return false;
-        }
-
-        if (delegate.isPreferenceControlledByPolicy(preference)) {
-            showManagedByAdministratorToast(preference.getContext());
-        } else if (delegate.isPreferenceControlledByCustodian(preference)) {
-            showManagedByParentToast(preference.getContext());
-        } else {
-            // If the preference is disabled, it should be either because it's managed by enterprise
-            // policy or by the custodian.
-            assert false;
-        }
-        return true;
-    }
-
-    /**
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library in favor of {@link #getSummaryWithManagedInfo(ManagedPreferenceDelegateCompat,
-     * android.support.v7.preference.Preference, CharSequence)}.
-     *
-     * @param delegate The {@link ManagedPreferenceDelegate} that controls whether the preference is
-     *        managed.
-     * @param preference The {@link Preference} that the summary should be used for.
-     * @param summary The original summary without the managed information.
-     * @return The summary appended with information about whether the specified preference is
-     *         managed.
-     */
-    private static CharSequence getSummaryWithManagedInfo(
-            @Nullable ManagedPreferenceDelegate delegate, Preference preference,
-            @Nullable CharSequence summary) {
-        if (delegate == null) return summary;
-
-        String extraSummary = null;
-        if (delegate.isPreferenceControlledByPolicy(preference)) {
-            extraSummary = preference.getContext().getString(R.string.managed_by_your_organization);
-        } else if (delegate.isPreferenceControlledByCustodian(preference)) {
-            extraSummary = preference.getContext().getString(getManagedByParentStringRes());
-        }
-
-        if (TextUtils.isEmpty(extraSummary)) return summary;
-        if (TextUtils.isEmpty(summary)) return extraSummary;
-        return String.format(Locale.getDefault(), "%s\n%s", summary, extraSummary);
-    }
-
-    /**
      * @param delegate The {@link ManagedPreferenceDelegateCompat} that controls whether the
      *         preference is
      *        managed.
-     * @param preference The {@link android.support.v7.preference.Preference} that the summary
+     * @param preference The {@link Preference} that the summary
      *         should be used for.
      * @param summary The original summary without the managed information.
      * @return The summary appended with information about whether the specified preference is
      *         managed.
      */
     private static CharSequence getSummaryWithManagedInfo(
-            @Nullable ManagedPreferenceDelegateCompat delegate,
-            android.support.v7.preference.Preference preference, @Nullable CharSequence summary) {
+            @Nullable ManagedPreferenceDelegateCompat delegate, Preference preference,
+            @Nullable CharSequence summary) {
         if (delegate == null) return summary;
 
         String extraSummary = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferenceUtils.java
index ed45a7a..27c31e0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferenceUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferenceUtils.java
@@ -9,7 +9,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.StrictMode;
-import android.preference.PreferenceFragment;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
 import android.support.annotation.XmlRes;
@@ -32,26 +31,6 @@
      * A helper that is used to load preferences from XML resources without causing a
      * StrictModeViolation. See http://crbug.com/692125.
      *
-     * TODO(crbug.com/967022): Once all {@link PreferenceFragment}s are migrated to the Support
-     * Library {@link PreferenceFragmentCompat}s, remove this method in favor of the below method.
-     *
-     * @param preferenceFragment A Framework {@link PreferenceFragment}.
-     * @param preferencesResId   The id of the XML resource to add to the PreferenceFragment.
-     */
-    public static void addPreferencesFromResource(
-            PreferenceFragment preferenceFragment, @XmlRes int preferencesResId) {
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            preferenceFragment.addPreferencesFromResource(preferencesResId);
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-    }
-
-    /**
-     * A helper that is used to load preferences from XML resources without causing a
-     * StrictModeViolation. See http://crbug.com/692125.
-     *
      * @param preferenceFragment A Support Library {@link PreferenceFragmentCompat}.
      * @param preferencesResId   The id of the XML resource to add to the PreferenceFragment.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
index e933be13..8c8c06d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
@@ -6,7 +6,6 @@
 
 import android.Manifest;
 import android.annotation.SuppressLint;
-import android.app.Fragment;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -14,13 +13,11 @@
 import android.content.res.Resources;
 import android.graphics.BitmapFactory;
 import android.nfc.NfcAdapter;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback;
 import android.support.graphics.drawable.VectorDrawableCompat;
+import android.support.v4.app.Fragment;
+import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceFragmentCompat;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -29,7 +26,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.ListView;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.VisibleForTesting;
@@ -55,8 +51,7 @@
  *    PreferenceUtils.getShowShadowOnScrollListener(...).
  */
 public class Preferences extends ChromeBaseAppCompatActivity
-        implements OnPreferenceStartFragmentCallback,
-                   PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+        implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
     /**
      * Preference fragments may implement this interface to intercept "Back" button taps in this
      * activity.
@@ -119,21 +114,11 @@
         if (savedInstanceState == null) {
             if (initialFragment == null) initialFragment = MainPreferences.class.getName();
 
-            if (isCompat(initialFragment)) {
-                android.support.v4.app.Fragment fragment =
-                        android.support.v4.app.Fragment.instantiate(
-                                this, initialFragment, initialArguments);
-                getSupportFragmentManager()
-                        .beginTransaction()
-                        .replace(android.R.id.content, fragment)
-                        .commit();
-            } else {
-                Fragment fragment = Fragment.instantiate(this, initialFragment, initialArguments);
-                getFragmentManager()
-                        .beginTransaction()
-                        .replace(android.R.id.content, fragment)
-                        .commit();
-            }
+            Fragment fragment = Fragment.instantiate(this, initialFragment, initialArguments);
+            getSupportFragmentManager()
+                    .beginTransaction()
+                    .replace(android.R.id.content, fragment)
+                    .commit();
         }
 
         if (ApiCompatibilityUtils.checkPermission(
@@ -151,39 +136,11 @@
                 ApiCompatibilityUtils.getColor(res, R.color.default_primary_color));
     }
 
-    /**
-     * Given a Fragment class name (as a string), determine whether it is a Support Library Fragment
-     * class, as opposed to a Framework Fragment.
-     *
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library.
-     *
-     * @param fragmentClass The fully qualified class name of a Fragment.
-     * @return Whether the class represents a Support Library Fragment.
-     */
-    private boolean isCompat(String fragmentClass) {
-        try {
-            return android.support.v4.app.Fragment.class.isAssignableFrom(
-                    Class.forName(fragmentClass));
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-
     // OnPreferenceStartFragmentCallback:
 
     @Override
-    // TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-    // Library.
     public boolean onPreferenceStartFragment(
-            PreferenceFragment preferenceFragment, Preference preference) {
-        startFragment(preference.getFragment(), preference.getExtras());
-        return true;
-    }
-
-    @Override
-    public boolean onPreferenceStartFragment(
-            PreferenceFragmentCompat caller, android.support.v7.preference.Preference preference) {
+            PreferenceFragmentCompat caller, Preference preference) {
         startFragment(preference.getFragment(), preference.getExtras());
         return true;
     }
@@ -205,48 +162,7 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Fragment fragment = getMainFragment();
-        if (fragment == null) {
-            onAttachedToWindowCompat();
-            return;
-        }
-        if (fragment.getView() == null
-                || fragment.getView().findViewById(android.R.id.list) == null) {
-            return;
-        }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            if (fragment instanceof PreferenceFragment && fragment.getView() != null) {
-                // Set list view padding to 0 so dividers are the full width of the screen.
-                fragment.getView().findViewById(android.R.id.list).setPadding(0, 0, 0, 0);
-            }
-        }
-        View contentView = fragment.getActivity().findViewById(android.R.id.content);
-        if (contentView == null || !(contentView instanceof FrameLayout)) {
-            return;
-        }
-
-        View inflatedView = View.inflate(getApplicationContext(),
-                R.layout.preferences_action_bar_shadow, (ViewGroup) contentView);
-        ListView listView = fragment.getView().findViewById(android.R.id.list);
-        listView.getViewTreeObserver().addOnScrollChangedListener(
-                PreferenceUtils.getShowShadowOnScrollListener(
-                        listView, inflatedView.findViewById(R.id.shadow)));
-    }
-
-    /**
-     * This method performs similar actions to {@link #onAttachedToWindow()}, but modified for the
-     * case where the main fragment is a Support Library fragment.
-     *
-     * Differences include:
-     *   * List ID reference is R.id.list instead of android.R.id.list.
-     *   * The {@link ListView} is now a {@link RecyclerView}.
-     *
-     * TODO(crbug.com/967022): Once all fragments are migrated to the Support Library, replace
-     * {@link #onAttachedToWindow()} with this method body.
-     */
-    public void onAttachedToWindowCompat() {
-        super.onAttachedToWindow();
-        android.support.v4.app.Fragment fragment = getMainFragmentCompat();
+        Fragment fragment = getMainFragmentCompat();
         if (fragment == null || fragment.getView() == null
                 || fragment.getView().findViewById(R.id.list) == null) {
             return;
@@ -299,27 +215,11 @@
 
     /**
      * Returns the fragment showing as this activity's main content, typically a {@link
-     * PreferenceFragment}. This does not include {@link android.app.DialogFragment}s or other
-     * {@link Fragment}s shown on top of the main content.
-     *
-     * This method only returns Framework {@link Fragment}s. If the main fragment is a Support
-     * Library fragment (of type {@link android.support.v4.app.Fragment}), this method returns null.
-     *
-     * TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
-     * Library.
+     * PreferenceFragmentCompat}. This does not include dialogs or other {@link Fragment}s shown on
+     * top of the main content.
      */
     @VisibleForTesting
-    public Fragment getMainFragment() {
-        return getFragmentManager().findFragmentById(android.R.id.content);
-    }
-
-    /**
-     * This method should be called to retrieve the activity's main content if {@link
-     * #getMainFragment()} returned null, which may indicate that the main fragment is a Support
-     * Library fragment.
-     */
-    @VisibleForTesting
-    public android.support.v4.app.Fragment getMainFragmentCompat() {
+    public Fragment getMainFragmentCompat() {
         return getSupportFragmentManager().findFragmentById(android.R.id.content);
     }
 
@@ -345,9 +245,7 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        Fragment mainFragment = getMainFragment();
-        if (mainFragment != null && mainFragment.onOptionsItemSelected(item)) return true;
-        android.support.v4.app.Fragment mainFragmentCompat = getMainFragmentCompat();
+        Fragment mainFragmentCompat = getMainFragmentCompat();
         if (mainFragmentCompat != null && mainFragmentCompat.onOptionsItemSelected(item)) {
             return true;
         }
@@ -365,7 +263,7 @@
 
     @Override
     public void onBackPressed() {
-        android.support.v4.app.Fragment activeFragment = getMainFragmentCompat();
+        Fragment activeFragment = getMainFragmentCompat();
         if (!(activeFragment instanceof OnBackPressedListener)) {
             super.onBackPressed();
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
index 6d19a05..000deed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.app.Activity;
-import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -60,20 +59,6 @@
      *
      * @param context The current Activity, or an application context if no Activity is available.
      * @param fragment The fragment to show, or null to show the top-level page.
-     *
-     * TODO(crbug.com/967022): Remove this method when Preference Support Library migration is
-     * complete.
-     */
-    public static void launchSettingsPage(
-            Context context, @Nullable Class<? extends Fragment> fragment) {
-        launchSettingsPage(context, fragment, null);
-    }
-
-    /**
-     * Launches settings, either on the top-level page or on a subpage.
-     *
-     * @param context The current Activity, or an application context if no Activity is available.
-     * @param fragment The fragment to show, or null to show the top-level page.
      */
     public static void launchSettingsPageCompat(
             Context context, @Nullable Class<? extends android.support.v4.app.Fragment> fragment) {
@@ -86,23 +71,6 @@
      * @param context The current Activity, or an application context if no Activity is available.
      * @param fragment The name of the fragment to show, or null to show the top-level page.
      * @param fragmentArgs The arguments bundle to initialize the instance of subpage fragment.
-     *
-     * TODO(crbug.com/967022): Remove this method when Preference Support Library migration is
-     * complete.
-     */
-    public static void launchSettingsPage(Context context,
-            @Nullable Class<? extends Fragment> fragment, @Nullable Bundle fragmentArgs) {
-        String fragmentName = fragment != null ? fragment.getName() : null;
-        Intent intent = createIntentForSettingsPage(context, fragmentName, fragmentArgs);
-        IntentUtils.safeStartActivity(context, intent);
-    }
-
-    /**
-     * Launches settings, either on the top-level page or on a subpage.
-     *
-     * @param context The current Activity, or an application context if no Activity is available.
-     * @param fragment The name of the fragment to show, or null to show the top-level page.
-     * @param fragmentArgs The arguments bundle to initialize the instance of subpage fragment.
      */
     public static void launchSettingsPageCompat(Context context,
             @Nullable Class<? extends android.support.v4.app.Fragment> fragment,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java
deleted file mode 100644
index 5b33b6a3..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java
+++ /dev/null
@@ -1,44 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * A preference that displays informational text.
- */
-public class TextMessagePreference extends ChromeBasePreference {
-    /**
-     * Constructor for inflating from XML.
-     */
-    public TextMessagePreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setSelectable(false);
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-
-        TextView titleView = (TextView) view.findViewById(android.R.id.title);
-        if (!TextUtils.isEmpty(getTitle())) {
-            titleView.setVisibility(View.VISIBLE);
-            titleView.setSingleLine(false);
-            titleView.setMaxLines(Integer.MAX_VALUE);
-            titleView.setMovementMethod(LinkMovementMethod.getInstance());
-        } else {
-            titleView.setVisibility(View.GONE);
-        }
-
-        TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
-        // No need to manually toggle visibility for summary - it is done in super.onBindView.
-        summaryView.setMovementMethod(LinkMovementMethod.getInstance());
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java
index bf32e46..929c11d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java
@@ -14,11 +14,6 @@
 
 /**
  * A preference that displays informational text.
- *
- * TODO(crbug.com/967022): This class is analogous to {@link TextMessagePreference}, but extends
- * {@link ChromeBasePreferenceCompat} rather than {@link ChromeBasePreference}. Once all {@link
- * TextMessagePreference}-containing fragments have been migrated to the Support Library, remove
- * {@link TextMessagePreference} in favor of this class.
  */
 public class TextMessagePreferenceCompat extends ChromeBasePreferenceCompat {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
index 912f8573..9ac9392 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillEditorBase.java
@@ -5,9 +5,9 @@
 package org.chromium.chrome.browser.preferences.autofill;
 
 import android.annotation.SuppressLint;
-import android.app.Fragment;
 import android.content.Context;
 import android.os.Bundle;
+import android.support.v4.app.Fragment;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/AddLanguageFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/AddLanguageFragment.java
index e8b6a3a..ba9d8c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/AddLanguageFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/languages/AddLanguageFragment.java
@@ -5,10 +5,10 @@
 package org.chromium.chrome.browser.preferences.languages;
 
 import android.app.Activity;
-import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.v4.app.Fragment;
 import android.support.v7.widget.DividerItemDecoration;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java
index 85f172b..d9256b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java
@@ -59,17 +59,28 @@
     @VisibleForTesting
     public static final String FRAGMENT_PASSPHRASE_TYPE = "password_type";
 
-    private static final String PREF_SYNC_EVERYTHING = "sync_everything";
-    private static final String PREF_SYNC_AUTOFILL = "sync_autofill";
-    private static final String PREF_SYNC_BOOKMARKS = "sync_bookmarks";
-    private static final String PREF_SYNC_PAYMENTS_INTEGRATION = "sync_payments_integration";
-    private static final String PREF_SYNC_HISTORY = "sync_history";
-    private static final String PREF_SYNC_PASSWORDS = "sync_passwords";
-    private static final String PREF_SYNC_RECENT_TABS = "sync_recent_tabs";
-    private static final String PREF_SYNC_SETTINGS = "sync_settings";
-    private static final String PREF_GOOGLE_ACTIVITY_CONTROLS = "google_activity_controls";
-    private static final String PREF_ENCRYPTION = "encryption";
-    private static final String PREF_SYNC_MANAGE_DATA = "sync_manage_data";
+    @VisibleForTesting
+    public static final String PREF_SYNC_EVERYTHING = "sync_everything";
+    @VisibleForTesting
+    public static final String PREF_SYNC_AUTOFILL = "sync_autofill";
+    @VisibleForTesting
+    public static final String PREF_SYNC_BOOKMARKS = "sync_bookmarks";
+    @VisibleForTesting
+    public static final String PREF_SYNC_PAYMENTS_INTEGRATION = "sync_payments_integration";
+    @VisibleForTesting
+    public static final String PREF_SYNC_HISTORY = "sync_history";
+    @VisibleForTesting
+    public static final String PREF_SYNC_PASSWORDS = "sync_passwords";
+    @VisibleForTesting
+    public static final String PREF_SYNC_RECENT_TABS = "sync_recent_tabs";
+    @VisibleForTesting
+    public static final String PREF_SYNC_SETTINGS = "sync_settings";
+    @VisibleForTesting
+    public static final String PREF_GOOGLE_ACTIVITY_CONTROLS = "google_activity_controls";
+    @VisibleForTesting
+    public static final String PREF_ENCRYPTION = "encryption";
+    @VisibleForTesting
+    public static final String PREF_SYNC_MANAGE_DATA = "sync_manage_data";
 
     private final ProfileSyncService mProfileSyncService = ProfileSyncService.get();
 
@@ -230,8 +241,8 @@
         // Note: mSyncPaymentsIntegration should be checked if mSyncEverything is checked, but if
         // mSyncEverything was just enabled, then that state may not have propagated to
         // mSyncPaymentsIntegration yet. See crbug.com/972863.
-        PersonalDataManager.setPaymentsIntegrationEnabled(
-                mSyncEverything.isChecked() || mSyncPaymentsIntegration.isChecked());
+        PersonalDataManager.setPaymentsIntegrationEnabled(mSyncEverything.isChecked()
+                || (mSyncPaymentsIntegration.isChecked() && mSyncAutofill.isChecked()));
         // Some calls to setChosenDataTypes don't trigger syncStateChanged, so schedule update here.
         PostTask.postTask(UiThreadTaskTraits.DEFAULT, this::updateSyncPreferences);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
index 0137bdae5..7b201412 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
@@ -88,7 +88,8 @@
     private static final String PREF_SYNC_ERROR_CARD = "sync_error_card";
     private static final String PREF_SYNC_DISABLED_BY_ADMINISTRATOR =
             "sync_disabled_by_administrator";
-    private static final String PREF_SYNC_REQUESTED = "sync_requested";
+    @VisibleForTesting
+    public static final String PREF_SYNC_REQUESTED = "sync_requested";
     private static final String PREF_MANAGE_SYNC = "manage_sync";
 
     private static final String PREF_SERVICES_CATEGORY = "services_category";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreference.java
index c34b9d5..b0be9d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreference.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
-import org.chromium.chrome.browser.preferences.ChromeImageViewPreference;
 import org.chromium.chrome.browser.preferences.ChromeImageViewPreferenceCompat;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
@@ -28,8 +27,8 @@
 /**
  * A preference that displays a website's favicon and URL and, optionally, the amount of local
  * storage used by the site. This preference can also display an additional icon on the right side
- * of the preference. See {@link ChromeImageViewPreference} for more details on how this icon can
- * be used.
+ * of the preference. See {@link ChromeImageViewPreferenceCompat} for more details on how this icon
+ * can be used.
  */
 class WebsitePreference extends ChromeImageViewPreferenceCompat implements FaviconImageCallback {
     private final Website mSite;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
index 9356c825..02cf286 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -27,7 +27,7 @@
     /** Manages whether to check update for the WebAPK, and starts update check if needed. */
     private WebApkUpdateManager mUpdateManager;
 
-    /** The start time that the activity becomes focused. */
+    /** The start time that the activity becomes focused in milliseconds since boot. */
     private long mStartTime;
 
     private static final String TAG = "cr_WebApkActivity";
@@ -110,8 +110,10 @@
     @Override
     public void onPauseWithNative() {
         WebApkInfo info = (WebApkInfo) getWebappInfo();
-        WebApkUma.recordWebApkSessionDuration(
-                info.distributor(), SystemClock.elapsedRealtime() - mStartTime);
+        long sessionDuration = SystemClock.elapsedRealtime() - mStartTime;
+        WebApkUma.recordWebApkSessionDuration(info.distributor(), sessionDuration);
+        WebApkUkmRecorder.recordWebApkSessionDuration(
+                info.manifestUrl(), info.distributor(), info.webApkVersionCode(), sessionDuration);
         super.onPauseWithNative();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index 7e15ca4..85328d89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -138,6 +138,7 @@
     public static final String RESOURCE_SHORT_NAME = "short_name";
     public static final String RESOURCE_STRING_TYPE = "string";
 
+    // This enum is used to back UMA/UKM histograms, and should therefore be treated as append-only.
     @IntDef({WebApkDistributor.BROWSER, WebApkDistributor.DEVICE_POLICY, WebApkDistributor.OTHER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface WebApkDistributor {
@@ -157,6 +158,7 @@
     private @WebApkDistributor int mDistributor;
     private ShareTarget mShareTarget;
     private String mShareTargetActivityName;
+    private int mApkVersionCode;
     private Map<String, String> mIconUrlToMurmur2HashMap;
     private boolean mIsSplashProvidedByWebApk;
 
@@ -282,9 +284,12 @@
         }
 
         Context appContext = ContextUtils.getApplicationContext();
+        PackageManager pm = appContext.getPackageManager();
         Resources res = null;
+        int apkVersion = 0;
         try {
-            res = appContext.getPackageManager().getResourcesForApplication(webApkPackageName);
+            res = pm.getResourcesForApplication(webApkPackageName);
+            apkVersion = pm.getPackageInfo(webApkPackageName, 0).versionCode;
         } catch (PackageManager.NameNotFoundException e) {
             return null;
         }
@@ -337,6 +342,9 @@
         int primaryIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.ICON_ID, 0);
         Bitmap primaryIcon = decodeBitmapFromDrawable(res, primaryIconId);
 
+        boolean isPrimaryIconMaskable =
+                IntentUtils.safeGetBoolean(bundle, WebApkMetaDataKeys.IS_ICON_MASKABLE, false);
+
         int badgeIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.BADGE_ICON_ID, 0);
         Bitmap badgeIcon = decodeBitmapFromDrawable(res, badgeIconId);
 
@@ -355,9 +363,10 @@
         return create(WebApkConstants.WEBAPK_ID_PREFIX + webApkPackageName, url, scope,
                 new Icon(primaryIcon), new Icon(badgeIcon), new Icon(splashIcon), name, shortName,
                 displayMode, orientation, source, themeColor, backgroundColor,
-                defaultBackgroundColor, webApkPackageName, shellApkVersion, manifestUrl,
-                manifestStartUrl, distributor, iconUrlToMurmur2HashMap, shareTarget,
-                shareTargetActivityName, forceNavigation, isSplashProvidedByWebApk, shareData);
+                defaultBackgroundColor, isPrimaryIconMaskable, webApkPackageName, shellApkVersion,
+                manifestUrl, manifestStartUrl, distributor, iconUrlToMurmur2HashMap, shareTarget,
+                shareTargetActivityName, forceNavigation, isSplashProvidedByWebApk, shareData,
+                apkVersion);
     }
 
     /**
@@ -377,6 +386,7 @@
      * @param backgroundColor          The background color of the WebAPK.
      * @param defaultBackgroundColor   The background color to use if the Web Manifest does not
      *                                 provide a background color.
+     * @param isPrimaryIconMaskable    Is the primary icon maskable.
      * @param webApkPackageName        The package of the WebAPK.
      * @param shellApkVersion          Version of the code in //chrome/android/webapk/shell_apk.
      * @param manifestUrl              URL of the Web Manifest.
@@ -395,15 +405,17 @@
      *                                 display the splash screen and (2) has a content provider
      *                                 which provides a screenshot of the splash screen.
      * @param shareData                Shared information from the share intent.
+     * @param webApkVersionCode        WebAPK's version code.
      */
     public static WebApkInfo create(String id, String url, String scope, Icon primaryIcon,
             Icon badgeIcon, Icon splashIcon, String name, String shortName,
             @WebDisplayMode int displayMode, int orientation, int source, long themeColor,
-            long backgroundColor, int defaultBackgroundColor, String webApkPackageName,
-            int shellApkVersion, String manifestUrl, String manifestStartUrl,
-            @WebApkDistributor int distributor, Map<String, String> iconUrlToMurmur2HashMap,
-            ShareTarget shareTarget, String shareTargetActivityName, boolean forceNavigation,
-            boolean isSplashProvidedByWebApk, ShareData shareData) {
+            long backgroundColor, int defaultBackgroundColor, boolean isPrimaryIconMaskable,
+            String webApkPackageName, int shellApkVersion, String manifestUrl,
+            String manifestStartUrl, @WebApkDistributor int distributor,
+            Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
+            String shareTargetActivityName, boolean forceNavigation,
+            boolean isSplashProvidedByWebApk, ShareData shareData, int webApkVersionCode) {
         if (id == null || url == null || manifestStartUrl == null || webApkPackageName == null) {
             Log.e(TAG,
                     "Incomplete data provided: " + id + ", " + url + ", " + manifestStartUrl + ", "
@@ -420,22 +432,23 @@
 
         return new WebApkInfo(id, url, scope, primaryIcon, badgeIcon, splashIcon, name, shortName,
                 displayMode, orientation, source, themeColor, backgroundColor,
-                defaultBackgroundColor, webApkPackageName, shellApkVersion, manifestUrl,
-                manifestStartUrl, distributor, iconUrlToMurmur2HashMap, shareTarget,
-                shareTargetActivityName, forceNavigation, isSplashProvidedByWebApk, shareData);
+                defaultBackgroundColor, isPrimaryIconMaskable, webApkPackageName, shellApkVersion,
+                manifestUrl, manifestStartUrl, distributor, iconUrlToMurmur2HashMap, shareTarget,
+                shareTargetActivityName, forceNavigation, isSplashProvidedByWebApk, shareData,
+                webApkVersionCode);
     }
 
     protected WebApkInfo(String id, String url, String scope, Icon primaryIcon, Icon badgeIcon,
             Icon splashIcon, String name, String shortName, @WebDisplayMode int displayMode,
             int orientation, int source, long themeColor, long backgroundColor,
-            int defaultBackgroundColor, String webApkPackageName, int shellApkVersion,
-            String manifestUrl, String manifestStartUrl, @WebApkDistributor int distributor,
-            Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
-            String shareTargetActivityName, boolean forceNavigation,
-            boolean isSplashProvidedByWebApk, ShareData shareData) {
+            int defaultBackgroundColor, boolean isPrimaryIconMaskable, String webApkPackageName,
+            int shellApkVersion, String manifestUrl, String manifestStartUrl,
+            @WebApkDistributor int distributor, Map<String, String> iconUrlToMurmur2HashMap,
+            ShareTarget shareTarget, String shareTargetActivityName, boolean forceNavigation,
+            boolean isSplashProvidedByWebApk, ShareData shareData, int webApkVersionCode) {
         super(id, url, scope, primaryIcon, name, shortName, displayMode, orientation, source,
                 themeColor, backgroundColor, defaultBackgroundColor, false /* isIconGenerated */,
-                false /* isIconAdaptive */, forceNavigation);
+                isPrimaryIconMaskable /* isIconAdaptive */, forceNavigation);
         mBadgeIcon = badgeIcon;
         mSplashIcon = splashIcon;
         mApkPackageName = webApkPackageName;
@@ -451,6 +464,7 @@
             mShareTarget = new ShareTarget();
         }
         mShareTargetActivityName = shareTargetActivityName;
+        mApkVersionCode = webApkVersionCode;
     }
 
     protected WebApkInfo() {}
@@ -481,6 +495,13 @@
         return mShareTargetActivityName;
     }
 
+    /**
+     * Returns the WebAPK's version code.
+     */
+    public int webApkVersionCode() {
+        return mApkVersionCode;
+    }
+
     @Override
     public boolean isForWebApk() {
         return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java
new file mode 100644
index 0000000..ba17c7b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUkmRecorder.java
@@ -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.
+
+package org.chromium.chrome.browser.webapps;
+
+/**
+ * A class to record User Keyed Metrics relevant to WebAPKs. This
+ * will allow us to concentrate on the use cases for the most used WebAPKs.
+ */
+public class WebApkUkmRecorder {
+    /**
+     * Records the duration, in exponentially-bucketed milliseconds, of a WebAPK session (from
+     * launch/foreground to background).
+     */
+    public static void recordWebApkSessionDuration(String manifestUrl,
+            @WebApkInfo.WebApkDistributor int distributor, int versionCode, long duration) {
+        nativeRecordSessionDuration(manifestUrl, distributor, versionCode, duration);
+    }
+
+    private static native void nativeRecordSessionDuration(
+            String manifestUrl, int distributor, int versionCode, long duration);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
index 4ad4d92..52a967a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
@@ -100,11 +100,11 @@
     @CalledByNative
     protected void onDataAvailable(String manifestStartUrl, String scopeUrl, String name,
             String shortName, String primaryIconUrl, String primaryIconMurmur2Hash,
-            Bitmap primaryIconBitmap, String badgeIconUrl, String badgeIconMurmur2Hash,
-            Bitmap badgeIconBitmap, String[] iconUrls, @WebDisplayMode int displayMode,
-            int orientation, long themeColor, long backgroundColor, String shareAction,
-            String shareParamsTitle, String shareParamsText, String shareParamsUrl,
-            boolean isShareMethodPost, boolean isShareEncTypeMultipart,
+            Bitmap primaryIconBitmap, boolean isPrimaryIconMaskable, String badgeIconUrl,
+            String badgeIconMurmur2Hash, Bitmap badgeIconBitmap, String[] iconUrls,
+            @WebDisplayMode int displayMode, int orientation, long themeColor, long backgroundColor,
+            String shareAction, String shareParamsTitle, String shareParamsText,
+            String shareParamsUrl, boolean isShareMethodPost, boolean isShareEncTypeMultipart,
             String[] shareParamsFileNames, String[][] shareParamsAccepts) {
         Context appContext = ContextUtils.getApplicationContext();
 
@@ -130,10 +130,11 @@
         WebApkInfo info = WebApkInfo.create(mOldInfo.id(), mOldInfo.uri().toString(), scopeUrl,
                 new WebApkInfo.Icon(primaryIconBitmap), new WebApkInfo.Icon(badgeIconBitmap), null,
                 name, shortName, displayMode, orientation, mOldInfo.source(), themeColor,
-                backgroundColor, defaultBackgroundColor, mOldInfo.webApkPackageName(),
-                mOldInfo.shellApkVersion(), mOldInfo.manifestUrl(), manifestStartUrl,
-                WebApkInfo.WebApkDistributor.BROWSER, iconUrlToMurmur2HashMap, shareTarget, null,
-                mOldInfo.shouldForceNavigation(), mOldInfo.isSplashProvidedByWebApk(), null);
+                backgroundColor, defaultBackgroundColor, isPrimaryIconMaskable,
+                mOldInfo.webApkPackageName(), mOldInfo.shellApkVersion(), mOldInfo.manifestUrl(),
+                manifestStartUrl, WebApkInfo.WebApkDistributor.BROWSER, iconUrlToMurmur2HashMap,
+                shareTarget, null, mOldInfo.shouldForceNavigation(),
+                mOldInfo.isSplashProvidedByWebApk(), null, mOldInfo.webApkVersionCode());
         mObserver.onGotManifestData(info, primaryIconUrl, badgeIconUrl);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index d1275006..47af076c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -6,8 +6,6 @@
 
 import static org.chromium.webapk.lib.common.WebApkConstants.WEBAPK_PACKAGE_PREFIX;
 
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.text.TextUtils;
@@ -232,21 +230,6 @@
     }
 
     /**
-     * Reads the WebAPK's version code. Returns 0 on failure.
-     */
-    private int readVersionCodeFromAndroidManifest(String webApkPackage) {
-        try {
-            PackageManager packageManager =
-                    ContextUtils.getApplicationContext().getPackageManager();
-            PackageInfo packageInfo = packageManager.getPackageInfo(webApkPackage, 0);
-            return packageInfo.versionCode;
-        } catch (PackageManager.NameNotFoundException e) {
-            e.printStackTrace();
-        }
-        return 0;
-    }
-
-    /**
      * Whether there is a new version of the //chrome/android/webapk/shell_apk code.
      */
     private static boolean isShellApkVersionOutOfDate(WebApkInfo info) {
@@ -377,7 +360,7 @@
     protected void storeWebApkUpdateRequestToFile(String updateRequestPath, WebApkInfo info,
             String primaryIconUrl, String badgeIconUrl, boolean isManifestStale,
             @WebApkUpdateReason int updateReason, Callback<Boolean> callback) {
-        int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageName());
+        int versionCode = info.webApkVersionCode();
         int size = info.iconUrlToMurmur2HashMap().size();
         String[] iconUrls = new String[size];
         String[] iconHashes = new String[size];
@@ -391,11 +374,11 @@
 
         WebApkUpdateManagerJni.get().storeWebApkUpdateRequestToFile(updateRequestPath,
                 info.manifestStartUrl(), info.scopeUri().toString(), info.name(), info.shortName(),
-                primaryIconUrl, info.icon(), badgeIconUrl, info.badgeIcon(), iconUrls, iconHashes,
-                info.displayMode(), info.orientation(), info.themeColor(), info.backgroundColor(),
-                info.shareTarget().getAction(), info.shareTarget().getParamTitle(),
-                info.shareTarget().getParamText(), info.shareTarget().getParamUrl(),
-                info.shareTarget().isShareMethodPost(),
+                primaryIconUrl, info.icon(), info.isIconAdaptive(), badgeIconUrl, info.badgeIcon(),
+                iconUrls, iconHashes, info.displayMode(), info.orientation(), info.themeColor(),
+                info.backgroundColor(), info.shareTarget().getAction(),
+                info.shareTarget().getParamTitle(), info.shareTarget().getParamText(),
+                info.shareTarget().getParamUrl(), info.shareTarget().isShareMethodPost(),
                 info.shareTarget().isShareEncTypeMultipart(), info.shareTarget().getFileNames(),
                 info.shareTarget().getFileAccepts(), info.manifestUrl(), info.webApkPackageName(),
                 versionCode, isManifestStale, updateReason, callback);
@@ -405,14 +388,15 @@
     interface Natives {
         public void storeWebApkUpdateRequestToFile(String updateRequestPath, String startUrl,
                 String scope, String name, String shortName, String primaryIconUrl,
-                Bitmap primaryIcon, String badgeIconUrl, Bitmap badgeIcon, String[] iconUrls,
-                String[] iconHashes, @WebDisplayMode int displayMode, int orientation,
-                long themeColor, long backgroundColor, String shareTargetAction,
-                String shareTargetParamTitle, String shareTargetParamText,
-                String shareTargetParamUrl, boolean shareTargetParamIsMethodPost,
-                boolean shareTargetParamIsEncTypeMultipart, String[] shareTargetParamFileNames,
-                Object[] shareTargetParamAccepts, String manifestUrl, String webApkPackage,
-                int webApkVersion, boolean isManifestStale, @WebApkUpdateReason int updateReason,
+                Bitmap primaryIcon, boolean isPrimaryIconMaskable, String badgeIconUrl,
+                Bitmap badgeIcon, String[] iconUrls, String[] iconHashes,
+                @WebDisplayMode int displayMode, int orientation, long themeColor,
+                long backgroundColor, String shareTargetAction, String shareTargetParamTitle,
+                String shareTargetParamText, String shareTargetParamUrl,
+                boolean shareTargetParamIsMethodPost, boolean shareTargetParamIsEncTypeMultipart,
+                String[] shareTargetParamFileNames, Object[] shareTargetParamAccepts,
+                String manifestUrl, String webApkPackage, int webApkVersion,
+                boolean isManifestStale, @WebApkUpdateReason int updateReason,
                 Callback<Boolean> callback);
         public void updateWebApkFromFile(String updateRequestPath, WebApkUpdateCallback callback);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
index b2c577e..77d7470d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashDelegate.java
@@ -135,6 +135,7 @@
 
         Bitmap selectedIcon = splashImage;
         boolean selectedIconGenerated = false;
+        // TODO(crbug.com/977173): assign selectedIconAdaptive to correct value
         boolean selectedIconAdaptive = false;
         if (selectedIcon == null) {
             selectedIcon = mWebappInfo.icon();
@@ -190,7 +191,7 @@
         splashView.setBackgroundColor(backgroundColor);
 
         Bitmap splashBitmap = null;
-        try (StrictModeContext smc = StrictModeContext.allowDiskReads()) {
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
             splashBitmap = FileUtils.queryBitmapFromContentProvider(appContext,
                     Uri.parse(WebApkCommonUtils.generateSplashContentProviderUri(
                             mWebappInfo.webApkPackageName())));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java
index 45c08570..24fdb1aa8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java
@@ -14,7 +14,6 @@
 import android.support.v7.widget.helper.ItemTouchHelper;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 
@@ -28,7 +27,6 @@
  */
 public abstract class DragReorderableListAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
     private static final int ANIMATION_DELAY_MS = 100;
-    private static final String TAG = "DragAdapter";
 
     protected final Context mContext;
 
@@ -237,12 +235,10 @@
         int increment = start < end ? 1 : -1;
         int i = start;
         while (i != end) {
-            Log.i(TAG, "Moving ViewHolder %s from %d to %d", viewHolder, i, i + increment);
             i += increment;
             mTouchHelperCallback.onMove(
                     mRecyclerView, viewHolder, mRecyclerView.findViewHolderForAdapterPosition(i));
         }
-        Log.i(TAG, "Letting go of ViewHolder %s", viewHolder);
         mTouchHelperCallback.onSelectedChanged(viewHolder, ItemTouchHelper.ACTION_STATE_IDLE);
         mTouchHelperCallback.clearView(mRecyclerView, viewHolder);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragStateDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragStateDelegate.java
index a2e7d75..3ac3a734 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragStateDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragStateDelegate.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.widget.dragreorder;
 
+import org.chromium.base.VisibleForTesting;
+
 /**
  * Responsible for keeping track of the drag state (whether drag is enabled, and if so, whether drag
  * is active).
@@ -21,4 +23,7 @@
      * dragged). Activating drag is only valid if drag is currently enabled.
      */
     boolean getDragActive();
+
+    @VisibleForTesting
+    void setA11yStateForTesting(boolean a11yEnabled);
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java
new file mode 100644
index 0000000..3189087e
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillUpstreamTest.java
@@ -0,0 +1,172 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.view.ViewGroup;
+
+import org.junit.After;
+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.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.infobar.AutofillSaveCardInfoBar;
+import org.chromium.chrome.browser.infobar.InfoBar;
+import org.chromium.chrome.browser.infobar.InfoBarLayout;
+import org.chromium.chrome.browser.sync.SyncTestRule;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.DOMUtils;
+import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.ui.widget.ButtonCompat;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Integration tests for the Autofill Upstream and Expiration Date Fix Flow.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@RetryOnFailure
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        "enable-features=AutofillUpstreamEditableExpirationDate"})
+public class AutofillUpstreamTest {
+    private static final String TEST_SERVER_DIR = "components/test/data/autofill";
+    private static final String TEST_FORM_URL = "/credit_card_upload_form_address_and_cc.html";
+    private static final String SAVE_BUTTON_LABEL = "Save";
+    private static final String CONTINUE_BUTTON_LABEL = "Continue";
+
+    @Rule
+    public SyncTestRule mSyncTestRule = new SyncTestRule();
+
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private EmbeddedTestServer mServer;
+
+    @Before
+    public void setUp() throws Exception {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mServer = new EmbeddedTestServer();
+        mServer.initializeNative(InstrumentationRegistry.getContext(),
+                EmbeddedTestServer.ServerHTTPSSetting.USE_HTTP);
+        mServer.addDefaultHandlers(TEST_SERVER_DIR);
+        mServer.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mServer.stopAndDestroyServer();
+    }
+
+    private void assertPrimaryButtonLabel(String buttonLabel) {
+        List<InfoBar> infobars = mActivityTestRule.getInfoBars();
+        if (hasAutofillSaveCardInfobar(infobars)) {
+            InfoBarLayout view = (InfoBarLayout) infobars.get(0).getView();
+            ButtonCompat primaryButton = view.getPrimaryButton();
+            Assert.assertEquals(buttonLabel, primaryButton.getText().toString());
+        } else {
+            Assert.fail("Save card infobar not found");
+        }
+    }
+
+    private void waitForSaveCardInfoBar(final ViewGroup view) {
+        CriteriaHelper.pollUiThread(
+                new Criteria("Autofill Save Card Infobar view was never added.") {
+                    @Override
+                    public boolean isSatisfied() {
+                        return hasAutofillSaveCardInfobar(mActivityTestRule.getInfoBars());
+                    }
+                });
+    }
+
+    private boolean hasAutofillSaveCardInfobar(List<InfoBar> infobars) {
+        return (infobars != null && infobars.size() == 1
+                && infobars.get(0) instanceof AutofillSaveCardInfoBar);
+    }
+
+    @Test
+    @MediumTest
+    @Restriction(Restriction.RESTRICTION_TYPE_INTERNET)
+    public void testSaveCardInfoBarWithAllFieldsFilled()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mActivityTestRule.startMainActivityWithURL(mServer.getURL(TEST_FORM_URL));
+        final WebContents webContents = mActivityTestRule.getActivity().getCurrentWebContents();
+
+        DOMUtils.clickNode(webContents, "fill_form");
+        DOMUtils.clickNode(webContents, "submit");
+        final ViewGroup view = webContents.getViewAndroidDelegate().getContainerView();
+        waitForSaveCardInfoBar(view);
+
+        assertPrimaryButtonLabel(SAVE_BUTTON_LABEL);
+    }
+
+    @Test
+    @MediumTest
+    @Restriction(Restriction.RESTRICTION_TYPE_INTERNET)
+    public void testSaveCardInfoBarWithEmptyMonth()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mActivityTestRule.startMainActivityWithURL(mServer.getURL(TEST_FORM_URL));
+        final WebContents webContents = mActivityTestRule.getActivity().getCurrentWebContents();
+
+        DOMUtils.clickNode(webContents, "fill_form");
+        // Clear the month field
+        DOMUtils.clickNode(webContents, "clear_month");
+        DOMUtils.clickNode(webContents, "submit");
+        final ViewGroup view = webContents.getViewAndroidDelegate().getContainerView();
+        waitForSaveCardInfoBar(view);
+
+        assertPrimaryButtonLabel(CONTINUE_BUTTON_LABEL);
+    }
+
+    @Test
+    @MediumTest
+    @Restriction(Restriction.RESTRICTION_TYPE_INTERNET)
+    public void testSaveCardInfoBarWithEmptyYear()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mActivityTestRule.startMainActivityWithURL(mServer.getURL(TEST_FORM_URL));
+        final WebContents webContents = mActivityTestRule.getActivity().getCurrentWebContents();
+
+        DOMUtils.clickNode(webContents, "fill_form");
+        // Clear the year field
+        DOMUtils.clickNode(webContents, "clear_year");
+        DOMUtils.clickNode(webContents, "submit");
+        final ViewGroup view = webContents.getViewAndroidDelegate().getContainerView();
+        waitForSaveCardInfoBar(view);
+
+        assertPrimaryButtonLabel(CONTINUE_BUTTON_LABEL);
+    }
+
+    @Test
+    @MediumTest
+    @Restriction(Restriction.RESTRICTION_TYPE_INTERNET)
+    public void testSaveCardInfoBarWithEmptyMonthAndYear()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mActivityTestRule.startMainActivityWithURL(mServer.getURL(TEST_FORM_URL));
+        final WebContents webContents = mActivityTestRule.getActivity().getCurrentWebContents();
+
+        DOMUtils.clickNode(webContents, "fill_form");
+        // Clear the month and year field
+        DOMUtils.clickNode(webContents, "clear_expiration_date");
+        DOMUtils.clickNode(webContents, "submit");
+        final ViewGroup view = webContents.getViewAndroidDelegate().getContainerView();
+        waitForSaveCardInfoBar(view);
+
+        assertPrimaryButtonLabel(CONTINUE_BUTTON_LABEL);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/OWNERS
index b946bf67..e716fed5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/OWNERS
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/OWNERS
@@ -1 +1,2 @@
-file://chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
\ No newline at end of file
+file://chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
+per-file AutofillUpstreamTest.java=file://components/autofill/core/browser/payments/OWNERS
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
index 45262cd7..390301a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
@@ -4,13 +4,15 @@
 
 package org.chromium.chrome.browser.bookmarks;
 
+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.doesNotExist;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
 import android.support.test.filters.MediumTest;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
 
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.night_mode.NightModeTestUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -23,6 +25,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.night_mode.NightModeTestUtils;
+import org.chromium.chrome.browser.widget.ListMenuButton;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
 import org.chromium.chrome.test.util.browser.Features;
@@ -57,22 +61,15 @@
     private static final String TEST_URL_A = "http://a.com";
     private static final String TAG = "BookmarkReorderTest";
 
-
     @Override
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    @DisabledTest(message = "https://crbug.com/986915")
-    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
-    public void testBookmarkFolderIcon(boolean nightModeEnabled) throws Exception {
-        Assert.assertTrue("Expected Bookmark Reordering to be enabled",
-                          ChromeFeatureList.isEnabled(ChromeFeatureList.REORDER_BOOKMARKS));
-        super.testBookmarkFolderIcon(nightModeEnabled);
+    @ParameterAnnotations.UseMethodParameterBefore(NightModeTestUtils.NightModeParams.class)
+    public void setupNightMode(boolean nightModeEnabled) {
+        NightModeTestUtils.setUpNightModeForChromeActivity(nightModeEnabled);
+        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
     }
 
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/986915")
     public void testEndIconVisibilityInSelectionMode() throws Exception {
         BookmarkId testId = addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_TITLE_A, TEST_URL_A);
@@ -88,8 +85,7 @@
         View aMoreButton = testFolderA.findViewById(R.id.more);
         View aDragHandle = testFolderA.findViewById(R.id.drag_handle);
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
+        toggleSelectionAndEndAnimation(testId, test);
 
         // Callback occurs when Item "test" is selected.
         CriteriaHelper.pollUiThread(test::isChecked, "Expected item \"test\" to become selected");
@@ -141,8 +137,7 @@
                                                        .isSearching(),
                 "Expected to enter search mode");
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
+        toggleSelectionAndEndAnimation(testId, test);
 
         // Callback occurs when Item "test" is selected.
         CriteriaHelper.pollUiThread(test::isChecked, "Expected item \"test\" to become selected");
@@ -205,33 +200,22 @@
                     }
                 };
 
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
+        // Perform registration to make callbacks work.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mBookmarkModel.addObserver(bookmarkModelObserver);
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
         });
 
         View foo = mItemsContainer.findViewHolderForAdapterPosition(3).itemView;
         Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_FOO,
                 ((BookmarkItemRow) foo).getTitle());
-        View dragHandle = foo.findViewById(R.id.drag_handle);
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(fooId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(fooId, (BookmarkRow) foo);
 
         // Starts as last bookmark (2nd index) and ends as 0th bookmark (promo header not included).
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()).simulateDragForTests(3, 1);
         });
 
+        modelReorderHelper.waitForCallback(0, 1);
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -280,33 +264,23 @@
                     }
                 };
 
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
+        // Perform registration to make callbacks work.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mBookmarkModel.addObserver(bookmarkModelObserver);
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
         });
 
         View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
         Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
                 ((BookmarkFolderRow) test).getTitle());
-        View dragHandle = test.findViewById(R.id.drag_handle);
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(testId, (BookmarkRow) test);
 
         // Starts as 0th bookmark (not counting promo header) and ends as last (index 3).
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()).simulateDragForTests(1, 4);
         });
 
+        modelReorderHelper.waitForCallback(0, 1);
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -351,27 +325,16 @@
                         modelReorderHelper.notifyCalled();
                     }
                 };
-
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
+        // Perform registration to make callbacks work.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mBookmarkModel.addObserver(bookmarkModelObserver);
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
         });
 
         View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
         Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
                 ((BookmarkFolderRow) test).getTitle());
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(testId, (BookmarkRow) test);
 
         // Starts as 0th bookmark (not counting promo header) and ends at the 1st index.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -396,23 +359,10 @@
 
         openBookmarkManager();
 
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
-        });
-
         ViewHolder promo = mItemsContainer.findViewHolderForAdapterPosition(0);
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(
+                testId, (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView);
 
         ReorderBookmarkItemsAdapter adapter =
                 ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter());
@@ -428,23 +378,10 @@
         BookmarkId testId = addFolderWithPartner(TEST_FOLDER_TITLE);
         openBookmarkManager();
 
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
-        });
-
         ViewHolder partner = mItemsContainer.findViewHolderForAdapterPosition(2);
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(
+                testId, (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView);
 
         ReorderBookmarkItemsAdapter adapter =
                 ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter());
@@ -462,25 +399,12 @@
 
         openBookmarkManager();
 
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-
-        // Perform registration to make all of these callbacks work.
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
-        });
-
         ViewHolder test = mItemsContainer.findViewHolderForAdapterPosition(1);
         Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
                 ((BookmarkFolderRow) test.itemView).getTitle());
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.getSelectionDelegate().toggleSelectionForItem(aId));
-
-        // Entering selection mode - items are getting redrawn.
-        layoutHelper.waitForCallback();
+        toggleSelectionAndEndAnimation(
+                aId, (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(2).itemView);
 
         ReorderBookmarkItemsAdapter adapter =
                 ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter());
@@ -497,27 +421,9 @@
 
         openBookmarkManager();
 
-        // Callback occurs upon changes to the bookmark model.
-        CallbackHelper modelReorderHelper = new CallbackHelper();
-        BookmarkBridge.BookmarkModelObserver bookmarkModelObserver =
-                new BookmarkBridge.BookmarkModelObserver() {
-                    @Override
-                    public void bookmarkModelChanged() {
-                        modelReorderHelper.notifyCalled();
-                    }
-                };
-
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mBookmarkModel.addObserver(bookmarkModelObserver);
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
-        });
-
         View promo = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
         TouchCommon.longPressView(promo);
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
         Assert.assertFalse("Expected that we would not be in selection mode "
                         + "after long pressing on promo view.",
                 mManager.getSelectionDelegate().isSelectionEnabled());
@@ -529,32 +435,129 @@
         addFolderWithPartner(TEST_FOLDER_TITLE);
         openBookmarkManager();
 
-        // Callback occurs upon changes to the bookmark model.
-        CallbackHelper modelReorderHelper = new CallbackHelper();
-        BookmarkBridge.BookmarkModelObserver bookmarkModelObserver =
-                new BookmarkBridge.BookmarkModelObserver() {
-                    @Override
-                    public void bookmarkModelChanged() {
-                        modelReorderHelper.notifyCalled();
-                    }
-                };
-
-        // Callback occurs upon layout changes.
-        CallbackHelper layoutHelper = new CallbackHelper();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mBookmarkModel.addObserver(bookmarkModelObserver);
-            mItemsContainer.addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                            oldBottom) -> layoutHelper.notifyCalled());
-        });
-
         View partner = mItemsContainer.findViewHolderForAdapterPosition(2).itemView;
         TouchCommon.longPressView(partner);
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
         Assert.assertFalse("Expected that we would not be in selection mode "
                         + "after long pressing on partner bookmark.",
                 mManager.getSelectionDelegate().isSelectionEnabled());
     }
 
+    @Test
+    @MediumTest
+    public void testMoveUpMenuItem() throws Exception {
+        addBookmark(TEST_PAGE_TITLE_GOOGLE, TEST_URL_A);
+        addFolder(TEST_FOLDER_TITLE);
+        openBookmarkManager();
+
+        View google = mItemsContainer.findViewHolderForAdapterPosition(2).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_GOOGLE,
+                ((BookmarkItemRow) google).getTitle());
+        View more = google.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::callOnClick);
+        onView(withText("Move up")).perform(click());
+
+        // Confirm that the "Google" bookmark is now on top, and that the "test" folder is 2nd
+        Assert.assertTrue(
+                ((BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView)
+                        .getTitle()
+                        .equals(TEST_PAGE_TITLE_GOOGLE));
+        Assert.assertTrue(
+                ((BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(2).itemView)
+                        .getTitle()
+                        .equals(TEST_FOLDER_TITLE));
+    }
+
+    @Test
+    @MediumTest
+    public void testMoveDownMenuItem() throws Exception {
+        addBookmark(TEST_PAGE_TITLE_GOOGLE, TEST_URL_A);
+        addFolder(TEST_FOLDER_TITLE);
+        openBookmarkManager();
+
+        View testFolder = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
+                ((BookmarkFolderRow) testFolder).getTitle());
+        ListMenuButton more = testFolder.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::callOnClick);
+        onView(withText("Move down")).perform(click());
+
+        // Confirm that the "Google" bookmark is now on top, and that the "test" folder is 2nd
+        Assert.assertTrue(
+                ((BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(1).itemView)
+                        .getTitle()
+                        .equals(TEST_PAGE_TITLE_GOOGLE));
+        Assert.assertTrue(
+                ((BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(2).itemView)
+                        .getTitle()
+                        .equals(TEST_FOLDER_TITLE));
+    }
+
+    @Test
+    @MediumTest
+    public void testMoveDownGoneForBottomElement() throws Exception {
+        addBookmarkWithPartner(TEST_PAGE_TITLE_GOOGLE, TEST_URL_A);
+        addFolderWithPartner(TEST_FOLDER_TITLE);
+        openBookmarkManager();
+
+        View google = mItemsContainer.findViewHolderForAdapterPosition(2).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_GOOGLE,
+                ((BookmarkItemRow) google).getTitle());
+        View more = google.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::callOnClick);
+        onView(withText("Move down")).check(doesNotExist());
+    }
+
+    @Test
+    @MediumTest
+    public void testMoveUpGoneForTopElement() throws Exception {
+        addBookmark(TEST_PAGE_TITLE_GOOGLE, TEST_URL_A);
+        addFolder(TEST_FOLDER_TITLE);
+        openBookmarkManager();
+
+        View testFolder = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
+                ((BookmarkFolderRow) testFolder).getTitle());
+        ListMenuButton more = testFolder.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::callOnClick);
+        onView(withText("Move up")).check(doesNotExist());
+    }
+
+    @Test
+    @MediumTest
+    public void testMoveButtonsGoneInSearchMode() throws Exception {
+        addFolder(TEST_FOLDER_TITLE);
+        openBookmarkManager();
+
+        View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
+        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
+
+        // Callback occurs when Item "test" is selected.
+        CriteriaHelper.pollUiThread(()
+                                            -> mBookmarkActivity.getManagerForTesting()
+                                                       .getToolbarForTests()
+                                                       .isSearching(),
+                "Expected to enter search mode");
+
+        View testFolder = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
+                ((BookmarkFolderRow) testFolder).getTitle());
+        View more = testFolder.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::callOnClick);
+
+        onView(withText("Move up")).check(doesNotExist());
+        onView(withText("Move down")).check(doesNotExist());
+    }
+
+    @Override
+    protected void openBookmarkManager() throws InterruptedException {
+        super.openBookmarkManager();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mManager.getDragStateDelegate().setA11yStateForTesting(false);
+            BookmarkPromoHeader.forcePromoStateForTests(BookmarkPromoHeader.PromoState.PROMO_SYNC);
+        });
+    }
+
     /**
      * Loads an empty partner bookmarks folder for testing. The partner bookmarks folder will appear
      * in the mobile bookmarks folder.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 34b37b26..66a2b992 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -51,6 +51,7 @@
 import org.chromium.chrome.test.util.MenuUtils;
 import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.components.sync.AndroidSyncSettings;
@@ -141,8 +142,8 @@
     protected void openBookmarkManager() throws InterruptedException {
         if (mActivityTestRule.getActivity().isTablet()) {
             mActivityTestRule.loadUrl(UrlConstants.BOOKMARKS_URL);
-            mItemsContainer =
-                    (RecyclerView) mActivityTestRule.getActivity().findViewById(R.id.recycler_view);
+            mItemsContainer = mActivityTestRule.getActivity().findViewById(R.id.recycler_view);
+            mItemsContainer.setItemAnimator(null); // Disable animation to reduce flakiness.
             mManager = ((BookmarkPage) mActivityTestRule.getActivity()
                                 .getActivityTab()
                                 .getNativePage())
@@ -153,7 +154,7 @@
                     InstrumentationRegistry.getInstrumentation(), BookmarkActivity.class,
                     new MenuUtils.MenuActivityTrigger(InstrumentationRegistry.getInstrumentation(),
                             mActivityTestRule.getActivity(), R.id.all_bookmarks_menu_id));
-            mItemsContainer = (RecyclerView) mBookmarkActivity.findViewById(R.id.recycler_view);
+            mItemsContainer = mBookmarkActivity.findViewById(R.id.recycler_view);
             mItemsContainer.setItemAnimator(null); // Disable animation to reduce flakiness.
             mManager = mBookmarkActivity.getManagerForTesting();
         }
@@ -367,14 +368,16 @@
 
         // Start searching without entering a query.
         TestThreadUtils.runOnUiThreadBlocking(manager::openSearchUI);
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
         Assert.assertEquals("Wrong state, should be searching", BookmarkUIState.STATE_SEARCHING,
                 manager.getCurrentState());
 
         // Select the folder and delete it.
+        toggleSelectionAndEndAnimation(testFolder,
+                (BookmarkRow) mItemsContainer.findViewHolderForLayoutPosition(2).itemView);
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> manager.getSelectionDelegate().toggleSelectionForItem(getIdByPosition(0)));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> manager.getToolbarForTests().onMenuItemClick(
+                ()
+                        -> manager.getToolbarForTests().onMenuItemClick(
                                 manager.getToolbarForTests().getMenu().findItem(
                                         R.id.selection_mode_delete_menu_id)));
 
@@ -418,7 +421,7 @@
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
     public void testBookmarkFolderIcon(boolean nightModeEnabled) throws Exception {
         BookmarkPromoHeader.forcePromoStateForTests(BookmarkPromoHeader.PromoState.PROMO_NONE);
-        addFolder(TEST_FOLDER_TITLE);
+        BookmarkId testId = addFolder(TEST_FOLDER_TITLE);
         openBookmarkManager();
 
         RecyclerView.Adapter adapter = getAdapter();
@@ -430,21 +433,14 @@
                                        .findViewHolderForAdapterPosition(0)
                                        .itemView;
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            itemView.performLongClick();
-            itemView.endAnimationsForTests();
-            manager.getToolbarForTests().endAnimationsForTesting();
-        });
+        toggleSelectionAndEndAnimation(getIdByPosition(0), itemView);
 
-        // Callback occurs when Item "test" is selected.
+        // Make sure the Item "test" is selected.
         CriteriaHelper.pollUiThread(
                 itemView::isChecked, "Expected item \"test\" to become selected");
 
         mRenderTestRule.render(manager.getView(), "bookmark_manager_folder_selected");
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> manager.getSelectionDelegate().toggleSelectionForItem(getIdByPosition(0)));
-
+        toggleSelectionAndEndAnimation(getIdByPosition(0), itemView);
         mRenderTestRule.render(manager.getView(), "bookmark_manager_one_folder");
     }
 
@@ -511,6 +507,15 @@
         });
     }
 
+    protected void toggleSelectionAndEndAnimation(BookmarkId id, BookmarkRow view) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mManager.getSelectionDelegate().toggleSelectionForItem(id);
+            view.endAnimationsForTests();
+            mManager.getToolbarForTests().endAnimationsForTesting();
+        });
+        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
+    }
+
     protected BookmarkId addBookmark(final String title, final String url)
             throws InterruptedException, ExecutionException {
         readPartnerBookmarks();
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 ee49b73..3983bfd 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
@@ -15,9 +15,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -57,8 +54,6 @@
 import org.chromium.content_public.browser.test.util.UiUtils;
 import org.chromium.policy.test.annotations.Policies;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.text.NumberFormat;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -86,22 +81,6 @@
         return (Preferences) activity;
     }
 
-    public static void clickPreference(PreferenceFragment fragment, Preference preference) {
-        try {
-            Method performClick = Preference.class.getDeclaredMethod("performClick",
-                    PreferenceScreen.class);
-            performClick.invoke(preference, fragment.getPreferenceScreen());
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalArgumentException e) {
-            throw new RuntimeException(e);
-        } catch (NoSuchMethodException e) {
-            throw new RuntimeException(e);
-        } catch (InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     /**
      * Change search engine and make sure it works correctly.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
index f92979d..a040edb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.signin;
 
-import android.app.Activity;
 import android.app.Instrumentation.ActivityMonitor;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -13,6 +12,7 @@
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
 import android.support.v7.preference.Preference;
 import android.widget.Button;
 
@@ -406,9 +406,9 @@
         signOutPref.getOnPreferenceClickListener().onPreferenceClick(signOutPref);
     }
 
-    private void acceptAlertDialogWithTag(Activity activity, String tag) {
+    private void acceptAlertDialogWithTag(AppCompatActivity activity, String tag) {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        DialogFragment fragment = ActivityUtils.waitForFragment(activity, tag);
+        DialogFragment fragment = ActivityUtils.waitForFragmentCompat(activity, tag);
         AlertDialog dialog = (AlertDialog) fragment.getDialog();
         Assert.assertTrue(dialog != null);
         Assert.assertTrue(dialog.isShowing());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
index 1ce5e78..8ae0ee6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -15,6 +15,7 @@
 public class FakeProfileSyncService extends ProfileSyncService {
     private boolean mEngineInitialized;
     private int mNumberOfSyncedDevices;
+    private boolean mPassphraseRequiredForDecryption;
     private Set<Integer> mChosenTypes = new HashSet<>();
 
     public FakeProfileSyncService() {
@@ -53,4 +54,13 @@
     public Set<Integer> getPreferredDataTypes() {
         return mChosenTypes;
     }
+
+    @Override
+    public boolean isPassphraseRequiredForDecryption() {
+        return mPassphraseRequiredForDecryption;
+    }
+
+    public void setPassphraseRequiredForDecryption(boolean passphraseRequiredForDecryption) {
+        mPassphraseRequiredForDecryption = passphraseRequiredForDecryption;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java
new file mode 100644
index 0000000..22554333
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncPreferencesTest.java
@@ -0,0 +1,551 @@
+// Copyright 2019 The Chromium 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.sync;
+
+import android.app.Dialog;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.widget.Button;
+import android.widget.EditText;
+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.DisabledTest;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill.PersonalDataManager;
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreferenceCompat;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.preferences.sync.ManageSyncPreferences;
+import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
+import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
+import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.ActivityUtils;
+import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
+import org.chromium.components.sync.ModelType;
+import org.chromium.components.sync.Passphrase;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for ManageSyncPreferences.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class ManageSyncPreferencesTest {
+    private static final String TAG = "ManageSyncPreferencesTest";
+
+    /**
+     * Maps ModelTypes to their UI element IDs.
+     */
+    private static final Map<Integer, String> UI_DATATYPES = new HashMap<>();
+
+    static {
+        UI_DATATYPES.put(ModelType.AUTOFILL, ManageSyncPreferences.PREF_SYNC_AUTOFILL);
+        UI_DATATYPES.put(ModelType.BOOKMARKS, ManageSyncPreferences.PREF_SYNC_BOOKMARKS);
+        UI_DATATYPES.put(ModelType.TYPED_URLS, ManageSyncPreferences.PREF_SYNC_HISTORY);
+        UI_DATATYPES.put(ModelType.PASSWORDS, ManageSyncPreferences.PREF_SYNC_PASSWORDS);
+        UI_DATATYPES.put(ModelType.PROXY_TABS, ManageSyncPreferences.PREF_SYNC_RECENT_TABS);
+        UI_DATATYPES.put(ModelType.PREFERENCES, ManageSyncPreferences.PREF_SYNC_SETTINGS);
+    }
+
+    private Preferences mPreferences;
+
+    @Rule
+    public SyncTestRule mSyncTestRule = new SyncTestRule();
+
+    @After
+    public void tearDown() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> ProfileSyncService.resetForTests());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testSyncEverythingAndDataTypes() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        Collection<CheckBoxPreference> dataTypes = getDataTypes(fragment).values();
+
+        assertSyncOnState(fragment);
+        mSyncTestRule.togglePreference(syncEverything);
+        // When syncEverything is toggled off all data types are checked and enabled by default.
+        // User needs to manually uncheck to toggle sync off for data types.
+        for (CheckBoxPreference dataType : dataTypes) {
+            Assert.assertTrue(dataType.isChecked());
+            Assert.assertTrue(dataType.isEnabled());
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testSettingDataTypes() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        Map<Integer, CheckBoxPreference> dataTypes = getDataTypes(fragment);
+
+        assertSyncOnState(fragment);
+        mSyncTestRule.togglePreference(syncEverything);
+        for (CheckBoxPreference dataType : dataTypes.values()) {
+            Assert.assertTrue(dataType.isChecked());
+            Assert.assertTrue(dataType.isEnabled());
+        }
+
+        Set<Integer> expectedTypes = new HashSet<>(dataTypes.keySet());
+        assertChosenDataTypesAre(expectedTypes);
+        mSyncTestRule.togglePreference(dataTypes.get(ModelType.AUTOFILL));
+        mSyncTestRule.togglePreference(dataTypes.get(ModelType.PASSWORDS));
+        expectedTypes.remove(ModelType.AUTOFILL);
+        expectedTypes.remove(ModelType.PASSWORDS);
+
+        closeFragment(fragment);
+        assertChosenDataTypesAre(expectedTypes);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationChecked() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(true);
+
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        assertSyncOnState(fragment);
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+
+        Assert.assertFalse(paymentsIntegration.isEnabled());
+        Assert.assertTrue(paymentsIntegration.isChecked());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationUnchecked() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(false);
+
+        mSyncTestRule.setChosenDataTypes(false, UI_DATATYPES.keySet());
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+
+        // All data types are enabled by default as syncEverything is toggled off.
+        Assert.assertTrue(paymentsIntegration.isEnabled());
+        Assert.assertFalse(paymentsIntegration.isChecked());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationCheckboxDisablesPaymentsIntegration() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(true);
+
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        assertSyncOnState(fragment);
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        mSyncTestRule.togglePreference(syncEverything);
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+        mSyncTestRule.togglePreference(paymentsIntegration);
+
+        closeFragment(fragment);
+        assertPaymentsIntegrationEnabled(false);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationCheckboxEnablesPaymentsIntegration() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(false);
+
+        mSyncTestRule.setChosenDataTypes(false, UI_DATATYPES.keySet());
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+        mSyncTestRule.togglePreference(paymentsIntegration);
+
+        closeFragment(fragment);
+        assertPaymentsIntegrationEnabled(true);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationCheckboxClearsServerAutofillCreditCards() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(true);
+
+        Assert.assertFalse(
+                "There should be no server cards", mSyncTestRule.hasServerAutofillCreditCards());
+        mSyncTestRule.addServerAutofillCreditCard();
+        Assert.assertTrue(
+                "There should be server cards", mSyncTestRule.hasServerAutofillCreditCards());
+
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        assertSyncOnState(fragment);
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        mSyncTestRule.togglePreference(syncEverything);
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+        mSyncTestRule.togglePreference(paymentsIntegration);
+
+        closeFragment(fragment);
+        assertPaymentsIntegrationEnabled(false);
+
+        Assert.assertFalse("There should be no server cards remaining",
+                mSyncTestRule.hasServerAutofillCreditCards());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationDisabledByAutofillSyncCheckbox() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(true);
+
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        assertSyncOnState(fragment);
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        mSyncTestRule.togglePreference(syncEverything);
+
+        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_AUTOFILL);
+        mSyncTestRule.togglePreference(syncAutofill);
+
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+        Assert.assertFalse(paymentsIntegration.isEnabled());
+        Assert.assertFalse(paymentsIntegration.isChecked());
+
+        closeFragment(fragment);
+        assertPaymentsIntegrationEnabled(false);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPaymentsIntegrationEnabledBySyncEverything() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(false);
+        mSyncTestRule.disableDataType(ModelType.AUTOFILL);
+
+        // Get the UI elements.
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_AUTOFILL);
+        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_PAYMENTS_INTEGRATION);
+
+        // All three are unchecked and payments is disabled.
+        Assert.assertFalse(syncEverything.isChecked());
+        Assert.assertFalse(syncAutofill.isChecked());
+        Assert.assertTrue(syncAutofill.isEnabled());
+        Assert.assertFalse(paymentsIntegration.isChecked());
+        Assert.assertFalse(paymentsIntegration.isEnabled());
+
+        // All three are checked after toggling sync everything.
+        mSyncTestRule.togglePreference(syncEverything);
+        Assert.assertTrue(syncEverything.isChecked());
+        Assert.assertTrue(syncAutofill.isChecked());
+        Assert.assertFalse(syncAutofill.isEnabled());
+        Assert.assertTrue(paymentsIntegration.isChecked());
+        Assert.assertFalse(paymentsIntegration.isEnabled());
+
+        // Closing the fragment enabled payments integration.
+        closeFragment(fragment);
+        assertPaymentsIntegrationEnabled(true);
+    }
+
+    /**
+     * Test that choosing a passphrase type while sync is off doesn't crash.
+     *
+     * This is a regression test for http://crbug.com/507557.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testChoosePassphraseTypeWhenSyncIsOff() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        ManageSyncPreferences fragment = startManageSyncPreferences();
+        Preference encryption = getEncryption(fragment);
+        clickPreference(encryption);
+
+        final PassphraseTypeDialogFragment typeFragment = getPassphraseTypeDialogFragment();
+        mSyncTestRule.stopSync();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { typeFragment.onItemClick(null, null, 0, Passphrase.Type.CUSTOM); });
+        // No crash means we passed.
+    }
+
+    /**
+     * Test that entering a passphrase while sync is off doesn't crash.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testEnterPassphraseWhenSyncIsOff() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        mSyncTestRule.stopSync();
+        TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> fragment.onPassphraseEntered("passphrase"));
+        // No crash means we passed.
+    }
+
+    /**
+     * Test that triggering OnPassphraseAccepted dismisses PassphraseDialogFragment.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    @DisabledTest(message = "https://crbug.com/986243")
+    public void testPassphraseDialogDismissed() {
+        final FakeProfileSyncService pss = overrideProfileSyncService();
+
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        // Trigger PassphraseDialogFragment to be shown when taping on Encryption.
+        pss.setPassphraseRequiredForDecryption(true);
+
+        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        Preference encryption = getEncryption(fragment);
+        clickPreference(encryption);
+
+        final PassphraseDialogFragment passphraseFragment = getPassphraseDialogFragment();
+        Assert.assertTrue(passphraseFragment.isAdded());
+
+        // Simulate OnPassphraseAccepted from external event by setting PassphraseRequired to false
+        // and triggering syncStateChanged().
+        // PassphraseDialogFragment should be dismissed.
+        pss.setPassphraseRequiredForDecryption(false);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            pss.syncStateChanged();
+            fragment.getFragmentManager().executePendingTransactions();
+            Assert.assertNull("PassphraseDialogFragment should be dismissed.",
+                    mPreferences.getFragmentManager().findFragmentByTag(
+                            ManageSyncPreferences.FRAGMENT_ENTER_PASSPHRASE));
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testPassphraseCreation() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        final ManageSyncPreferences fragment = startManageSyncPreferences();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> fragment.onPassphraseTypeSelected(Passphrase.Type.CUSTOM));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        PassphraseCreationDialogFragment pcdf = getPassphraseCreationDialogFragment();
+        AlertDialog dialog = (AlertDialog) pcdf.getDialog();
+        Button okButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
+        EditText enterPassphrase = (EditText) dialog.findViewById(R.id.passphrase);
+        EditText confirmPassphrase = (EditText) dialog.findViewById(R.id.confirm_passphrase);
+
+        // Error if you try to submit empty passphrase.
+        Assert.assertNull(confirmPassphrase.getError());
+        clickButton(okButton);
+        Assert.assertTrue(pcdf.isResumed());
+        Assert.assertNotNull(enterPassphrase.getError());
+        Assert.assertNull(confirmPassphrase.getError());
+
+        // Error if you try to submit with only the first box filled.
+        clearError(confirmPassphrase);
+        setText(enterPassphrase, "foo");
+        clickButton(okButton);
+        Assert.assertTrue(pcdf.isResumed());
+        Assert.assertNull(enterPassphrase.getError());
+        Assert.assertNotNull(confirmPassphrase.getError());
+
+        // Remove first box should only show empty error message
+        setText(enterPassphrase, "");
+        clickButton(okButton);
+        Assert.assertNotNull(enterPassphrase.getError());
+        Assert.assertNull(confirmPassphrase.getError());
+
+        // Error if you try to submit with only the second box filled.
+        clearError(confirmPassphrase);
+        setText(confirmPassphrase, "foo");
+        clickButton(okButton);
+        Assert.assertTrue(pcdf.isResumed());
+        Assert.assertNull(enterPassphrase.getError());
+        Assert.assertNotNull(confirmPassphrase.getError());
+
+        // No error if text doesn't match without button press.
+        setText(enterPassphrase, "foo");
+        clearError(confirmPassphrase);
+        setText(confirmPassphrase, "bar");
+        Assert.assertNull(enterPassphrase.getError());
+        Assert.assertNull(confirmPassphrase.getError());
+
+        // Error if you try to submit unmatching text.
+        clearError(confirmPassphrase);
+        clickButton(okButton);
+        Assert.assertTrue(pcdf.isResumed());
+        Assert.assertNull(enterPassphrase.getError());
+        Assert.assertNotNull(confirmPassphrase.getError());
+
+        // Success if text matches.
+        setText(confirmPassphrase, "foo");
+        clickButton(okButton);
+        Assert.assertFalse(pcdf.isResumed());
+    }
+
+    private FakeProfileSyncService overrideProfileSyncService() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            // PSS has to be constructed on the UI thread.
+            FakeProfileSyncService fakeProfileSyncService = new FakeProfileSyncService();
+            ProfileSyncService.overrideForTests(fakeProfileSyncService);
+            return fakeProfileSyncService;
+        });
+    }
+
+    private ManageSyncPreferences startManageSyncPreferences() {
+        mPreferences = mSyncTestRule.startPreferences(ManageSyncPreferences.class.getName());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        return (ManageSyncPreferences) mPreferences.getMainFragmentCompat();
+    }
+
+    private void closeFragment(ManageSyncPreferences fragment) {
+        FragmentTransaction transaction =
+                mPreferences.getSupportFragmentManager().beginTransaction();
+        transaction.remove(fragment);
+        transaction.commit();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private ChromeSwitchPreferenceCompat getSyncEverything(ManageSyncPreferences fragment) {
+        return (ChromeSwitchPreferenceCompat) fragment.findPreference(
+                ManageSyncPreferences.PREF_SYNC_EVERYTHING);
+    }
+
+    private Map<Integer, CheckBoxPreference> getDataTypes(ManageSyncPreferences fragment) {
+        Map<Integer, CheckBoxPreference> dataTypes = new HashMap<>();
+        for (Map.Entry<Integer, String> uiDataType : UI_DATATYPES.entrySet()) {
+            Integer modelType = uiDataType.getKey();
+            String prefId = uiDataType.getValue();
+            dataTypes.put(modelType, (CheckBoxPreference) fragment.findPreference(prefId));
+        }
+        return dataTypes;
+    }
+
+    private Preference getGoogleActivityControls(ManageSyncPreferences fragment) {
+        return fragment.findPreference(ManageSyncPreferences.PREF_GOOGLE_ACTIVITY_CONTROLS);
+    }
+
+    private Preference getEncryption(ManageSyncPreferences fragment) {
+        return fragment.findPreference(ManageSyncPreferences.PREF_ENCRYPTION);
+    }
+
+    private Preference getManageData(ManageSyncPreferences fragment) {
+        return fragment.findPreference(ManageSyncPreferences.PREF_SYNC_MANAGE_DATA);
+    }
+
+    private PassphraseDialogFragment getPassphraseDialogFragment() {
+        return ActivityUtils.waitForFragmentCompat(
+                mPreferences, ManageSyncPreferences.FRAGMENT_ENTER_PASSPHRASE);
+    }
+
+    private PassphraseTypeDialogFragment getPassphraseTypeDialogFragment() {
+        return ActivityUtils.waitForFragmentCompat(
+                mPreferences, ManageSyncPreferences.FRAGMENT_PASSPHRASE_TYPE);
+    }
+
+    private PassphraseCreationDialogFragment getPassphraseCreationDialogFragment() {
+        return ActivityUtils.waitForFragmentCompat(
+                mPreferences, ManageSyncPreferences.FRAGMENT_CUSTOM_PASSPHRASE);
+    }
+
+    private void assertSyncOnState(ManageSyncPreferences fragment) {
+        ChromeSwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
+        Assert.assertTrue("The sync everything switch should be on.", syncEverything.isChecked());
+        Assert.assertTrue(
+                "The sync everything switch should be enabled.", syncEverything.isEnabled());
+        for (CheckBoxPreference dataType : getDataTypes(fragment).values()) {
+            String key = dataType.getKey();
+            Assert.assertTrue("Data type " + key + " should be checked.", dataType.isChecked());
+            Assert.assertFalse("Data type " + key + " should be disabled.", dataType.isEnabled());
+        }
+        Assert.assertTrue("The google activity controls button should always be enabled.",
+                getGoogleActivityControls(fragment).isEnabled());
+        Assert.assertTrue("The encryption button should always be enabled.",
+                getEncryption(fragment).isEnabled());
+        Assert.assertTrue("The manage sync data button should be always enabled.",
+                getManageData(fragment).isEnabled());
+    }
+
+    private void assertChosenDataTypesAre(final Set<Integer> enabledDataTypes) {
+        final Set<Integer> disabledDataTypes = new HashSet<>(UI_DATATYPES.keySet());
+        disabledDataTypes.removeAll(enabledDataTypes);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Set<Integer> actualDataTypes =
+                    mSyncTestRule.getProfileSyncService().getChosenDataTypes();
+            Assert.assertTrue(actualDataTypes.containsAll(enabledDataTypes));
+            Assert.assertTrue(Collections.disjoint(disabledDataTypes, actualDataTypes));
+        });
+    }
+
+    private void assertPaymentsIntegrationEnabled(final boolean enabled) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals(enabled, PersonalDataManager.isPaymentsIntegrationEnabled());
+        });
+    }
+
+    private void clickPreference(final Preference pref) {
+        TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> pref.getOnPreferenceClickListener().onPreferenceClick(pref));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private void clickButton(final Button button) {
+        TestThreadUtils.runOnUiThreadBlocking((Runnable) button::performClick);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private void setText(final TextView textView, final String text) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> textView.setText(text));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private void clearError(final TextView textView) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> textView.setError(null));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java
new file mode 100644
index 0000000..28f25380
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesPreferencesTest.java
@@ -0,0 +1,178 @@
+// Copyright 2019 The Chromium 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.sync;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v4.app.FragmentTransaction;
+
+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.Feature;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreferenceCompat;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.preferences.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
+import org.chromium.components.sync.AndroidSyncSettings;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+/**
+ * Tests for SyncAndServicesPreferences.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class SyncAndServicesPreferencesTest {
+    private static final String TAG = "SyncAndServicesPreferencesTest";
+
+    @Rule
+    public SyncTestRule mSyncTestRule = new SyncTestRule();
+
+    private Preferences mPreferences;
+
+    @After
+    public void tearDown() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> ProfileSyncService.resetForTests());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testSyncSwitch() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        final ChromeSwitchPreferenceCompat syncSwitch = getSyncSwitch(fragment);
+
+        Assert.assertTrue(syncSwitch.isChecked());
+        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
+        mSyncTestRule.togglePreference(syncSwitch);
+        Assert.assertFalse(syncSwitch.isChecked());
+        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
+        mSyncTestRule.togglePreference(syncSwitch);
+        Assert.assertTrue(syncSwitch.isChecked());
+        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
+    }
+
+    /**
+     * This is a regression test for http://crbug.com/454939.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testOpeningSettingsDoesntEnableSync() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.stopSync();
+        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        closeFragment(fragment);
+        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
+    }
+
+    /**
+     * This is a regression test for http://crbug.com/467600.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testOpeningSettingsDoesntStartEngine() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.stopSync();
+        startSyncAndServicesPreferences();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertFalse(mSyncTestRule.getProfileSyncService().isSyncRequested());
+        });
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testDefaultControlStatesWithSyncOffThenOn() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.stopSync();
+        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        assertSyncOffState(fragment);
+        mSyncTestRule.togglePreference(getSyncSwitch(fragment));
+        SyncTestUtil.waitForEngineInitialized();
+        assertSyncOnState(fragment);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testDefaultControlStatesWithSyncOnThenOff() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        SyncTestUtil.waitForSyncActive();
+        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        assertSyncOnState(fragment);
+        mSyncTestRule.togglePreference(getSyncSwitch(fragment));
+        assertSyncOffState(fragment);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Sync"})
+    public void testSyncSwitchClearsServerAutofillCreditCards() {
+        mSyncTestRule.setUpTestAccountAndSignIn();
+        mSyncTestRule.setPaymentsIntegrationEnabled(true);
+
+        Assert.assertFalse(
+                "There should be no server cards", mSyncTestRule.hasServerAutofillCreditCards());
+        mSyncTestRule.addServerAutofillCreditCard();
+        Assert.assertTrue(
+                "There should be server cards", mSyncTestRule.hasServerAutofillCreditCards());
+
+        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
+        SyncAndServicesPreferences fragment = startSyncAndServicesPreferences();
+        assertSyncOnState(fragment);
+        ChromeSwitchPreferenceCompat syncSwitch = getSyncSwitch(fragment);
+        Assert.assertTrue(syncSwitch.isChecked());
+        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
+        mSyncTestRule.togglePreference(syncSwitch);
+        Assert.assertFalse(syncSwitch.isChecked());
+        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
+
+        closeFragment(fragment);
+
+        Assert.assertFalse("There should be no server cards remaining",
+                mSyncTestRule.hasServerAutofillCreditCards());
+    }
+
+    private SyncAndServicesPreferences startSyncAndServicesPreferences() {
+        mPreferences = mSyncTestRule.startPreferences(SyncAndServicesPreferences.class.getName());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        return (SyncAndServicesPreferences) mPreferences.getMainFragmentCompat();
+    }
+
+    private void closeFragment(SyncAndServicesPreferences fragment) {
+        FragmentTransaction transaction =
+                mPreferences.getSupportFragmentManager().beginTransaction();
+        transaction.remove(fragment);
+        transaction.commit();
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private ChromeSwitchPreferenceCompat getSyncSwitch(SyncAndServicesPreferences fragment) {
+        return (ChromeSwitchPreferenceCompat) fragment.findPreference(
+                SyncAndServicesPreferences.PREF_SYNC_REQUESTED);
+    }
+
+    private void assertSyncOnState(SyncAndServicesPreferences fragment) {
+        Assert.assertTrue("The sync switch should be on.", getSyncSwitch(fragment).isChecked());
+        Assert.assertTrue(
+                "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
+    }
+
+    private void assertSyncOffState(SyncAndServicesPreferences fragment) {
+        Assert.assertFalse("The sync switch should be off.", getSyncSwitch(fragment).isChecked());
+        Assert.assertTrue(
+                "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
deleted file mode 100644
index fe7e226..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
+++ /dev/null
@@ -1,844 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.sync;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.CheckBoxPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.SwitchPreferenceCompat;
-import android.support.v7.preference.TwoStatePreference;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Ignore;
-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.DisableIf;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.autofill.PersonalDataManager;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.preferences.Preferences;
-import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
-import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
-import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
-import org.chromium.chrome.browser.sync.ui.SyncCustomizationFragment;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.ActivityUtils;
-import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
-import org.chromium.components.sync.AndroidSyncSettings;
-import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.Passphrase;
-import org.chromium.components.sync.protocol.AutofillWalletSpecifics;
-import org.chromium.components.sync.protocol.EntitySpecifics;
-import org.chromium.components.sync.protocol.SyncEntity;
-import org.chromium.components.sync.protocol.WalletMaskedCreditCard;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Tests for SyncCustomizationFragment.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@SuppressLint("UseSparseArrays")
-@Ignore("TODO(crbug.com/890847): Adapt these tests for SyncAndServicesPreferences.")
-public class SyncCustomizationFragmentTest {
-    @Rule
-    public SyncTestRule mSyncTestRule = new SyncTestRule();
-
-    private static final String TAG = "SyncCustomizationFragmentTest";
-
-    /**
-     * Fake ProfileSyncService for test to control the value returned from
-     * isPassphraseRequiredForDecryption.
-     */
-    private class FakeProfileSyncService extends ProfileSyncService {
-        private boolean mPassphraseRequiredForDecryption;
-
-        FakeProfileSyncService() {
-            super();
-            setMasterSyncEnabledProvider(() -> AndroidSyncSettings.get().isMasterSyncEnabled());
-        }
-
-        @Override
-        public boolean isPassphraseRequiredForDecryption() {
-            return mPassphraseRequiredForDecryption;
-        }
-
-        void setPassphraseRequiredForDecryption(boolean passphraseRequiredForDecryption) {
-            mPassphraseRequiredForDecryption = passphraseRequiredForDecryption;
-        }
-    }
-
-    /**
-     * Maps ModelTypes to their UI element IDs.
-     */
-    private static final Map<Integer, String> UI_DATATYPES;
-
-    static {
-        UI_DATATYPES = new HashMap<>();
-        UI_DATATYPES.put(ModelType.AUTOFILL, SyncCustomizationFragment.PREFERENCE_SYNC_AUTOFILL);
-        UI_DATATYPES.put(ModelType.BOOKMARKS, SyncCustomizationFragment.PREFERENCE_SYNC_BOOKMARKS);
-        UI_DATATYPES.put(ModelType.TYPED_URLS, SyncCustomizationFragment.PREFERENCE_SYNC_OMNIBOX);
-        UI_DATATYPES.put(ModelType.PASSWORDS, SyncCustomizationFragment.PREFERENCE_SYNC_PASSWORDS);
-        UI_DATATYPES.put(
-                ModelType.PROXY_TABS, SyncCustomizationFragment.PREFERENCE_SYNC_RECENT_TABS);
-        UI_DATATYPES.put(ModelType.PREFERENCES, SyncCustomizationFragment.PREFERENCE_SYNC_SETTINGS);
-    }
-
-    private Preferences mPreferences;
-
-    @Before
-    public void setUp() throws Exception {
-        mPreferences = null;
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(() -> ProfileSyncService.resetForTests());
-    }
-
-    /**
-     * Minimal test fixture to debug flakiness in the actual tests.
-     * TODO(crbug.com/879246): Remove after investigation.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSetupOnly() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-    }
-
-    /**
-     * Minimal test fixture to debug flakiness in the actual tests.
-     * TODO(crbug.com/879246): Remove after investigation.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSetupAndWaitForSyncActive() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSyncSwitch() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        final SwitchPreferenceCompat syncSwitch = getSyncSwitch(fragment);
-
-        Assert.assertTrue(syncSwitch.isChecked());
-        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
-        togglePreference(syncSwitch);
-        Assert.assertFalse(syncSwitch.isChecked());
-        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
-        togglePreference(syncSwitch);
-        Assert.assertTrue(syncSwitch.isChecked());
-        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
-    }
-
-    /**
-     * This is a regression test for http://crbug.com/454939.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testOpeningSettingsDoesntEnableSync() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        mSyncTestRule.stopSync();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        closeFragment(fragment);
-        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
-    }
-
-    /**
-     * This is a regression test for http://crbug.com/467600.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisabledTest
-    public void testOpeningSettingsDoesntStartEngine() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        mSyncTestRule.stopSync();
-        startSyncCustomizationFragment();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Assert.assertFalse(mSyncTestRule.getProfileSyncService().isSyncRequested());
-            Assert.assertFalse(mSyncTestRule.getProfileSyncService().isEngineInitialized());
-        });
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisabledTest
-    public void testDefaultControlStatesWithSyncOffThenOn() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        mSyncTestRule.stopSync();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOffState(fragment);
-        togglePreference(getSyncSwitch(fragment));
-        SyncTestUtil.waitForEngineInitialized();
-        assertDefaultSyncOnState(fragment);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testDefaultControlStatesWithSyncOnThenOff() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        togglePreference(getSyncSwitch(fragment));
-        assertDefaultSyncOffState(fragment);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSyncEverythingAndDataTypes() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        Collection<CheckBoxPreference> dataTypes = getDataTypes(fragment).values();
-
-        assertDefaultSyncOnState(fragment);
-        togglePreference(syncEverything);
-        for (CheckBoxPreference dataType : dataTypes) {
-            Assert.assertTrue(dataType.isChecked());
-            Assert.assertTrue(dataType.isEnabled());
-        }
-
-        // If all data types are unchecked, sync should turn off.
-        for (CheckBoxPreference dataType : dataTypes) {
-            togglePreference(dataType);
-        }
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        assertDefaultSyncOffState(fragment);
-        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSettingDataTypes() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        Map<Integer, CheckBoxPreference> dataTypes = getDataTypes(fragment);
-
-        assertDefaultSyncOnState(fragment);
-        togglePreference(syncEverything);
-        for (CheckBoxPreference dataType : dataTypes.values()) {
-            Assert.assertTrue(dataType.isChecked());
-            Assert.assertTrue(dataType.isEnabled());
-        }
-
-        Set<Integer> expectedTypes = new HashSet<>(dataTypes.keySet());
-        expectedTypes.add(ModelType.PREFERENCES);
-        expectedTypes.add(ModelType.PRIORITY_PREFERENCES);
-        assertDataTypesAre(expectedTypes);
-        togglePreference(dataTypes.get(ModelType.AUTOFILL));
-        togglePreference(dataTypes.get(ModelType.PASSWORDS));
-        // Nothing should have changed before the fragment closes.
-        assertDataTypesAre(expectedTypes);
-
-        closeFragment(fragment);
-        expectedTypes.remove(ModelType.AUTOFILL);
-        expectedTypes.remove(ModelType.PASSWORDS);
-        assertDataTypesAre(expectedTypes);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationChecked() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(true);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-
-        Assert.assertFalse(paymentsIntegration.isEnabled());
-        Assert.assertTrue(paymentsIntegration.isChecked());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationUnchecked() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(false);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-
-        Assert.assertTrue(paymentsIntegration.isEnabled());
-        Assert.assertFalse(paymentsIntegration.isChecked());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationCheckboxDisablesPaymentsIntegration() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(true);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-        togglePreference(paymentsIntegration);
-
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(false);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisableIf.Build(message = "crbug.com/899989 ", sdk_is_less_than = Build.VERSION_CODES.M)
-    public void testPaymentsIntegrationCheckboxEnablesPaymentsIntegration() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(false);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-        togglePreference(paymentsIntegration);
-
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(true);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisabledTest(message = "crbug.com/955490")
-    public void testPaymentsIntegrationCheckboxClearsServerAutofillCreditCards() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(true);
-
-        Assert.assertFalse("There should be no server cards", hasServerAutofillCreditCards());
-        addServerAutofillCreditCard();
-        Assert.assertTrue("There should be server cards", hasServerAutofillCreditCards());
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-        togglePreference(paymentsIntegration);
-
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(false);
-
-        Assert.assertFalse(
-                "There should be no server cards remaining", hasServerAutofillCreditCards());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisabledTest(message = "crbug.com/951141")
-    public void testSyncSwitchClearsServerAutofillCreditCards() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(true);
-
-        Assert.assertFalse("There should be no server cards", hasServerAutofillCreditCards());
-        addServerAutofillCreditCard();
-        Assert.assertTrue("There should be server cards", hasServerAutofillCreditCards());
-
-        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncSwitch = getSyncSwitch(fragment);
-        Assert.assertTrue(syncSwitch.isChecked());
-        Assert.assertTrue(AndroidSyncSettings.get().isChromeSyncEnabled());
-        togglePreference(syncSwitch);
-        Assert.assertFalse(syncSwitch.isChecked());
-        Assert.assertFalse(AndroidSyncSettings.get().isChromeSyncEnabled());
-
-        closeFragment(fragment);
-
-        Assert.assertFalse(
-                "There should be no server cards remaining", hasServerAutofillCreditCards());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationDisabledByAutofillSyncCheckbox() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(true);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_SYNC_AUTOFILL);
-        togglePreference(syncAutofill);
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-        Assert.assertFalse(paymentsIntegration.isEnabled());
-        Assert.assertFalse(paymentsIntegration.isChecked());
-
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(false);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationEnabledByAutofillSyncCheckbox() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-
-        setPaymentsIntegrationEnabled(false);
-
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        assertDefaultSyncOnState(fragment);
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        togglePreference(syncEverything);
-
-        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_SYNC_AUTOFILL);
-        togglePreference(syncAutofill); // Disable autofill sync.
-        togglePreference(syncAutofill); // Re-enable autofill sync again.
-
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-        Assert.assertTrue(paymentsIntegration.isEnabled());
-        Assert.assertTrue(paymentsIntegration.isChecked());
-
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(true);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testPaymentsIntegrationEnabledBySyncEverything() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        setPaymentsIntegrationEnabled(false);
-        mSyncTestRule.disableDataType(ModelType.AUTOFILL);
-
-        // Get the UI elements.
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        CheckBoxPreference syncAutofill = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_SYNC_AUTOFILL);
-        CheckBoxPreference paymentsIntegration = (CheckBoxPreference) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_PAYMENTS_INTEGRATION);
-
-        // All three are unchecked and payments is disabled.
-        Assert.assertFalse(syncEverything.isChecked());
-        Assert.assertFalse(syncAutofill.isChecked());
-        Assert.assertTrue(syncAutofill.isEnabled());
-        Assert.assertFalse(paymentsIntegration.isChecked());
-        Assert.assertFalse(paymentsIntegration.isEnabled());
-
-        // All three are checked after toggling sync everything.
-        togglePreference(syncEverything);
-        Assert.assertTrue(syncEverything.isChecked());
-        Assert.assertTrue(syncAutofill.isChecked());
-        Assert.assertFalse(syncAutofill.isEnabled());
-        Assert.assertTrue(paymentsIntegration.isChecked());
-        Assert.assertFalse(paymentsIntegration.isEnabled());
-
-        // Closing the fragment enabled payments integration.
-        closeFragment(fragment);
-        assertPaymentsIntegrationEnabled(true);
-    }
-
-    /**
-     * Test that choosing a passphrase type while sync is off doesn't crash.
-     *
-     * This is a regression test for http://crbug.com/507557.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testChoosePassphraseTypeWhenSyncIsOff() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        Preference encryption = getEncryption(fragment);
-        clickPreference(encryption);
-
-        final PassphraseTypeDialogFragment typeFragment = getPassphraseTypeDialogFragment();
-        mSyncTestRule.stopSync();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { typeFragment.onItemClick(null, null, 0, Passphrase.Type.CUSTOM); });
-        // No crash means we passed.
-    }
-
-    /**
-     * Test that entering a passphrase while sync is off doesn't crash.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testEnterPassphraseWhenSyncIsOff() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        final SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        mSyncTestRule.stopSync();
-        TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> fragment.onPassphraseEntered("passphrase"));
-        // No crash means we passed.
-    }
-
-    /**
-     * Test that triggering OnPassphraseAccepted dismisses PassphraseDialogFragment.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisabledTest
-    public void testPassphraseDialogDismissed() {
-        final FakeProfileSyncService pss = overrideProfileSyncService();
-
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        // Trigger PassphraseDialogFragment to be shown when taping on Encryption.
-        pss.setPassphraseRequiredForDecryption(true);
-
-        final SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        Preference encryption = getEncryption(fragment);
-        clickPreference(encryption);
-
-        final PassphraseDialogFragment passphraseFragment = getPassphraseDialogFragment();
-        Assert.assertTrue(passphraseFragment.isAdded());
-
-        // Simulate OnPassphraseAccepted from external event by setting PassphraseRequired to false
-        // and triggering syncStateChanged().
-        // PassphraseDialogFragment should be dismissed.
-        pss.setPassphraseRequiredForDecryption(false);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            pss.syncStateChanged();
-            fragment.getFragmentManager().executePendingTransactions();
-            Assert.assertNull("PassphraseDialogFragment should be dismissed.",
-                    mPreferences.getFragmentManager().findFragmentByTag(
-                            SyncCustomizationFragment.FRAGMENT_ENTER_PASSPHRASE));
-        });
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    @DisableIf.Build(message = "crbug.com/899989 ", sdk_is_less_than = Build.VERSION_CODES.M)
-    public void testPassphraseCreation() {
-        mSyncTestRule.setUpTestAccountAndSignIn();
-        SyncTestUtil.waitForSyncActive();
-        final SyncCustomizationFragment fragment = startSyncCustomizationFragment();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> fragment.onPassphraseTypeSelected(Passphrase.Type.CUSTOM));
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        PassphraseCreationDialogFragment pcdf = getPassphraseCreationDialogFragment();
-        AlertDialog dialog = (AlertDialog) pcdf.getDialog();
-        Button okButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
-        EditText enterPassphrase = (EditText) dialog.findViewById(R.id.passphrase);
-        EditText confirmPassphrase = (EditText) dialog.findViewById(R.id.confirm_passphrase);
-
-        // Error if you try to submit empty passphrase.
-        Assert.assertNull(confirmPassphrase.getError());
-        clickButton(okButton);
-        Assert.assertTrue(pcdf.isResumed());
-        Assert.assertNotNull(enterPassphrase.getError());
-        Assert.assertNull(confirmPassphrase.getError());
-
-        // Error if you try to submit with only the first box filled.
-        clearError(confirmPassphrase);
-        setText(enterPassphrase, "foo");
-        clickButton(okButton);
-        Assert.assertTrue(pcdf.isResumed());
-        Assert.assertNull(enterPassphrase.getError());
-        Assert.assertNotNull(confirmPassphrase.getError());
-
-        // Remove first box should only show empty error message
-        setText(enterPassphrase, "");
-        clickButton(okButton);
-        Assert.assertNotNull(enterPassphrase.getError());
-        Assert.assertNull(confirmPassphrase.getError());
-
-        // Error if you try to submit with only the second box filled.
-        clearError(confirmPassphrase);
-        setText(confirmPassphrase, "foo");
-        clickButton(okButton);
-        Assert.assertTrue(pcdf.isResumed());
-        Assert.assertNull(enterPassphrase.getError());
-        Assert.assertNotNull(confirmPassphrase.getError());
-
-        // No error if text doesn't match without button press.
-        setText(enterPassphrase, "foo");
-        clearError(confirmPassphrase);
-        setText(confirmPassphrase, "bar");
-        Assert.assertNull(enterPassphrase.getError());
-        Assert.assertNull(confirmPassphrase.getError());
-
-        // Error if you try to submit unmatching text.
-        clearError(confirmPassphrase);
-        clickButton(okButton);
-        Assert.assertTrue(pcdf.isResumed());
-        Assert.assertNull(enterPassphrase.getError());
-        Assert.assertNotNull(confirmPassphrase.getError());
-
-        // Success if text matches.
-        setText(confirmPassphrase, "foo");
-        clickButton(okButton);
-        Assert.assertFalse(pcdf.isResumed());
-    }
-
-    private FakeProfileSyncService overrideProfileSyncService() {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
-            // PSS has to be constructed on the UI thread.
-            FakeProfileSyncService fakeProfileSyncService = new FakeProfileSyncService();
-            ProfileSyncService.overrideForTests(fakeProfileSyncService);
-            return fakeProfileSyncService;
-        });
-    }
-
-    private SyncCustomizationFragment startSyncCustomizationFragment() {
-        mPreferences = mSyncTestRule.startPreferences(SyncCustomizationFragment.class.getName());
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        return (SyncCustomizationFragment) mPreferences.getMainFragmentCompat();
-    }
-
-    private void closeFragment(SyncCustomizationFragment fragment) {
-        FragmentTransaction transaction =
-                mPreferences.getSupportFragmentManager().beginTransaction();
-        transaction.remove(fragment);
-        transaction.commit();
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private SwitchPreferenceCompat getSyncSwitch(SyncCustomizationFragment fragment) {
-        return (SwitchPreferenceCompat) fragment.findPreference(
-                SyncCustomizationFragment.PREF_SYNC_SWITCH);
-    }
-
-    private SwitchPreferenceCompat getSyncEverything(SyncCustomizationFragment fragment) {
-        return (SwitchPreferenceCompat) fragment.findPreference(
-                SyncCustomizationFragment.PREFERENCE_SYNC_EVERYTHING);
-    }
-
-    private Map<Integer, CheckBoxPreference> getDataTypes(SyncCustomizationFragment fragment) {
-        Map<Integer, CheckBoxPreference> dataTypes = new HashMap<>();
-        for (Map.Entry<Integer, String> uiDataType : UI_DATATYPES.entrySet()) {
-            Integer modelType = uiDataType.getKey();
-            String prefId = uiDataType.getValue();
-            dataTypes.put(modelType, (CheckBoxPreference) fragment.findPreference(prefId));
-        }
-        return dataTypes;
-    }
-
-    private Preference getEncryption(SyncCustomizationFragment fragment) {
-        return fragment.findPreference(SyncCustomizationFragment.PREFERENCE_ENCRYPTION);
-    }
-
-    private Preference getManageData(SyncCustomizationFragment fragment) {
-        return fragment.findPreference(SyncCustomizationFragment.PREFERENCE_SYNC_MANAGE_DATA);
-    }
-
-    private PassphraseDialogFragment getPassphraseDialogFragment() {
-        return ActivityUtils.waitForFragmentCompat(
-                mPreferences, SyncCustomizationFragment.FRAGMENT_ENTER_PASSPHRASE);
-    }
-
-    private PassphraseTypeDialogFragment getPassphraseTypeDialogFragment() {
-        return ActivityUtils.waitForFragmentCompat(
-                mPreferences, SyncCustomizationFragment.FRAGMENT_PASSPHRASE_TYPE);
-    }
-
-    private PassphraseCreationDialogFragment getPassphraseCreationDialogFragment() {
-        return ActivityUtils.waitForFragmentCompat(
-                mPreferences, SyncCustomizationFragment.FRAGMENT_CUSTOM_PASSPHRASE);
-    }
-
-    private void assertDefaultSyncOnState(SyncCustomizationFragment fragment) {
-        Assert.assertTrue("The sync switch should be on.", getSyncSwitch(fragment).isChecked());
-        Assert.assertTrue(
-                "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        Assert.assertTrue("The sync everything switch should be on.", syncEverything.isChecked());
-        Assert.assertTrue(
-                "The sync everything switch should be enabled.", syncEverything.isEnabled());
-        for (CheckBoxPreference dataType : getDataTypes(fragment).values()) {
-            String key = dataType.getKey();
-            Assert.assertTrue("Data type " + key + " should be checked.", dataType.isChecked());
-            Assert.assertFalse("Data type " + key + " should be disabled.", dataType.isEnabled());
-        }
-        Assert.assertTrue(
-                "The encryption button should be enabled.", getEncryption(fragment).isEnabled());
-        Assert.assertTrue("The manage sync data button should be always enabled.",
-                getManageData(fragment).isEnabled());
-    }
-
-    private void assertDefaultSyncOffState(SyncCustomizationFragment fragment) {
-        Assert.assertFalse("The sync switch should be off.", getSyncSwitch(fragment).isChecked());
-        Assert.assertTrue(
-                "The sync switch should be enabled.", getSyncSwitch(fragment).isEnabled());
-        SwitchPreferenceCompat syncEverything = getSyncEverything(fragment);
-        Assert.assertTrue("The sync everything switch should be on.", syncEverything.isChecked());
-        Assert.assertFalse(
-                "The sync everything switch should be disabled.", syncEverything.isEnabled());
-        for (CheckBoxPreference dataType : getDataTypes(fragment).values()) {
-            String key = dataType.getKey();
-            Assert.assertTrue("Data type " + key + " should be checked.", dataType.isChecked());
-            Assert.assertFalse("Data type " + key + " should be disabled.", dataType.isEnabled());
-        }
-        Assert.assertFalse(
-                "The encryption button should be disabled.", getEncryption(fragment).isEnabled());
-        Assert.assertTrue("The manage sync data button should be always enabled.",
-                getManageData(fragment).isEnabled());
-    }
-
-    private void assertDataTypesAre(final Set<Integer> enabledDataTypes) {
-        final Set<Integer> disabledDataTypes = new HashSet<>(UI_DATATYPES.keySet());
-        disabledDataTypes.removeAll(enabledDataTypes);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Set<Integer> actualDataTypes =
-                    mSyncTestRule.getProfileSyncService().getPreferredDataTypes();
-            Assert.assertTrue(actualDataTypes.containsAll(enabledDataTypes));
-            Assert.assertTrue(Collections.disjoint(disabledDataTypes, actualDataTypes));
-        });
-    }
-
-    private void setPaymentsIntegrationEnabled(final boolean enabled) {
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> PersonalDataManager.setPaymentsIntegrationEnabled(enabled));
-    }
-
-    private void assertPaymentsIntegrationEnabled(final boolean enabled) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Assert.assertEquals(enabled, PersonalDataManager.isPaymentsIntegrationEnabled());
-        });
-    }
-
-    private void addServerAutofillCreditCard() {
-        final String serverId = "025eb937c022489eb8dc78cbaa969218";
-        WalletMaskedCreditCard card =
-                WalletMaskedCreditCard.newBuilder()
-                        .setId(serverId)
-                        .setStatus(WalletMaskedCreditCard.WalletCardStatus.VALID)
-                        .setNameOnCard("Jon Doe")
-                        .setType(WalletMaskedCreditCard.WalletCardType.UNKNOWN)
-                        .setLastFour("1111")
-                        .setExpMonth(11)
-                        .setExpYear(2020)
-                        .build();
-        AutofillWalletSpecifics wallet_specifics =
-                AutofillWalletSpecifics.newBuilder()
-                        .setType(AutofillWalletSpecifics.WalletInfoType.MASKED_CREDIT_CARD)
-                        .setMaskedCard(card)
-                        .build();
-        EntitySpecifics specifics =
-                EntitySpecifics.newBuilder().setAutofillWallet(wallet_specifics).build();
-        SyncEntity entity = SyncEntity.newBuilder()
-                                    .setName(serverId)
-                                    .setIdString(serverId)
-                                    .setSpecifics(specifics)
-                                    .build();
-        mSyncTestRule.getFakeServerHelper().setWalletData(entity);
-        SyncTestUtil.triggerSyncAndWaitForCompletion();
-    }
-
-    private boolean hasServerAutofillCreditCards() {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
-            List<CreditCard> cards = PersonalDataManager.getInstance().getCreditCardsForSettings();
-            for (int i = 0; i < cards.size(); i++) {
-                if (!cards.get(i).getIsLocal()) return true;
-            }
-            return false;
-        });
-    }
-
-    // UI interaction convenience methods.
-    private void togglePreference(final TwoStatePreference pref) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            boolean newValue = !pref.isChecked();
-            pref.getOnPreferenceChangeListener().onPreferenceChange(pref, newValue);
-            pref.setChecked(newValue);
-        });
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private void clickPreference(final Preference pref) {
-        TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> pref.getOnPreferenceClickListener().onPreferenceClick(pref));
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private void clickButton(final Button button) {
-        TestThreadUtils.runOnUiThreadBlocking((Runnable) button::performClick);
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private void setText(final TextView textView, final String text) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> textView.setText(text));
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private void clearError(final TextView textView) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> textView.setError(null));
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
index d40503a0..062a79a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
@@ -7,12 +7,15 @@
 import android.accounts.Account;
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.support.v7.preference.TwoStatePreference;
 
 import org.junit.Assert;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.autofill.PersonalDataManager;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator;
 import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory;
 import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator;
@@ -24,6 +27,10 @@
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.components.sync.ModelType;
+import org.chromium.components.sync.protocol.AutofillWalletSpecifics;
+import org.chromium.components.sync.protocol.EntitySpecifics;
+import org.chromium.components.sync.protocol.SyncEntity;
+import org.chromium.components.sync.protocol.WalletMaskedCreditCard;
 import org.chromium.components.sync.test.util.MockSyncContentResolverDelegate;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -74,6 +81,19 @@
     private ProfileSyncService mProfileSyncService;
     private MockSyncContentResolverDelegate mSyncContentResolver;
 
+    private void ruleSetUp() throws Throwable {
+        // This must be called before super.setUp() in order for test authentication to work.
+        SigninTestUtil.setUpAuthForTest();
+    }
+
+    private void ruleTearDown() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mProfileSyncService.requestStop();
+            FakeServerHelper.deleteFakeServer();
+        });
+        SigninTestUtil.tearDownAuthForTest();
+    }
+
     public SyncTestRule() {
         super(ChromeActivity.class);
     }
@@ -183,6 +203,22 @@
     }
 
     /*
+     * Enables the |chosenDataTypes|, which must be in USER_SELECTABLE_TYPES.
+     */
+    public void setChosenDataTypes(boolean syncEverything, Set<Integer> chosenDataTypes) {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mProfileSyncService.setChosenDataTypes(syncEverything, chosenDataTypes); });
+    }
+
+    /*
+     * Sets payments integration to |enabled|.
+     */
+    public void setPaymentsIntegrationEnabled(final boolean enabled) {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.setPaymentsIntegrationEnabled(enabled));
+    }
+
+    /*
      * Disables the |modelType| Sync data type, which must be in USER_SELECTABLE_TYPES.
      */
     public void disableDataType(final int modelType) {
@@ -239,16 +275,57 @@
         };
     }
 
-    private void ruleSetUp() throws Throwable {
-        // This must be called before super.setUp() in order for test authentication to work.
-        SigninTestUtil.setUpAuthForTest();
+    /*
+     * Adds a credit card to server for autofill.
+     */
+    public void addServerAutofillCreditCard() {
+        final String serverId = "025eb937c022489eb8dc78cbaa969218";
+        WalletMaskedCreditCard card =
+                WalletMaskedCreditCard.newBuilder()
+                        .setId(serverId)
+                        .setStatus(WalletMaskedCreditCard.WalletCardStatus.VALID)
+                        .setNameOnCard("Jon Doe")
+                        .setType(WalletMaskedCreditCard.WalletCardType.UNKNOWN)
+                        .setLastFour("1111")
+                        .setExpMonth(11)
+                        .setExpYear(2020)
+                        .build();
+        AutofillWalletSpecifics wallet_specifics =
+                AutofillWalletSpecifics.newBuilder()
+                        .setType(AutofillWalletSpecifics.WalletInfoType.MASKED_CREDIT_CARD)
+                        .setMaskedCard(card)
+                        .build();
+        EntitySpecifics specifics =
+                EntitySpecifics.newBuilder().setAutofillWallet(wallet_specifics).build();
+        SyncEntity entity = SyncEntity.newBuilder()
+                                    .setName(serverId)
+                                    .setIdString(serverId)
+                                    .setSpecifics(specifics)
+                                    .build();
+        getFakeServerHelper().setWalletData(entity);
+        SyncTestUtil.triggerSyncAndWaitForCompletion();
     }
 
-    private void ruleTearDown() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mProfileSyncService.requestStop();
-            FakeServerHelper.deleteFakeServer();
+    /*
+     * Checks if server has any credit card information to autofill.
+     */
+    public boolean hasServerAutofillCreditCards() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            List<CreditCard> cards = PersonalDataManager.getInstance().getCreditCardsForSettings();
+            for (int i = 0; i < cards.size(); i++) {
+                if (!cards.get(i).getIsLocal()) return true;
+            }
+            return false;
         });
-        SigninTestUtil.tearDownAuthForTest();
+    }
+
+    // UI interaction convenience methods.
+    public void togglePreference(final TwoStatePreference pref) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            boolean newValue = !pref.isChecked();
+            pref.getOnPreferenceChangeListener().onPreferenceChange(pref, newValue);
+            pref.setChecked(newValue);
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
index 1d0491b..7acf7f7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
@@ -413,7 +413,7 @@
         VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(mockApiWithDoff);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PreferencesLauncher.launchSettingsPage(context, null);
+            PreferencesLauncher.launchSettingsPageCompat(context, null);
             VrShellDelegateUtils.getDelegateInstance().acceptDoffPromptForTesting();
         });
         CriteriaHelper.pollUiThread(() -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
index 631ee76..ab669f8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
@@ -231,7 +231,9 @@
                 MANIFEST_TEST_PAGE_TITLE);
         addShortcutToTab(mTab, "", true);
 
-        Assert.assertTrue(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
+        // TODO(crbug.com/977173): re-enable maskable icon support once server support
+        // is ready.
+        Assert.assertFalse(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
index a53b975..79f7016 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
@@ -103,10 +103,11 @@
         final WebApkUpdateDataFetcher fetcher = new WebApkUpdateDataFetcher();
         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
             WebApkInfo oldInfo = WebApkInfo.create("", "", scopeUrl, null, null, null, null, null,
-                    -1, -1, -1, -1, -1, -1, "random.package", -1, manifestUrl, "",
+                    -1, -1, -1, -1, -1, -1, false, "random.package", -1, manifestUrl, "",
                     WebApkInfo.WebApkDistributor.BROWSER, new HashMap<String, String>(), null,
                     null /*shareTargetActivityName*/, false /* forceNavigation */,
-                    false /* isSplashProvidedByWebApk */, null /* shareData */);
+                    false /* isSplashProvidedByWebApk */, null /* shareData */,
+                    1 /* webApkVersionCode */);
             fetcher.start(mTab, oldInfo, observer);
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 64b94bb..77df393 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -154,12 +154,12 @@
             WebApkInfo info = WebApkInfo.create(
                     WEBAPK_ID, "", creationData.scope, null, null, null, creationData.name,
                     creationData.shortName, creationData.displayMode, creationData.orientation, 0,
-                    creationData.themeColor, creationData.backgroundColor, 0, "",
+                    creationData.themeColor, creationData.backgroundColor, 0, false, "",
                     WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION, creationData.manifestUrl,
                     creationData.startUrl, WebApkInfo.WebApkDistributor.BROWSER,
                     creationData.iconUrlToMurmur2HashMap, null, null /*shareTargetActivityName*/,
                     false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
-                    null /* shareData */
+                    null /* shareData */, 1 /* webApkVersionCode */
 
             );
             updateManager.updateIfNeeded(mTab, info);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
index e8df4cc..54c9d4a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
@@ -134,9 +134,10 @@
                         false /* isIconAdaptive */, false /* forceNavigation */)
                 : WebApkInfo.create(
                         "", "", webappStartUrlOrScopeUrl, null, null, null, null, null, displayMode,
-                        0, 0, 0, 0, 0, "", 0, null, "", WebApkInfo.WebApkDistributor.BROWSER, null,
-                        null, null /*shareTargetActivityName*/, false /* forceNavigation */,
-                        false /* isSplashProvidedByWebApk */, null /* shareData */
+                        0, 0, 0, 0, 0, false, "", 0, null, "", WebApkInfo.WebApkDistributor.BROWSER,
+                        null, null, null /*shareTargetActivityName*/, false /* forceNavigation */,
+                        false /* isSplashProvidedByWebApk */, null /* shareData */,
+                        1 /* webApkVersionCode */
 
                 );
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ToSAckedReceiverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ToSAckedReceiverTest.java
index 3e32f5eb..5d472ca3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ToSAckedReceiverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ToSAckedReceiverTest.java
@@ -23,16 +23,10 @@
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.AccountManagerDelegateException;
 import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.CoreAccountId;
-import org.chromium.components.signin.CoreAccountInfo;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
-/**
- * Tests for {@link ToSAckedReceiver}.
- */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class ToSAckedReceiverTest {
@@ -64,11 +58,9 @@
         Assert.assertThat(toSAckedAccounts, Matchers.contains(GOOGLE_ACCOUNT));
 
         AccountManagerDelegate accountManagerDelegate = Mockito.mock(AccountManagerDelegate.class);
-        CoreAccountInfo accountInfo = new CoreAccountInfo(
-                new CoreAccountId("gaia-id"), new Account(GOOGLE_ACCOUNT, "LegitAccount"));
-        Mockito.doReturn(Arrays.asList(accountInfo))
-                .when(accountManagerDelegate)
-                .getAccountInfosSync();
+        Account[] accounts = new Account[1];
+        accounts[0] = new Account(GOOGLE_ACCOUNT, "LegitAccount");
+        Mockito.doReturn(accounts).when(accountManagerDelegate).getAccountsSync();
         AccountManagerFacade.overrideAccountManagerFacadeForTests(accountManagerDelegate);
         Assert.assertTrue(ToSAckedReceiver.checkAnyUserHasSeenToS());
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
index 5db808a..8c041a3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -61,13 +60,14 @@
         sSuggestionTypes[OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE] = "SEARCH_SUGGEST_PROFILE";
         sSuggestionTypes[OmniboxSuggestionType.SEARCH_OTHER_ENGINE] = "SEARCH_OTHER_ENGINE";
         sSuggestionTypes[OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED] = "NAVSUGGEST_PERSONALIZED";
-        sSuggestionTypes[OmniboxSuggestionType.CALCULATOR] = "CALCULATOR";
         sSuggestionTypes[OmniboxSuggestionType.CLIPBOARD_URL] = "CLIPBOARD_URL";
         sSuggestionTypes[OmniboxSuggestionType.VOICE_SUGGEST] = "VOICE_SUGGEST";
         sSuggestionTypes[OmniboxSuggestionType.DOCUMENT_SUGGESTION] = "DOCUMENT_SUGGESTION";
         sSuggestionTypes[OmniboxSuggestionType.PEDAL] = "PEDAL";
         sSuggestionTypes[OmniboxSuggestionType.CLIPBOARD_TEXT] = "CLIPBOARD_TEXT";
         sSuggestionTypes[OmniboxSuggestionType.CLIPBOARD_IMAGE] = "CLIPBOARD_IMAGE";
+        // Note: CALCULATOR suggestions are not handled by basic suggestion processor.
+        // These suggestions are now processed by AnswerSuggestionProcessor instead.
 
         sIconTypes = new String[SuggestionIcon.TOTAL_COUNT];
         sIconTypes[SuggestionIcon.UNSET] = "UNSET";
@@ -76,7 +76,6 @@
         sIconTypes[SuggestionIcon.GLOBE] = "GLOBE";
         sIconTypes[SuggestionIcon.MAGNIFIER] = "MAGNIFIER";
         sIconTypes[SuggestionIcon.VOICE] = "VOICE";
-        sIconTypes[SuggestionIcon.CALCULATOR] = "CALCULATOR";
         sIconTypes[SuggestionIcon.FAVICON] = "FAVICON";
     }
 
@@ -143,42 +142,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.PEDAL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_TEXT, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_IMAGE, SuggestionIcon.MAGNIFIER},
-        };
-
-        for (int[] test : testSuites) {
-            assertSuggestionIconTypeIs(createSearchSuggestion(test[0]), test[1]);
-        }
-    }
-
-    @Test
-    @EnableFeatures(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT)
-    @DisableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    public void getSuggestionIconTypeForSearch_AnwersInSuggest() {
-        mProcessor.onNativeInitialized();
-        int[][] testSuites = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.CALCULATOR},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.MAGNIFIER},
@@ -194,7 +157,6 @@
 
     @Test
     @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    @DisableFeatures(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT)
     public void getSuggestionIconTypeForSearch_FavIcons() {
         mProcessor.onNativeInitialized();
         int[][] testSuites = {
@@ -213,7 +175,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.MAGNIFIER},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.MAGNIFIER},
@@ -245,7 +206,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.GLOBE},
@@ -261,7 +221,6 @@
 
     @Test
     @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    @DisableFeatures(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT)
     public void getSuggestionIconTypeForUrl_FavIcons() {
         mProcessor.onNativeInitialized();
         int[][] testSuites = {
@@ -280,7 +239,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.GLOBE},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.GLOBE},
@@ -312,7 +270,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.BOOKMARK},
@@ -328,7 +285,6 @@
 
     @Test
     @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    @DisableFeatures(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT)
     public void getSuggestionIconTypeForBookmarks_FavIcons() {
         mProcessor.onNativeInitialized();
         int[][] testSuites = {
@@ -347,7 +303,6 @@
                 {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.CALCULATOR, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.BOOKMARK},
                 {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.BOOKMARK},
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index 18b9954..4438bc56 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -127,14 +127,15 @@
         @Override
         public void storeWebApkUpdateRequestToFile(String updateRequestPath, String startUrl,
                 String scope, String name, String shortName, String primaryIconUrl,
-                Bitmap primaryIcon, String badgeIconUrl, Bitmap badgeIcon, String[] iconUrls,
-                String[] iconHashes, @WebDisplayMode int displayMode, int orientation,
-                long themeColor, long backgroundColor, String shareTargetAction,
-                String shareTargetParamTitle, String shareTargetParamText,
-                String shareTargetParamUrl, boolean shareTargetParamIsMethodPost,
-                boolean shareTargetParamIsEncTypeMultipart, String[] shareTargetParamFileNames,
-                Object[] shareTargetParamAccepts, String manifestUrl, String webApkPackage,
-                int webApkVersion, boolean isManifestStale, @WebApkUpdateReason int updateReason,
+                Bitmap primaryIcon, boolean isPrimaryIconMaskable, String badgeIconUrl,
+                Bitmap badgeIcon, String[] iconUrls, String[] iconHashes,
+                @WebDisplayMode int displayMode, int orientation, long themeColor,
+                long backgroundColor, String shareTargetAction, String shareTargetParamTitle,
+                String shareTargetParamText, String shareTargetParamUrl,
+                boolean shareTargetParamIsMethodPost, boolean shareTargetParamIsEncTypeMultipart,
+                String[] shareTargetParamFileNames, Object[] shareTargetParamAccepts,
+                String manifestUrl, String webApkPackage, int webApkVersion,
+                boolean isManifestStale, @WebApkUpdateReason int updateReason,
                 Callback<Boolean> callback) {}
 
         @Override
@@ -362,9 +363,9 @@
                 new WebApkInfo.Icon(manifestData.badgeIcon), null, manifestData.name,
                 manifestData.shortName, manifestData.displayMode, manifestData.orientation, -1,
                 manifestData.themeColor, manifestData.backgroundColor,
-                manifestData.defaultBackgroundColor, kPackageName, -1, WEB_MANIFEST_URL,
-                manifestData.startUrl, WebApkInfo.WebApkDistributor.BROWSER,
-                manifestData.iconUrlToMurmur2HashMap,
+                manifestData.defaultBackgroundColor, false /* isPrimaryIconMaskable */,
+                kPackageName, -1, WEB_MANIFEST_URL, manifestData.startUrl,
+                WebApkInfo.WebApkDistributor.BROWSER, manifestData.iconUrlToMurmur2HashMap,
                 new WebApkInfo.ShareTarget(manifestData.shareTargetAction,
                         manifestData.shareTargetParamTitle, null, null,
                         manifestData.shareTargetMethod != null
@@ -374,7 +375,8 @@
                                         SHARE_TARGET_ENC_TYPE_MULTIPART),
                         manifestData.shareTargetFileNames, manifestData.shareTargetFileAccepts),
                 null /* shareTargetActivityName */, false /* forceNavigation */,
-                false /* isSplashProvidedByWebApk */, null /* shareData */);
+                false /* isSplashProvidedByWebApk */, null /* shareData */,
+                1 /* webApkVersionCode */);
     }
 
     /**
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 2197c1bb..e18a782 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3862.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3864.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/profiles/update_afdo_profile.py b/chrome/android/profiles/update_afdo_profile.py
deleted file mode 100755
index 690521ca..0000000
--- a/chrome/android/profiles/update_afdo_profile.py
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/usr/bin/python
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""This script is used to update our local AFDO profiles.
-
-This uses profiles of Chrome provided by our friends from Chrome OS. Though the
-profiles are available externally, the bucket they sit in is otherwise
-unreadable by non-Googlers. Gsutil usage with this bucket is therefore quite
-awkward: you can't do anything but `cp` certain files with an external account,
-and you can't even do that if you're not yet authenticated.
-
-No authentication is necessary if you pull these profiles directly over
-https."""
-
-import argparse
-import contextlib
-import os
-import subprocess
-import sys
-import urllib2
-
-GS_HTTP_URL = 'https://storage.googleapis.com'
-GS_BASE_URL = GS_HTTP_URL + '/chromeos-prebuilt/afdo-job/llvm'
-PROFILE_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
-LOCAL_PROFILE_PATH = os.path.join(PROFILE_DIRECTORY, 'afdo.prof')
-
-# We use these to track the local profile; newest.txt is owned by git and tracks
-# the name of the newest profile we should pull, and local.txt is the most
-# recent profile we've successfully pulled.
-NEWEST_PROFILE_NAME_PATH = os.path.join(PROFILE_DIRECTORY, 'newest.txt')
-LOCAL_PROFILE_NAME_PATH = os.path.join(PROFILE_DIRECTORY, 'local.txt')
-
-
-def ReadUpToDateProfileName():
-  with open(NEWEST_PROFILE_NAME_PATH) as f:
-    return f.read().strip()
-
-
-def ReadLocalProfileName():
-  try:
-    with open(LOCAL_PROFILE_NAME_PATH) as f:
-      return f.read().strip()
-  except IOError:
-    # Assume it either didn't exist, or we couldn't read it. In either case, we
-    # should probably grab a new profile (and, in doing so, make this file sane
-    # again)
-    return None
-
-
-def WriteLocalProfileName(name):
-  with open(LOCAL_PROFILE_NAME_PATH, 'w') as f:
-    f.write(name)
-
-
-def CheckCallOrExit(cmd):
-  proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-  stdout, stderr = proc.communicate()
-  exit_code = proc.wait()
-  if not exit_code:
-    return
-
-  complaint_lines = [
-      '## %s failed with exit code %d' % (cmd[0], exit_code),
-      '## Full command: %s' % cmd,
-      '## Stdout:\n' + stdout,
-      '## Stderr:\n' + stderr,
-  ]
-  print >>sys.stderr, '\n'.join(complaint_lines)
-  sys.exit(1)
-
-
-def RetrieveProfile(desired_profile_name, out_path):
-  # vpython is > python 2.7.9, so we can expect urllib to validate HTTPS certs
-  # properly.
-  ext = os.path.splitext(desired_profile_name)[1]
-  compressed_path = out_path + ext
-  gs_prefix = 'gs://'
-  if not desired_profile_name.startswith(gs_prefix):
-    gs_url = GS_BASE_URL + '/' + desired_profile_name
-  else:
-    gs_url = GS_HTTP_URL + '/' + desired_profile_name[len(gs_prefix):]
-
-  with contextlib.closing(urllib2.urlopen(gs_url)) as u:
-    with open(compressed_path, 'wb') as f:
-      while True:
-        buf = u.read(4096)
-        if not buf:
-          break
-        f.write(buf)
-
-  if ext == '.bz2':
-    # NOTE: we can't use Python's bzip module, since it doesn't support
-    # multi-stream bzip files. It will silently succeed and give us a garbage
-    # profile.
-    # bzip2 removes the compressed file on success.
-    CheckCallOrExit(['bzip2', '-d', compressed_path])
-  elif ext == '.xz':
-    # ...And we can't use the `lzma` module, since it was introduced in python3.
-    # xz removes the compressed file on success.
-    CheckCallOrExit(['xz', '-d', compressed_path])
-  else:
-    # Wait until after downloading the file to check the file extension, so the
-    # user has something usable locally if the file extension is unrecognized.
-    raise ValueError(
-        'Only bz2 and xz extensions are supported; "%s" is not' % ext)
-
-
-def CleanProfilesDirectory():
-  # Start with a clean slate, removing old profiles/downloads/etc.
-  old_artifacts = (p for p in os.listdir(PROFILE_DIRECTORY) if
-                   p.startswith('chromeos-chrome-'))
-  for artifact in old_artifacts:
-    os.remove(os.path.join(PROFILE_DIRECTORY, artifact))
-
-
-def main():
-  parser = argparse.ArgumentParser('Downloads profiles provided by Chrome OS')
-  parser.add_argument('-f', '--force', action='store_true',
-                      help='Fetch a profile even if the local one is current')
-  args = parser.parse_args()
-
-  up_to_date_profile = ReadUpToDateProfileName()
-  if not args.force:
-    local_profile_name = ReadLocalProfileName()
-    # In a perfect world, the local profile should always exist if we
-    # successfully read local_profile_name. If it's gone, though, the user
-    # probably removed it as a way to get us to download it again.
-    if local_profile_name == up_to_date_profile \
-        and os.path.exists(LOCAL_PROFILE_PATH):
-      return 0
-
-  CleanProfilesDirectory()
-
-  new_tmpfile = LOCAL_PROFILE_PATH + '.new'
-  RetrieveProfile(up_to_date_profile, new_tmpfile)
-  os.rename(new_tmpfile, LOCAL_PROFILE_PATH)
-  WriteLocalProfileName(up_to_date_profile)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessPreferences.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessPreferences.java
index e2f0df1f1..45844c3 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessPreferences.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessPreferences.java
@@ -8,7 +8,6 @@
 import android.support.v4.app.Fragment;
 import android.support.v7.widget.RecyclerView;
 import android.view.Menu;
-import android.widget.ListView;
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.AppHooks;
@@ -36,33 +35,11 @@
     }
 
     /**
-     * Adds paddings for the main list in the current android.app.Fragment.
-     * Support library fragments are handled in {@link #onAttachedToWindowCompat()}.
-     * TODO(crbug.com/967022): Once all fragments are migrated to the support library, this should
-     * be deleted.
+     * Adds paddings for the main list in the current support library Fragment.
      */
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
-        android.app.Fragment fragment = getMainFragment();
-        if (fragment == null || fragment.getView() == null
-                || fragment.getView().findViewById(android.R.id.list) == null) {
-            return;
-        }
-
-        int padding = getResources().getDimensionPixelSize(
-                org.chromium.chrome.touchless.R.dimen.touchless_preferences_highlight_padding);
-        ListView listView = fragment.getView().findViewById(android.R.id.list);
-        listView.setPadding(padding, 0, padding, 0);
-        listView.setDividerHeight(padding);
-    }
-
-    /**
-     * Adds paddings for the main list in the current support library Fragment.
-     */
-    @Override
-    public void onAttachedToWindowCompat() {
-        super.onAttachedToWindowCompat();
         Fragment fragment = getMainFragmentCompat();
         if (fragment == null || fragment.getView() == null
                 || fragment.getView().findViewById(R.id.list) == null) {
diff --git a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
index 190c027..d7dc991 100644
--- a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
+++ b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
@@ -25,6 +25,7 @@
     public static final String DEFAULT_BACKGROUND_COLOR_ID =
             "org.chromium.webapk.shell_apk.defaultBackgroundColorId";
     public static final String ICON_ID = "org.chromium.webapk.shell_apk.iconId";
+    public static final String IS_ICON_MASKABLE = "org.chromium.webapk.shell_apk.isIconMaskable";
     public static final String SPLASH_ID = "org.chromium.webapk.shell_apk.splashId";
 
     public static final String ICON_URLS_AND_ICON_MURMUR2_HASHES =
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index fb15fc8..d83f3c64 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -213,8 +213,14 @@
   <message name="IDS_CELLULAR_SETUP_TRY_AGAIN_LABEL" desc="Label for button to retry a step during cellular setup">
     Try again
   </message>
-  <message name="IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_TITLE" desc="Title for first screen of cellular setup during which ChromeOS is preparing the cellular device for setup." translateable="false">
-    Sim Detect Page
+  <message name="IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_TITLE" desc="Title for first screen of cellular setup during which ChromeOS is preparing the cellular device for setup.">
+    Preparing to setup your cellular device...
+  </message>
+  <message name="IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_ERROR_TITLE" desc="Title for cellular setup when Chrome OS encounters an error preparing the cellular device for setup.">
+    We couldn't detect your SIM card
+  </message>
+  <message name="IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_ERROR_MESSAGE" desc="Message displayed under title in cellular setup when Chrome OS encounters an error preparing the cellular device for setup. Prompts user to insert SIM and try again.">
+    Please insert your SIM and try again
   </message>
   <message name="IDS_CELLULAR_SETUP_PROVISIONING_PAGE_TITLE" desc="Title for cellular setup step in which the user uses the embedded carrier provisioning portal to make payment and activate the device." translateable="false">
     Provisioning Page
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 7b14104..4083803 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -33,7 +33,10 @@
 
   <!-- Search and Assistant section. -->
   <message name="IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL" desc="Label in OS settings describing search engine behavior.">
-    Searches from the app launcher use your browser <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>search engine setting<ph name="END_LINK">&lt;/a&gt;</ph>.
+    Preferred search engine
+  </message>
+  <message name="IDS_OS_SETTINGS_SEARCH_ENGINE_TOOLTIP" desc="Tooltip in OS settings explaining that search engine is used in both the Chrome browser and the Chrome OS app launcher.">
+    Used by Chrome browser and <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> launcher
   </message>
 
   <!-- Files Page (OS settings) -->
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 335df5f4..301e642 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -685,7 +685,7 @@
       Google Play Store
     </message>
     <message name="IDS_SETTINGS_ANDROID_APPS_SUBTEXT" desc="Description for the section for enabling and managing Google Play Store (Android) apps.">
-      Install apps and games from Google Play on your Chromebook. &lt;a target="_blank" href="<ph name="URL">$1<ex>https://google.com/</ex></ph>"&gt;Learn more&lt;/a&gt;
+      Install apps and games from Google Play on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. &lt;a target="_blank" href="<ph name="URL">$2<ex>https://google.com/</ex></ph>"&gt;Learn more&lt;/a&gt;
     </message>
     <message name="IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS" desc="Label for launching Android apps settings.">
       Manage Android preferences
@@ -697,6 +697,8 @@
     <message name="IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE" desc="Title of the confirmation dialog for disabling android apps.">
       Remove Android apps?
     </message>
+    <!-- TODO(jamescook): Use device type instead of "Chromebook", which may
+         require changing ArcPlayTermsOfServiceConsent resource id handling. -->
     <message name="IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE" desc="Describes what will happen if the user opts out of android apps.">
       Apps you’ve downloaded from Google Play will be deleted from this Chromebook.
       <ph name="LINE_BREAKS1">&lt;br&gt;&lt;br&gt;</ph>
@@ -4843,7 +4845,7 @@
       Android phone
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_SETUP_SUMMARY" desc="Tells the user to connect their Chromebook to their phone.">
-      Connect your Chromebook with your phone. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+      Connect your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> with your phone. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_NO_ELIGIBLE_HOSTS" desc="Tells the user that there is no phone with their account on it that can connect to their Chromebook.">
       No eligible devices. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
@@ -4864,7 +4866,7 @@
       Disabled
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_SMART_LOCK_SUMMARY" desc="Description of for the 'Smart Lock' setting. This feature automatically unlocks the user's Chromebook if their phone is nearby and unlocked.">
-      Unlock your Chromebook with your phone. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+      Unlock your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> with your phone. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING" desc="Name of a feature. This feature automatically offers the user to tether to their phone if their Chromebook is offline and their phone supports tethering.">
       Instant Tethering
@@ -4876,16 +4878,16 @@
       Messages
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_ANDROID_MESSAGES_SUMMARY" desc="Description of for the 'Android Messages' setting. This feature lets the user read and reply to text messages from their Chromebook. New text messages will appear as notifications.">
-      Send and receive text messages from your Chromebook. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+      Send and receive text messages from your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE" desc="Header to tell the user an action will make their Chromebook forget their phone. This means they will no longer have access to multidevice features.">
       Forget phone
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE_EXPLANATION" desc="Explanation on a clickable menu item that makes the Chromebook forget the user's phone. It tells the user that the menu item will cause their phone to stop acting as a partner for their Chromebook for multidevice features.">
-      Disconnect your phone from your Chromebook
+      Disconnect your phone from your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
     </message>
     <message name="IDS_SETTINGS_MULTIDEVICE_FORGET_DEVICE_DIALOG_MESSAGE" desc="Text of a dialog that lets the user choose if their Chromebook should forget their phone. This means they will no longer have access to multidevice features.">
-      Disconnect your phone from your Chromebook. They will no longer connect automatically.
+      Disconnect your phone from your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. They will no longer connect automatically.
     </message>
   </if>
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 540fba3..b4f55f0 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -133,6 +133,8 @@
     "autofill/strike_database_factory.h",
     "autofill/validation_rules_storage_factory.cc",
     "autofill/validation_rules_storage_factory.h",
+    "availability/availability_prober.cc",
+    "availability/availability_prober.h",
     "background_fetch/background_fetch_delegate_factory.cc",
     "background_fetch/background_fetch_delegate_factory.h",
     "background_fetch/background_fetch_delegate_impl.cc",
@@ -325,12 +327,12 @@
     "component_updater/recovery_component_installer.h",
     "component_updater/recovery_improved_component_installer.cc",
     "component_updater/recovery_improved_component_installer.h",
+    "component_updater/safety_tips_component_installer.cc",
+    "component_updater/safety_tips_component_installer.h",
     "component_updater/sth_set_component_remover.cc",
     "component_updater/sth_set_component_remover.h",
     "component_updater/subresource_filter_component_installer.cc",
     "component_updater/subresource_filter_component_installer.h",
-    "component_updater/supervised_user_whitelist_installer.cc",
-    "component_updater/supervised_user_whitelist_installer.h",
     "component_updater/sw_reporter_installer_win.cc",
     "component_updater/sw_reporter_installer_win.h",
     "consent_auditor/consent_auditor_factory.cc",
@@ -629,6 +631,8 @@
     "lookalikes/lookalike_url_service.cc",
     "lookalikes/lookalike_url_service.h",
     "lookalikes/safety_tips/safety_tip_ui.h",
+    "lookalikes/safety_tips/safety_tips_config.cc",
+    "lookalikes/safety_tips/safety_tips_config.h",
     "mac/bluetooth_utility.h",
     "mac/bluetooth_utility.mm",
     "mac/dock.h",
@@ -1340,8 +1344,6 @@
     "previews/previews_lite_page_url_loader_interceptor.h",
     "previews/previews_offline_helper.cc",
     "previews/previews_offline_helper.h",
-    "previews/previews_prober.cc",
-    "previews/previews_prober.h",
     "previews/previews_service.cc",
     "previews/previews_service.h",
     "previews/previews_service_factory.cc",
@@ -1879,12 +1881,13 @@
   ]
   deps = [
     ":active_use_util",
+    ":availability_protos",
     ":ntp_background_proto",
-    ":previews_protos",
     ":resource_prefetch_predictor_proto",
     "//base:i18n",
     "//base/allocator:buildflags",
     "//base/util/values:values_util",
+    "//build:branding_buildflags",
     "//cc",
     "//chrome:extra_resources",
     "//chrome:resources",
@@ -1963,7 +1966,6 @@
     "//components/history/content/browser",
     "//components/history/core/browser",
     "//components/history/core/common",
-    "//components/image_fetcher/core",
     "//components/infobars/core",
     "//components/invalidation/impl",
     "//components/keyed_service/content",
@@ -1990,7 +1992,6 @@
     "//components/network_hints/common",
     "//components/network_session_configurator/browser",
     "//components/network_time",
-    "//components/ntp_snippets",
     "//components/ntp_tiles",
     "//components/offline_items_collection/core",
     "//components/offline_pages/buildflags",
@@ -2668,6 +2669,8 @@
       "android/webapps/add_to_homescreen_manager.h",
       "android/webapps/single_tab_mode_tab_helper.cc",
       "android/webapps/single_tab_mode_tab_helper.h",
+      "android/webapps/webapk_ukm_recorder.cc",
+      "android/webapps/webapk_ukm_recorder.h",
       "android/webapps/webapp_registry.cc",
       "android/webapps/webapp_registry.h",
       "android/widget/thumbnail_generator.cc",
@@ -2825,6 +2828,10 @@
       "sync/profile_sync_service_android.h",
       "translate/android/translate_bridge.cc",
     ]
+    public_deps += [
+      "//components/image_fetcher/core",
+      "//components/ntp_snippets",
+    ]
     deps += [
       ":client_discourse_context_proto",
       ":delta_file_proto",
@@ -2883,10 +2890,10 @@
       "apps/app_service/app_icon_source.h",
       "apps/app_service/app_launch_params.cc",
       "apps/app_service/app_launch_params.h",
+      "apps/app_service/app_service_proxy.cc",
+      "apps/app_service/app_service_proxy.h",
       "apps/app_service/app_service_proxy_factory.cc",
       "apps/app_service/app_service_proxy_factory.h",
-      "apps/app_service/app_service_proxy_impl.cc",
-      "apps/app_service/app_service_proxy_impl.h",
       "apps/app_service/dip_px_util.cc",
       "apps/app_service/dip_px_util.h",
       "apps/intent_helper/apps_navigation_throttle.cc",
@@ -3327,6 +3334,8 @@
       "search/promos/promo_service_observer.h",
       "search/search_engine_base_url_tracker.cc",
       "search/search_engine_base_url_tracker.h",
+      "search/search_provider_observer.cc",
+      "search/search_provider_observer.h",
       "search/search_suggest/search_suggest_data.cc",
       "search/search_suggest/search_suggest_data.h",
       "search/search_suggest/search_suggest_loader.h",
@@ -3484,11 +3493,12 @@
       "//chrome/common/search:generate_chrome_colors_info",
       "//chrome/common/themes:autogenerated_theme_util",
       "//chrome/services/app_service:lib",
-      "//chrome/services/app_service/public/cpp:app_service_proxy",
       "//chrome/services/app_service/public/cpp:app_update",
       "//chrome/services/app_service/public/cpp:icon_loader",
       "//components/feedback",
+      "//components/image_fetcher/core",
       "//components/keep_alive_registry",
+      "//components/ntp_snippets",
       "//components/vector_icons",
       "//components/web_modal",
       "//components/zoom",
@@ -3841,8 +3851,6 @@
       "first_run/upgrade_util_linux.cc",
       "first_run/upgrade_util_linux.h",
       "icon_loader_auralinux.cc",
-      "password_manager/native_backend_kwallet_x.cc",
-      "password_manager/native_backend_kwallet_x.h",
       "platform_util_linux.cc",
       "shell_integration_linux.cc",
       "shell_integration_linux.h",
@@ -3874,16 +3882,6 @@
       ]
     }
 
-    # libsecret hard depends on GLib.
-    if (use_glib) {
-      sources += [
-        "password_manager/native_backend_libsecret.cc",
-        "password_manager/native_backend_libsecret.h",
-      ]
-      defines += [ "USE_LIBSECRET" ]
-      deps += [ "//third_party/libsecret" ]
-    }
-
     if (use_ozone) {
       sources += [
         "fullscreen_ozone.cc",
@@ -4683,14 +4681,18 @@
         "offline_pages/android/request_coordinator_factory.cc",
       ]
     }
+
+    public_deps += [
+      "//components/offline_pages/core",
+      "//components/offline_pages/core/background:background_offliner",
+      "//components/offline_pages/core/prefetch",
+    ]
+
     deps += [
       "//chrome/common:offline_page_auto_fetcher_mojom",
       "//components/offline_pages/content/background_loader",
       "//components/offline_pages/content/renovations",
-      "//components/offline_pages/core",
-      "//components/offline_pages/core/background:background_offliner",
       "//components/offline_pages/core/downloads:offline_pages_ui_adapter",
-      "//components/offline_pages/core/prefetch",
       "//components/offline_pages/core/renovations",
       "//components/offline_pages/core/request_header:request_header",
     ]
@@ -4957,6 +4959,8 @@
 
   if (enable_supervised_users) {
     sources += [
+      "component_updater/supervised_user_whitelist_installer.cc",
+      "component_updater/supervised_user_whitelist_installer.h",
       "content_settings/content_settings_supervised_provider.cc",
       "content_settings/content_settings_supervised_provider.h",
       "supervised_user/child_accounts/child_account_service.cc",
@@ -5031,7 +5035,7 @@
 
   if (enable_vr) {
     if (enable_gvr_services) {
-      deps += [ "android/vr:vr_android" ]
+      public_deps += [ "android/vr:vr_android" ]
       configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
       allow_circular_includes_from += [ "android/vr:vr_android" ]
     }
@@ -5070,14 +5074,6 @@
     configs += [ "//printing:cups" ]
   }
 
-  if (use_gnome_keyring) {
-    sources += [
-      "password_manager/native_backend_gnome_x.cc",
-      "password_manager/native_backend_gnome_x.h",
-    ]
-    configs += [ "//components/os_crypt:gnome_keyring" ]
-  }
-
   if (use_nss_certs) {
     sources += [
       "certificate_manager_model.cc",
@@ -5180,9 +5176,9 @@
   ]
 }
 
-proto_library("previews_protos") {
+proto_library("availability_protos") {
   sources = [
-    "previews/proto/previews_prober_cache_entry.proto",
+    "availability/proto/availability_prober_cache_entry.proto",
   ]
 }
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8763ce2..e6f84a4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -143,6 +143,7 @@
 #include "ui/display/display_features.h"
 #include "ui/display/display_switches.h"
 #include "ui/events/blink/blink_features.h"
+#include "ui/events/blink/prediction/filter_factory.h"
 #include "ui/events/blink/prediction/predictor_factory.h"
 #include "ui/events/event_switches.h"
 #include "ui/gfx/switches.h"
@@ -1062,6 +1063,14 @@
       kResamplingInputEventsLinearSecondEnabled,
       base::size(kResamplingInputEventsLinearSecondEnabled), nullptr}};
 
+const FeatureEntry::FeatureParam kFilteringPredictionEmptyFilterEnabled[] = {
+    {"filter", ui::input_prediction::kFilterNameEmpty}};
+
+const FeatureEntry::FeatureVariation kFilteringPredictionFeatureVariations[] = {
+    {ui::input_prediction::kFilterNameEmpty,
+     kFilteringPredictionEmptyFilterEnabled,
+     base::size(kFilteringPredictionEmptyFilterEnabled), nullptr}};
+
 #if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kBottomOfflineIndicatorEnabled[] = {
     {"bottom_offline_indicator", "true"}};
@@ -1819,6 +1828,11 @@
     {"enable-site-per-process", flag_descriptions::kStrictSiteIsolationName,
      flag_descriptions::kStrictSiteIsolationDescription, kOsAndroid,
      SINGLE_VALUE_TYPE(switches::kSitePerProcess)},
+    {"enable-process-sharing-with-default-site-instances",
+     flag_descriptions::kProcessSharingWithDefaultSiteInstancesName,
+     flag_descriptions::kProcessSharingWithDefaultSiteInstancesDescription,
+     kOsAndroid,
+     FEATURE_VALUE_TYPE(features::kProcessSharingWithDefaultSiteInstances)},
     {"enable-process-sharing-with-strict-site-instances",
      flag_descriptions::kProcessSharingWithStrictSiteInstancesName,
      flag_descriptions::kProcessSharingWithStrictSiteInstancesDescription,
@@ -2556,11 +2570,6 @@
 #endif  // defined(OS_WIN)
 
 #if defined(OS_ANDROID)
-    {"omnibox-new-answer-layout",
-     flag_descriptions::kOmniboxNewAnswerLayoutName,
-     flag_descriptions::kOmniboxNewAnswerLayoutDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(omnibox::kOmniboxNewAnswerLayout)},
-
     {"omnibox-on-device-head-suggestions",
      flag_descriptions::kOmniboxOnDeviceHeadSuggestionsName,
      flag_descriptions::kOmniboxOnDeviceHeadSuggestionsDescription, kOsAndroid,
@@ -3414,6 +3423,13 @@
                                     kResamplingInputEventsFeatureVariations,
                                     "ResamplingScrollEvents")},
 
+    {"enable-filtering-scroll-events",
+     flag_descriptions::kFilteringScrollPredictionName,
+     flag_descriptions::kFilteringScrollPredictionDescription, kOsAll,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(features::kFilteringScrollPrediction,
+                                    kFilteringPredictionFeatureVariations,
+                                    "FilteringScrollPrediction")},
+
     {"compositor-threaded-scrollbar-scrolling",
      flag_descriptions::kCompositorThreadedScrollbarScrollingName,
      flag_descriptions::kCompositorThreadedScrollbarScrollingDescription,
@@ -3762,8 +3778,7 @@
      SINGLE_VALUE_TYPE(switches::kDisableBestEffortTasks)},
     {"enable-sync-uss-passwords",
      flag_descriptions::kEnableSyncUSSPasswordsName,
-     flag_descriptions::kEnableSyncUSSPasswordsDescription,
-     kOsMac | kOsWin | kOsCrOS | kOsAndroid,
+     flag_descriptions::kEnableSyncUSSPasswordsDescription, kOsAll,
      FEATURE_VALUE_TYPE(switches::kSyncUSSPasswords)},
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
@@ -4192,6 +4207,11 @@
      flag_descriptions::kNotificationSchedulerDebugOptionDescription,
      kOsAndroid, MULTI_VALUE_TYPE(kNotificationSchedulerChoices)},
 
+    {"update-hover-at-begin-frame",
+     flag_descriptions::kUpdateHoverAtBeginFrameName,
+     flag_descriptions::kUpdateHoverAtBeginFrameDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kUpdateHoverAtBeginFrame)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
index 100b9f28..e7fd6356 100644
--- a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.cc
@@ -109,6 +109,13 @@
       static_cast<TermsAndConditionsState>(state));
 }
 
+void AssistantPaymentRequestDelegate::OnTermsAndConditionsLinkClicked(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    jint link) {
+  ui_controller_->OnTermsAndConditionsLinkClicked(link);
+}
+
 base::android::ScopedJavaGlobalRef<jobject>
 AssistantPaymentRequestDelegate::GetJavaObject() {
   return java_assistant_payment_request_delegate_;
diff --git a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
index 87545be..1b605847 100644
--- a/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
+++ b/chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h
@@ -36,6 +36,11 @@
       const base::android::JavaParamRef<jobject>& jcaller,
       jint state);
 
+  void OnTermsAndConditionsLinkClicked(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      jint link);
+
   base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
 
  private:
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 979488d8..16225f9 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -695,6 +695,10 @@
   ui_delegate_->SetTermsAndConditions(state);
 }
 
+void UiControllerAndroid::OnTermsAndConditionsLinkClicked(int link) {
+  ui_delegate_->OnTermsAndConditionsLinkClicked(link);
+}
+
 void UiControllerAndroid::OnPaymentRequestOptionsChanged(
     const PaymentRequestOptions* payment_options) {
   JNIEnv* env = AttachCurrentThread();
@@ -714,8 +718,13 @@
       env, jmodel, payment_options->request_shipping);
   Java_AssistantPaymentRequestModel_setRequestPayment(
       env, jmodel, payment_options->request_payment_method);
-  Java_AssistantPaymentRequestModel_setRequestTermsAndConditions(
-      env, jmodel, payment_options->request_terms_and_conditions);
+  Java_AssistantPaymentRequestModel_setAcceptTermsAndConditionsText(
+      env, jmodel,
+      base::android::ConvertUTF8ToJavaString(
+          env, payment_options->accept_terms_and_conditions_text));
+  Java_AssistantPaymentRequestModel_setShowTermsAsCheckbox(
+      env, jmodel, payment_options->show_terms_as_checkbox);
+
   Java_AssistantPaymentRequestModel_setSupportedBasicCardNetworks(
       env, jmodel,
       base::android::ToJavaArrayOfStrings(
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index a2b319f..68c3a3a 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -119,6 +119,7 @@
                             std::string email);
   void OnCreditCardChanged(std::unique_ptr<autofill::CreditCard> card);
   void OnTermsAndConditionsChanged(TermsAndConditionsState state);
+  void OnTermsAndConditionsLinkClicked(int link);
 
   // Called by AssistantFormDelegate:
   void OnCounterChanged(int input_index, int counter_index, int value);
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index a155ba3a..569aae2 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -205,7 +205,6 @@
     &offline_pages::kPrefetchingOfflinePagesFeature,
     &omnibox::kHideSteadyStateUrlScheme,
     &omnibox::kHideSteadyStateUrlTrivialSubdomains,
-    &omnibox::kOmniboxNewAnswerLayout,
     &omnibox::kOmniboxRichEntitySuggestions,
     &omnibox::kQueryInOmnibox,
     &omnibox::kUIExperimentShowSuggestionFavicons,
diff --git a/chrome/browser/android/digital_asset_links/digital_asset_links_handler.cc b/chrome/browser/android/digital_asset_links/digital_asset_links_handler.cc
index 095e5d481..d925f53 100644
--- a/chrome/browser/android/digital_asset_links/digital_asset_links_handler.cc
+++ b/chrome/browser/android/digital_asset_links/digital_asset_links_handler.cc
@@ -27,6 +27,10 @@
 
 namespace {
 
+// In some cases we get a network change while fetching the digital asset
+// links file. See https://crbug.com/987329.
+const int kNumNetworkRetries = 1;
+
 // Location on a website where the asset links file can be found, see
 // https://developers.google.com/digital-asset-links/v1/getting-started.
 const char kAssetLinksAbsolutePath[] = ".well-known/assetlinks.json";
@@ -246,6 +250,9 @@
   request->url = request_url;
   url_loader_ =
       network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  url_loader_->SetRetryOptions(
+      kNumNetworkRetries,
+      network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
   url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
       shared_url_loader_factory_.get(),
       base::BindOnce(&DigitalAssetLinksHandler::OnURLLoadComplete,
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index f5571be..5aca12b 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -220,8 +220,14 @@
 void DownloadManagerService::OnFullBrowserStarted(JNIEnv* env, jobject obj) {
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
                  content::NotificationService::AllSources());
-  Profile* profile = ProfileManager::GetActiveUserProfile();
+  // Register coordinator for each available profile.
+  Profile* profile =
+      ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
   ResetCoordinatorIfNeeded(profile->GetProfileKey());
+  if (profile->HasOffTheRecordProfile()) {
+    ResetCoordinatorIfNeeded(
+        profile->GetOffTheRecordProfile()->GetProfileKey());
+  }
 }
 
 void DownloadManagerService::Observe(
diff --git a/chrome/browser/android/vr/gvr_input_delegate.cc b/chrome/browser/android/vr/gvr_input_delegate.cc
index fcb5c45..644b125 100644
--- a/chrome/browser/android/vr/gvr_input_delegate.cc
+++ b/chrome/browser/android/vr/gvr_input_delegate.cc
@@ -47,7 +47,6 @@
     // [0.0, 1.0], with (0, 0) corresponding to the top-left of the touchpad.
     // Normalize the values to use X axis range -1 (left) to 1 (right) and Y
     // axis range -1 (top) to 1 (bottom).
-    // TODO(https://crbug.com/966060): Revisit this if the convention changes.
     gamepad.axes[0] = (data.touch_pos.x() * 2.0) - 1.0;
     gamepad.axes[1] = (data.touch_pos.y() * 2.0) - 1.0;
   } else {
diff --git a/chrome/browser/android/webapk/webapk.proto b/chrome/browser/android/webapk/webapk.proto
index e75cc63..7b84ad9f 100644
--- a/chrome/browser/android/webapk/webapk.proto
+++ b/chrome/browser/android/webapk/webapk.proto
@@ -47,6 +47,7 @@
     DISPLAY_MODE_DIFFERS = 12;
     WEB_SHARE_TARGET_DIFFERS = 13;
     MANUALLY_TRIGGERED = 14;
+    PRIMARY_ICON_MASKABLE_DIFFERS = 15;
   }
 
   // Package name of the WebAPK.
@@ -116,6 +117,8 @@
   // Specifies Chrome's intended usages for the image.
   repeated Usage usages = 8;
 
+  optional bool is_primay_icon_maskable = 9;
+
   reserved 2, 3, 4, 7;
 }
 
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index 9c4134c..79116e3 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -37,6 +37,7 @@
 void WebApkInstallService::InstallAsync(content::WebContents* web_contents,
                                         const ShortcutInfo& shortcut_info,
                                         const SkBitmap& primary_icon,
+                                        bool is_primary_icon_maskable,
                                         const SkBitmap& badge_icon,
                                         WebappInstallSource install_source) {
   if (IsInstallInProgress(shortcut_info.manifest_url)) {
@@ -54,7 +55,8 @@
   // WebContents has been destroyed before the install is finished.
   auto observer = std::make_unique<LifetimeObserver>(web_contents);
   WebApkInstaller::InstallAsync(
-      browser_context_, shortcut_info, primary_icon, badge_icon,
+      browser_context_, shortcut_info, primary_icon, is_primary_icon_maskable,
+      badge_icon,
       base::Bind(&WebApkInstallService::OnFinishedInstall,
                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&observer),
                  shortcut_info, primary_icon));
diff --git a/chrome/browser/android/webapk/webapk_install_service.h b/chrome/browser/android/webapk/webapk_install_service.h
index 5f66f384..eee6ada 100644
--- a/chrome/browser/android/webapk/webapk_install_service.h
+++ b/chrome/browser/android/webapk/webapk_install_service.h
@@ -70,6 +70,7 @@
   void InstallAsync(content::WebContents* web_contents,
                     const ShortcutInfo& shortcut_info,
                     const SkBitmap& primary_icon,
+                    bool is_primary_icon_maskable,
                     const SkBitmap& badge_icon,
                     WebappInstallSource install_source);
 
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index f27f3e5..d978586 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -120,6 +120,8 @@
       return webapk::WebApk::OLD_SHELL_APK;
     case WebApkUpdateReason::PRIMARY_ICON_HASH_DIFFERS:
       return webapk::WebApk::PRIMARY_ICON_HASH_DIFFERS;
+    case WebApkUpdateReason::PRIMARY_ICON_MASKABLE_DIFFERS:
+      return webapk::WebApk::PRIMARY_ICON_MASKABLE_DIFFERS;
     case WebApkUpdateReason::BADGE_ICON_HASH_DIFFERS:
       return webapk::WebApk::BADGE_ICON_HASH_DIFFERS;
     case WebApkUpdateReason::SCOPE_DIFFERS:
@@ -178,6 +180,7 @@
 std::unique_ptr<std::string> BuildProtoInBackground(
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
+    bool is_primary_icon_maskable,
     const SkBitmap& badge_icon,
     const std::string& package_name,
     const std::string& version,
@@ -264,6 +267,7 @@
     if (entry.first == shortcut_info.best_primary_icon_url.spec()) {
       SetImageData(image, primary_icon);
       image->add_usages(webapk::Image::PRIMARY_ICON);
+      image->set_is_primay_icon_maskable(is_primary_icon_maskable);
     }
     if (entry.first == shortcut_info.best_badge_icon_url.spec()) {
       if (shortcut_info.best_badge_icon_url !=
@@ -289,6 +293,7 @@
     const base::FilePath& update_request_path,
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
+    bool is_primary_icon_maskable,
     const SkBitmap& badge_icon,
     const std::string& package_name,
     const std::string& version,
@@ -299,8 +304,9 @@
                                                 base::BlockingType::MAY_BLOCK);
 
   std::unique_ptr<std::string> proto = BuildProtoInBackground(
-      shortcut_info, primary_icon, badge_icon, package_name, version,
-      icon_url_to_murmur2_hash, is_manifest_stale, update_reason);
+      shortcut_info, primary_icon, is_primary_icon_maskable, badge_icon,
+      package_name, version, icon_url_to_murmur2_hash, is_manifest_stale,
+      update_reason);
 
   // Create directory if it does not exist.
   base::CreateDirectory(update_request_path.DirName());
@@ -339,12 +345,13 @@
 void WebApkInstaller::InstallAsync(content::BrowserContext* context,
                                    const ShortcutInfo& shortcut_info,
                                    const SkBitmap& primary_icon,
+                                   bool is_primary_icon_maskable,
                                    const SkBitmap& badge_icon,
                                    FinishCallback finish_callback) {
   // The installer will delete itself when it is done.
   WebApkInstaller* installer = new WebApkInstaller(context);
-  installer->InstallAsync(shortcut_info, primary_icon, badge_icon,
-                          std::move(finish_callback));
+  installer->InstallAsync(shortcut_info, primary_icon, is_primary_icon_maskable,
+                          badge_icon, std::move(finish_callback));
 }
 
 // static
@@ -360,10 +367,11 @@
 void WebApkInstaller::InstallAsyncForTesting(WebApkInstaller* installer,
                                              const ShortcutInfo& shortcut_info,
                                              const SkBitmap& primary_icon,
+                                             bool is_primary_icon_maskable,
                                              const SkBitmap& badge_icon,
                                              FinishCallback callback) {
-  installer->InstallAsync(shortcut_info, primary_icon, badge_icon,
-                          std::move(callback));
+  installer->InstallAsync(shortcut_info, primary_icon, is_primary_icon_maskable,
+                          badge_icon, std::move(callback));
 }
 
 // static
@@ -389,6 +397,7 @@
 void WebApkInstaller::BuildProto(
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
+    bool is_primary_icon_maskable,
     const SkBitmap& badge_icon,
     const std::string& package_name,
     const std::string& version,
@@ -398,8 +407,8 @@
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
       base::BindOnce(&BuildProtoInBackground, shortcut_info, primary_icon,
-                     badge_icon, package_name, version,
-                     icon_url_to_murmur2_hash, is_manifest_stale,
+                     is_primary_icon_maskable, badge_icon, package_name,
+                     version, icon_url_to_murmur2_hash, is_manifest_stale,
                      WebApkUpdateReason::NONE),
       std::move(callback));
 }
@@ -409,6 +418,7 @@
     const base::FilePath& update_request_path,
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
+    bool is_primary_icon_maskable,
     const SkBitmap& badge_icon,
     const std::string& package_name,
     const std::string& version,
@@ -419,8 +429,9 @@
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
       base::BindOnce(&StoreUpdateRequestToFileInBackground, update_request_path,
-                     shortcut_info, primary_icon, badge_icon, package_name,
-                     version, icon_url_to_murmur2_hash, is_manifest_stale,
+                     shortcut_info, primary_icon, is_primary_icon_maskable,
+                     badge_icon, package_name, version,
+                     icon_url_to_murmur2_hash, is_manifest_stale,
                      update_reason),
       std::move(callback));
 }
@@ -486,6 +497,7 @@
 
 void WebApkInstaller::InstallAsync(const ShortcutInfo& shortcut_info,
                                    const SkBitmap& primary_icon,
+                                   bool is_primary_icon_maskable,
                                    const SkBitmap& badge_icon,
                                    FinishCallback finish_callback) {
   install_duration_timer_.reset(new base::ElapsedTimer());
@@ -493,6 +505,7 @@
   install_shortcut_info_.reset(new ShortcutInfo(shortcut_info));
   install_primary_icon_ = primary_icon;
   install_badge_icon_ = badge_icon;
+  is_primary_icon_maskable_ = is_primary_icon_maskable;
   short_name_ = shortcut_info.short_name;
   finish_callback_ = std::move(finish_callback);
   task_type_ = INSTALL;
@@ -664,8 +677,9 @@
   }
 
   BuildProto(*install_shortcut_info_, install_primary_icon_,
-             install_badge_icon_, "" /* package_name */, "" /* version */,
-             icon_url_to_murmur2_hash, false /* is_manifest_stale */,
+             is_primary_icon_maskable_, install_badge_icon_,
+             "" /* package_name */, "" /* version */, icon_url_to_murmur2_hash,
+             false /* is_manifest_stale */,
              base::BindOnce(&WebApkInstaller::SendRequest,
                             weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index 3739d6e..a888075 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -60,6 +60,7 @@
   static void InstallAsync(content::BrowserContext* context,
                            const ShortcutInfo& shortcut_info,
                            const SkBitmap& primary_icon,
+                           bool is_primary_icon_maskable,
                            const SkBitmap& badge_icon,
                            FinishCallback finish_callback);
 
@@ -76,6 +77,7 @@
   static void InstallAsyncForTesting(WebApkInstaller* installer,
                                      const ShortcutInfo& shortcut_info,
                                      const SkBitmap& primary_icon,
+                                     bool is_primary_icon_maskable,
                                      const SkBitmap& badge_icon,
                                      FinishCallback callback);
 
@@ -106,6 +108,8 @@
   static void BuildProto(
       const ShortcutInfo& shortcut_info,
       const SkBitmap& primary_icon,
+      bool is_primary_icon_maskable,
+
       const SkBitmap& badge_icon,
       const std::string& package_name,
       const std::string& version,
@@ -120,6 +124,7 @@
       const base::FilePath& update_request_path,
       const ShortcutInfo& shortcut_info,
       const SkBitmap& primary_icon,
+      bool is_primary_icon_maskable,
       const SkBitmap& badge_icon,
       const std::string& package_name,
       const std::string& version,
@@ -158,6 +163,7 @@
   // install completed or failed.
   void InstallAsync(const ShortcutInfo& shortcut_info,
                     const SkBitmap& primary_icon,
+                    bool is_primary_icon_maskable,
                     const SkBitmap& badge_icon,
                     FinishCallback finish_callback);
 
@@ -214,6 +220,8 @@
   SkBitmap install_primary_icon_;
   SkBitmap install_badge_icon_;
 
+  bool is_primary_icon_maskable_;
+
   base::string16 short_name_;
 
   // WebAPK server URL.
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 2d91b23f..8f88933 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -114,7 +114,7 @@
 
     // WebApkInstaller owns itself.
     WebApkInstaller::InstallAsyncForTesting(
-        installer.release(), info, SkBitmap(), SkBitmap(),
+        installer.release(), info, SkBitmap(), false, SkBitmap(),
         base::BindOnce(&WebApkInstallerRunner::OnCompleted,
                        base::Unretained(this)));
 
@@ -164,8 +164,8 @@
     base::RunLoop run_loop;
     quit_closure_ = run_loop.QuitClosure();
     WebApkInstaller::StoreUpdateRequestToFile(
-        update_request_path, ShortcutInfo((GURL())), SkBitmap(), SkBitmap(), "",
-        "", std::map<std::string, std::string>(), false,
+        update_request_path, ShortcutInfo((GURL())), SkBitmap(), false,
+        SkBitmap(), "", "", std::map<std::string, std::string>(), false,
         WebApkUpdateReason::PRIMARY_ICON_HASH_DIFFERS,
         base::BindOnce(&UpdateRequestStorer::OnComplete,
                        base::Unretained(this)));
@@ -216,8 +216,9 @@
     SkBitmap primary_icon(gfx::test::CreateBitmap(144, 144));
     SkBitmap badge_icon(gfx::test::CreateBitmap(72, 72));
     WebApkInstaller::BuildProto(
-        info, primary_icon, badge_icon, "" /* package_name */, "" /* version */,
-        icon_url_to_murmur2_hash, is_manifest_stale,
+        info, primary_icon, false /* is_primary_icon_maskable */, badge_icon,
+        "" /* package_name */, "" /* version */, icon_url_to_murmur2_hash,
+        is_manifest_stale,
         base::BindOnce(&BuildProtoRunner::OnBuiltWebApkProto,
                        base::Unretained(this)));
 
diff --git a/chrome/browser/android/webapk/webapk_types.h b/chrome/browser/android/webapk/webapk_types.h
index 73d202f9..197e527 100644
--- a/chrome/browser/android/webapk/webapk_types.h
+++ b/chrome/browser/android/webapk/webapk_types.h
@@ -13,6 +13,7 @@
   NONE,
   OLD_SHELL_APK,
   PRIMARY_ICON_HASH_DIFFERS,
+  PRIMARY_ICON_MASKABLE_DIFFERS,
   BADGE_ICON_HASH_DIFFERS,
   SCOPE_DIFFERS,
   START_URL_DIFFERS,
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
index 040376e..a8e8c7b7 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
@@ -7,6 +7,7 @@
 #include <jni.h>
 #include <vector>
 
+#include "base/android/build_info.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
@@ -34,6 +35,12 @@
 
 namespace {
 
+bool DoesAndroidSupportMaskableIcons() {
+  // TODO(crbug.com/977173): re-enable maskable icon support once server support
+  // is ready.
+  return false;
+}
+
 // Returns whether the given |url| is within the scope of the |scope| url.
 bool IsInScope(const GURL& url, const GURL& scope) {
   return base::StartsWith(url.spec(), scope.spec(),
@@ -63,6 +70,7 @@
       scope_(scope),
       web_manifest_url_(web_manifest_url),
       info_(GURL()),
+      is_primary_icon_maskable_(false),
       weak_ptr_factory_(this) {
   java_ref_.Reset(env, obj);
 }
@@ -111,6 +119,7 @@
 
   InstallableParams params;
   params.valid_manifest = true;
+  params.prefer_maskable_icon = DoesAndroidSupportMaskableIcons();
   params.has_worker = true;
   params.valid_primary_icon = true;
   params.valid_badge_icon = true;
@@ -148,6 +157,7 @@
   info_.manifest_url = data.manifest_url;
   info_.best_primary_icon_url = data.primary_icon_url;
   primary_icon_ = *data.primary_icon;
+  is_primary_icon_maskable_ = data.has_maskable_primary_icon;
 
   if (data.badge_icon && !data.badge_icon->drawsNothing()) {
     info_.best_badge_icon_url = data.badge_icon_url;
@@ -213,6 +223,7 @@
       base::android::ConvertUTF8ToJavaString(env, primary_icon_murmur2_hash);
   ScopedJavaLocalRef<jobject> java_primary_icon =
       gfx::ConvertToJavaBitmap(&primary_icon_);
+  jboolean java_is_primary_icon_maskable = is_primary_icon_maskable_;
   ScopedJavaLocalRef<jstring> java_badge_icon_url =
       base::android::ConvertUTF8ToJavaString(env,
                                              info_.best_badge_icon_url.spec());
@@ -264,8 +275,9 @@
   Java_WebApkUpdateDataFetcher_onDataAvailable(
       env, java_ref_, java_url, java_scope, java_name, java_short_name,
       java_primary_icon_url, java_primary_icon_murmur2_hash, java_primary_icon,
-      java_badge_icon_url, java_badge_icon_murmur2_hash, java_badge_icon,
-      java_icon_urls, info_.display, info_.orientation,
+      java_is_primary_icon_maskable, java_badge_icon_url,
+      java_badge_icon_murmur2_hash, java_badge_icon, java_icon_urls,
+      info_.display, info_.orientation,
       OptionalSkColorToJavaColor(info_.theme_color),
       OptionalSkColorToJavaColor(info_.background_color), java_share_action,
       java_share_params_title, java_share_params_text, java_share_params_url,
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.h b/chrome/browser/android/webapk/webapk_update_data_fetcher.h
index c69ec782..f11ab18 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.h
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.h
@@ -82,6 +82,8 @@
   // Downloaded data for |web_manifest_url_|.
   ShortcutInfo info_;
   SkBitmap primary_icon_;
+  bool is_primary_icon_maskable_;
+
   SkBitmap badge_icon_;
 
   base::WeakPtrFactory<WebApkUpdateDataFetcher> weak_ptr_factory_;
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index d9986a5b0..332434f 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -54,6 +54,7 @@
     const JavaParamRef<jstring>& java_short_name,
     const JavaParamRef<jstring>& java_primary_icon_url,
     const JavaParamRef<jobject>& java_primary_icon_bitmap,
+    jboolean java_is_primary_icon_maskable,
     const JavaParamRef<jstring>& java_badge_icon_url,
     const JavaParamRef<jobject>& java_badge_icon_bitmap,
     const JavaParamRef<jobjectArray>& java_icon_urls,
@@ -166,9 +167,10 @@
       static_cast<WebApkUpdateReason>(java_update_reason);
 
   WebApkInstaller::StoreUpdateRequestToFile(
-      base::FilePath(update_request_path), info, primary_icon, badge_icon,
-      webapk_package, std::to_string(java_webapk_version),
-      icon_url_to_murmur2_hash, java_is_manifest_stale, update_reason,
+      base::FilePath(update_request_path), info, primary_icon,
+      java_is_primary_icon_maskable, badge_icon, webapk_package,
+      std::to_string(java_webapk_version), icon_url_to_murmur2_hash,
+      java_is_manifest_stale, update_reason,
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(java_callback)));
 }
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
index e2626c4..411ff3f7 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -46,8 +46,9 @@
 }
 
 bool DoesAndroidSupportMaskableIcons() {
-  return base::android::BuildInfo::GetInstance()->sdk_int() >=
-         base::android::SDK_VERSION_OREO;
+  // TODO(crbug.com/977173): re-enable maskable icon support once server support
+  // is ready.
+  return false;
 }
 
 InstallableParams ParamsToPerformManifestAndIconFetch() {
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
index 48384ab..4acdcae3 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
@@ -140,7 +140,8 @@
       is_installable = false;
     } else if (params.valid_manifest && params.has_worker) {
       if (!IsManifestValidForWebApp(manifest_,
-                                    true /* check_webapp_manifest_display */)) {
+                                    true /* check_webapp_manifest_display */,
+                                    false /* prefer_maskable_icon */)) {
         code = valid_manifest_->errors.at(0);
         is_installable = false;
       } else if (!is_installable_) {
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.cc b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
index b882493..22e3aa0 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
@@ -70,6 +70,7 @@
     WebApkInstallService::Get(web_contents->GetBrowserContext())
         ->InstallAsync(web_contents, data_fetcher_->shortcut_info(),
                        data_fetcher_->primary_icon(),
+                       data_fetcher_->has_maskable_primary_icon(),
                        data_fetcher_->badge_icon(),
                        InstallableMetrics::GetInstallSource(
                            web_contents, InstallTrigger::MENU));
diff --git a/chrome/browser/android/webapps/webapk_ukm_recorder.cc b/chrome/browser/android/webapps/webapk_ukm_recorder.cc
new file mode 100644
index 0000000..3cdaabb
--- /dev/null
+++ b/chrome/browser/android/webapps/webapk_ukm_recorder.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 "chrome/browser/android/webapps/webapk_ukm_recorder.h"
+
+#include <jni.h>
+
+#include "base/android/jni_string.h"
+#include "chrome/android/chrome_jni_headers/WebApkUkmRecorder_jni.h"
+#include "services/metrics/public/cpp/metrics_utils.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "url/gurl.h"
+
+using base::android::JavaParamRef;
+
+// static
+void WebApkUkmRecorder::RecordSessionDuration(const GURL& manifest_url,
+                                              int64_t distributor,
+                                              int64_t version_code,
+                                              int64_t duration) {
+  if (!manifest_url.is_valid())
+    return;
+
+  ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+  ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
+  ukm_recorder->UpdateSourceURL(source_id, manifest_url);
+  ukm::builders::WebAPK_SessionEnd(source_id)
+      .SetDistributor(distributor)
+      .SetAppVersion(version_code)
+      .SetSessionDuration(ukm::GetExponentialBucketMinForUserTiming(duration))
+      .Record(ukm_recorder);
+}
+
+// Called by the Java counterpart to record the Session Duration UKM metric.
+void JNI_WebApkUkmRecorder_RecordSessionDuration(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& manifest_url,
+    jint distributor,
+    jint version_code,
+    jlong duration) {
+  WebApkUkmRecorder::RecordSessionDuration(
+      GURL(base::android::ConvertJavaStringToUTF8(env, manifest_url)),
+      distributor, version_code, duration);
+}
diff --git a/chrome/browser/android/webapps/webapk_ukm_recorder.h b/chrome/browser/android/webapps/webapk_ukm_recorder.h
new file mode 100644
index 0000000..2c5b3c2b
--- /dev/null
+++ b/chrome/browser/android/webapps/webapk_ukm_recorder.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_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
+#define CHROME_BROWSER_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+
+class GURL;
+
+// WebApkUkmRecorder is the C++ counterpart of
+// org.chromium.chrome.browser.webapps's WebApkUkmRecorder in Java.
+// It contains static WebAPK UKM metrics-recording logic, and only
+// needs to be in a class so that it can be a friend of ukm::UkmRecorder.
+// All of the actual JNI goes through raw functions in webapk_ukm_recorder.cc to
+// avoid having to instantiate this class and deal with object lifetimes.
+class WebApkUkmRecorder {
+ public:
+  static void RecordSessionDuration(const GURL& manifest_url,
+                                    int64_t distributor,
+                                    int64_t version_code,
+                                    int64_t duration);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WebApkUkmRecorder);
+};
+
+#endif  // CHROME_BROWSER_ANDROID_WEBAPPS_WEBAPK_UKM_RECORDER_H_
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index a919a8e6..d8d517e 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -11,11 +11,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/dip_px_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/apps/app_service/app_service_proxy_impl.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
similarity index 73%
rename from chrome/browser/apps/app_service/app_service_proxy_impl.cc
rename to chrome/browser/apps/app_service/app_service_proxy.cc
index c8f17d5..f904890b 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_impl.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.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/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 
 #include <utility>
 
@@ -16,10 +16,10 @@
 
 namespace apps {
 
-AppServiceProxyImpl::InnerIconLoader::InnerIconLoader(AppServiceProxyImpl* host)
+AppServiceProxy::InnerIconLoader::InnerIconLoader(AppServiceProxy* host)
     : host_(host), overriding_icon_loader_for_testing_(nullptr) {}
 
-apps::mojom::IconKeyPtr AppServiceProxyImpl::InnerIconLoader::GetIconKey(
+apps::mojom::IconKeyPtr AppServiceProxy::InnerIconLoader::GetIconKey(
     const std::string& app_id) {
   if (overriding_icon_loader_for_testing_) {
     return overriding_icon_loader_for_testing_->GetIconKey(app_id);
@@ -35,7 +35,7 @@
 }
 
 std::unique_ptr<IconLoader::Releaser>
-AppServiceProxyImpl::InnerIconLoader::LoadIconFromIconKey(
+AppServiceProxy::InnerIconLoader::LoadIconFromIconKey(
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
@@ -66,23 +66,17 @@
 }
 
 // static
-AppServiceProxyImpl* AppServiceProxyImpl::GetImplForTesting(Profile* profile) {
-  return static_cast<AppServiceProxyImpl*>(
-      AppServiceProxyFactory::GetForProfile(profile));
-}
-
-// static
-AppServiceProxyImpl* AppServiceProxyImpl::CreateForTesting(
+AppServiceProxy* AppServiceProxy::CreateForTesting(
     Profile* profile,
     service_manager::Connector* connector) {
-  return new AppServiceProxyImpl(profile, connector);
+  return new AppServiceProxy(profile, connector);
 }
 
-AppServiceProxyImpl::AppServiceProxyImpl(Profile* profile)
-    : AppServiceProxyImpl(profile, nullptr) {}
+AppServiceProxy::AppServiceProxy(Profile* profile)
+    : AppServiceProxy(profile, nullptr) {}
 
-AppServiceProxyImpl::AppServiceProxyImpl(Profile* profile,
-                                         service_manager::Connector* connector)
+AppServiceProxy::AppServiceProxy(Profile* profile,
+                                 service_manager::Connector* connector)
     : inner_icon_loader_(this),
       icon_coalescer_(&inner_icon_loader_),
       outer_icon_loader_(&icon_coalescer_,
@@ -100,15 +94,15 @@
                            mojo::MakeRequest(&app_service_));
 
   if (app_service_.is_bound()) {
-    // The AppServiceProxyImpl is a subscriber: something that wants to be able
+    // The AppServiceProxy is a subscriber: something that wants to be able
     // to list all known apps.
     apps::mojom::SubscriberPtr subscriber;
     bindings_.AddBinding(this, mojo::MakeRequest(&subscriber));
     app_service_->RegisterSubscriber(std::move(subscriber), nullptr);
 
 #if defined(OS_CHROMEOS)
-    // The AppServiceProxyImpl is also a publisher, of a variety of app types.
-    // That responsibility isn't intrinsically part of the AppServiceProxyImpl,
+    // The AppServiceProxy is also a publisher, of a variety of app types.
+    // That responsibility isn't intrinsically part of the AppServiceProxy,
     // but doing that here, for each such app type, is as good a place as any.
     built_in_chrome_os_apps_.Initialize(app_service_, profile);
     crostini_apps_.Initialize(app_service_, profile);
@@ -120,15 +114,22 @@
   }
 }
 
-AppServiceProxyImpl::~AppServiceProxyImpl() = default;
+AppServiceProxy::~AppServiceProxy() = default;
 
-apps::mojom::IconKeyPtr AppServiceProxyImpl::GetIconKey(
-    const std::string& app_id) {
+apps::mojom::AppServicePtr& AppServiceProxy::AppService() {
+  return app_service_;
+}
+
+apps::AppRegistryCache& AppServiceProxy::AppRegistryCache() {
+  return cache_;
+}
+
+apps::mojom::IconKeyPtr AppServiceProxy::GetIconKey(const std::string& app_id) {
   return outer_icon_loader_.GetIconKey(app_id);
 }
 
 std::unique_ptr<apps::IconLoader::Releaser>
-AppServiceProxyImpl::LoadIconFromIconKey(
+AppServiceProxy::LoadIconFromIconKey(
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
@@ -141,10 +142,10 @@
       allow_placeholder_icon, std::move(callback));
 }
 
-void AppServiceProxyImpl::Launch(const std::string& app_id,
-                                 int32_t event_flags,
-                                 apps::mojom::LaunchSource launch_source,
-                                 int64_t display_id) {
+void AppServiceProxy::Launch(const std::string& app_id,
+                             int32_t event_flags,
+                             apps::mojom::LaunchSource launch_source,
+                             int64_t display_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this, event_flags, launch_source,
                               display_id](const apps::AppUpdate& update) {
@@ -154,8 +155,8 @@
   }
 }
 
-void AppServiceProxyImpl::SetPermission(const std::string& app_id,
-                                        apps::mojom::PermissionPtr permission) {
+void AppServiceProxy::SetPermission(const std::string& app_id,
+                                    apps::mojom::PermissionPtr permission) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(
         app_id, [this, &permission](const apps::AppUpdate& update) {
@@ -165,7 +166,7 @@
   }
 }
 
-void AppServiceProxyImpl::Uninstall(const std::string& app_id) {
+void AppServiceProxy::Uninstall(const std::string& app_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
       app_service_->Uninstall(update.AppType(), update.AppId());
@@ -173,7 +174,7 @@
   }
 }
 
-void AppServiceProxyImpl::OpenNativeSettings(const std::string& app_id) {
+void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {
   if (app_service_.is_bound()) {
     cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
       app_service_->OpenNativeSettings(update.AppType(), update.AppId());
@@ -181,11 +182,11 @@
   }
 }
 
-void AppServiceProxyImpl::FlushMojoCallsForTesting() {
+void AppServiceProxy::FlushMojoCallsForTesting() {
   bindings_.FlushForTesting();
 }
 
-apps::IconLoader* AppServiceProxyImpl::OverrideInnerIconLoaderForTesting(
+apps::IconLoader* AppServiceProxy::OverrideInnerIconLoaderForTesting(
     apps::IconLoader* icon_loader) {
   apps::IconLoader* old =
       inner_icon_loader_.overriding_icon_loader_for_testing_;
@@ -193,7 +194,7 @@
   return old;
 }
 
-void AppServiceProxyImpl::ReInitializeCrostiniForTesting(Profile* profile) {
+void AppServiceProxy::ReInitializeCrostiniForTesting(Profile* profile) {
 #if defined(OS_CHROMEOS)
   if (app_service_.is_bound()) {
     crostini_apps_.ReInitializeForTesting(app_service_, profile);
@@ -201,7 +202,7 @@
 #endif
 }
 
-void AppServiceProxyImpl::Shutdown() {
+void AppServiceProxy::Shutdown() {
 #if defined(OS_CHROMEOS)
   if (app_service_.is_bound()) {
     extension_apps_.Shutdown();
@@ -210,11 +211,11 @@
 #endif  // OS_CHROMEOS
 }
 
-void AppServiceProxyImpl::OnApps(std::vector<apps::mojom::AppPtr> deltas) {
+void AppServiceProxy::OnApps(std::vector<apps::mojom::AppPtr> deltas) {
   cache_.OnApps(std::move(deltas));
 }
 
-void AppServiceProxyImpl::Clone(apps::mojom::SubscriberRequest request) {
+void AppServiceProxy::Clone(apps::mojom::SubscriberRequest request) {
   bindings_.AddBinding(this, std::move(request));
 }
 
diff --git a/chrome/browser/apps/app_service/app_service_proxy_impl.h b/chrome/browser/apps/app_service/app_service_proxy.h
similarity index 77%
rename from chrome/browser/apps/app_service/app_service_proxy_impl.h
rename to chrome/browser/apps/app_service/app_service_proxy.h
index 3cbfc30..d74151c 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_impl.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
-#define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
+#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_coalescer.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -34,27 +34,22 @@
 // proxy for a given Profile, and therefore share its caches.
 //
 // See chrome/services/app_service/README.md.
-class AppServiceProxyImpl : public KeyedService,
-                            public apps::AppServiceProxy,
-                            public apps::mojom::Subscriber {
+class AppServiceProxy : public KeyedService,
+                        public apps::IconLoader,
+                        public apps::mojom::Subscriber {
  public:
-  // This method returns an AppServiceProxyImpl, not just an AppServiceProxy, so
-  // that callers (which are presumably in test code) can then call other
-  // XxxForTesting methods.
-  //
-  // For regular (non-test) code, use AppServiceProxyFactory::GetForProfile
-  // instead.
-  static AppServiceProxyImpl* GetImplForTesting(Profile* profile);
-
-  static AppServiceProxyImpl* CreateForTesting(
+  static AppServiceProxy* CreateForTesting(
       Profile* profile,
       service_manager::Connector* connector);
 
-  explicit AppServiceProxyImpl(Profile* profile);
+  explicit AppServiceProxy(Profile* profile);
 
-  ~AppServiceProxyImpl() override;
+  ~AppServiceProxy() override;
 
-  // apps::AppServiceProxy (including apps::IconLoader) overrides.
+  apps::mojom::AppServicePtr& AppService();
+  apps::AppRegistryCache& AppRegistryCache();
+
+  // apps::IconLoader overrides.
   apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
   std::unique_ptr<IconLoader::Releaser> LoadIconFromIconKey(
       apps::mojom::AppType app_type,
@@ -64,14 +59,16 @@
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
+
+  // TODO: Provide comments for public API methods.
   void Launch(const std::string& app_id,
               int32_t event_flags,
               apps::mojom::LaunchSource launch_source,
-              int64_t display_id) override;
+              int64_t display_id);
   void SetPermission(const std::string& app_id,
-                     apps::mojom::PermissionPtr permission) override;
-  void Uninstall(const std::string& app_id) override;
-  void OpenNativeSettings(const std::string& app_id) override;
+                     apps::mojom::PermissionPtr permission);
+  void Uninstall(const std::string& app_id);
+  void OpenNativeSettings(const std::string& app_id);
 
   void FlushMojoCallsForTesting();
   apps::IconLoader* OverrideInnerIconLoaderForTesting(
@@ -82,7 +79,7 @@
   // An adapter, presenting an IconLoader interface based on the underlying
   // Mojo service (or on a fake implementation for testing).
   //
-  // Conceptually, the ASP (the AppServiceProxyImpl) is itself such an adapter:
+  // Conceptually, the ASP (the AppServiceProxy) is itself such an adapter:
   // UI clients call the IconLoader::LoadIconFromIconKey method (which the ASP
   // implements) and the ASP translates (i.e. adapts) these to Mojo calls (or
   // C++ calls to the Fake). This diagram shows control flow going left to
@@ -112,7 +109,7 @@
   // component: the one that ultimately talks to the Mojo service.
   //
   // The outer_icon_loader_ field (of type IconCache) is the "Outer" component:
-  // the entry point for calls into the AppServiceProxyImpl.
+  // the entry point for calls into the AppServiceProxy.
   //
   // Note that even if the ASP provides some icon caching, upstream UI clients
   // may want to introduce further icon caching. See the commentary where
@@ -121,7 +118,7 @@
   // IPC coalescing would be one of the "MoreDecorators".
   class InnerIconLoader : public apps::IconLoader {
    public:
-    explicit InnerIconLoader(AppServiceProxyImpl* host);
+    explicit InnerIconLoader(AppServiceProxy* host);
 
     // apps::IconLoader overrides.
     apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
@@ -134,14 +131,14 @@
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override;
 
-    // |host_| owns |this|, as the InnerIconLoader is an AppServiceProxyImpl
+    // |host_| owns |this|, as the InnerIconLoader is an AppServiceProxy
     // field.
-    AppServiceProxyImpl* host_;
+    AppServiceProxy* host_;
 
     apps::IconLoader* overriding_icon_loader_for_testing_;
   };
 
-  AppServiceProxyImpl(Profile* profile, service_manager::Connector* connector);
+  AppServiceProxy(Profile* profile, service_manager::Connector* connector);
 
   // KeyedService overrides.
   void Shutdown() override;
@@ -150,6 +147,9 @@
   void OnApps(std::vector<apps::mojom::AppPtr> deltas) override;
   void Clone(apps::mojom::SubscriberRequest request) override;
 
+  apps::mojom::AppServicePtr app_service_;
+  apps::AppRegistryCache cache_;
+
   mojo::BindingSet<apps::mojom::Subscriber> bindings_;
 
   // The LoadIconFromIconKey implementation sends a chained series of requests
@@ -168,9 +168,9 @@
   ExtensionApps extension_web_apps_;
 #endif  // OS_CHROMEOS
 
-  DISALLOW_COPY_AND_ASSIGN(AppServiceProxyImpl);
+  DISALLOW_COPY_AND_ASSIGN(AppServiceProxy);
 };
 
 }  // namespace apps
 
-#endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_IMPL_H_
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
diff --git a/chrome/browser/apps/app_service/app_service_proxy_factory.cc b/chrome/browser/apps/app_service/app_service_proxy_factory.cc
index d744503..c576d416 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_factory.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_factory.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 
 #include "base/feature_list.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -27,7 +27,7 @@
   //     is branched from (i.e. "inherit" the parent service),
   //   - return a temporary service just for the incognito session (probably
   //     the least sensible option).
-  return static_cast<AppServiceProxyImpl*>(
+  return static_cast<AppServiceProxy*>(
       AppServiceProxyFactory::GetInstance()->GetServiceForBrowserContext(
           profile, true /* create */));
 }
@@ -59,7 +59,7 @@
 
 KeyedService* AppServiceProxyFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new AppServiceProxyImpl(Profile::FromBrowserContext(context));
+  return new AppServiceProxy(Profile::FromBrowserContext(context));
 }
 
 bool AppServiceProxyFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
similarity index 92%
rename from chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc
rename to chrome/browser/apps/app_service/app_service_proxy_unittest.cc
index 57ab4c49..09f986c 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_impl_unittest.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
@@ -6,12 +6,12 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia_rep.h"
 
-class AppServiceProxyImplTest : public testing::Test {
+class AppServiceProxyTest : public testing::Test {
  protected:
   using UniqueReleaser = std::unique_ptr<apps::IconLoader::Releaser>;
 
@@ -66,11 +66,11 @@
 
     return loader->LoadIcon(app_type, app_id, icon_compression,
                             size_hint_in_dip, allow_placeholder_icon,
-                            base::BindOnce(&AppServiceProxyImplTest::OnLoadIcon,
+                            base::BindOnce(&AppServiceProxyTest::OnLoadIcon,
                                            base::Unretained(this)));
   }
 
-  void OverrideAppServiceProxyInnerIconLoader(apps::AppServiceProxyImpl* proxy,
+  void OverrideAppServiceProxyInnerIconLoader(apps::AppServiceProxy* proxy,
                                               apps::IconLoader* icon_loader) {
     proxy->OverrideInnerIconLoaderForTesting(icon_loader);
   }
@@ -84,14 +84,14 @@
   int num_outer_finished_callbacks_ = 0;
 };
 
-TEST_F(AppServiceProxyImplTest, IconCache) {
+TEST_F(AppServiceProxyTest, IconCache) {
   // This is mostly a sanity check. For an isolated, comprehensive unit test of
   // the IconCache code, see icon_cache_unittest.cc.
   //
-  // This tests an AppServiceProxyImpl as a 'black box', which uses an
+  // This tests an AppServiceProxy as a 'black box', which uses an
   // IconCache but also other IconLoader filters, such as an IconCoalescer.
 
-  apps::AppServiceProxyImpl proxy(nullptr);
+  apps::AppServiceProxy proxy(nullptr);
   FakeIconLoader fake;
   OverrideAppServiceProxyInnerIconLoader(&proxy, &fake);
 
@@ -130,14 +130,14 @@
   EXPECT_EQ(3, NumOuterFinishedCallbacks());
 }
 
-TEST_F(AppServiceProxyImplTest, IconCoalescer) {
+TEST_F(AppServiceProxyTest, IconCoalescer) {
   // This is mostly a sanity check. For an isolated, comprehensive unit test of
   // the IconCoalescer code, see icon_coalescer_unittest.cc.
   //
-  // This tests an AppServiceProxyImpl as a 'black box', which uses an
+  // This tests an AppServiceProxy as a 'black box', which uses an
   // IconCoalescer but also other IconLoader filters, such as an IconCache.
 
-  apps::AppServiceProxyImpl proxy(nullptr);
+  apps::AppServiceProxy proxy(nullptr);
   FakeIconLoader fake;
   OverrideAppServiceProxyInnerIconLoader(&proxy, &fake);
 
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 02f978e..90e3ac0 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/arc_apps_factory.h"
 #include "chrome/browser/apps/app_service/dip_px_util.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/grit/component_extension_resources.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/arc/app_permissions/arc_app_permissions_bridge.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/app.mojom.h"
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index d2365135..f2c8bba7 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -119,7 +119,9 @@
 
   bool show_persistence_options = ShouldShowPersistenceOptions(apps);
   ShowIntentPickerBubbleForApps(
-      web_contents, std::move(apps), show_persistence_options,
+      web_contents, std::move(apps),
+      /*show_stay_in_chrome=*/show_persistence_options,
+      /*show_remember_selection=*/show_persistence_options,
       base::BindOnce(&OnIntentPickerClosed, web_contents,
                      ui_auto_display_service, url));
 }
@@ -203,7 +205,8 @@
 void AppsNavigationThrottle::ShowIntentPickerBubbleForApps(
     content::WebContents* web_contents,
     std::vector<IntentPickerAppInfo> apps,
-    bool show_persistence_options,
+    bool show_stay_in_chrome,
+    bool show_remember_selection,
     IntentPickerResponse callback) {
   if (apps.empty())
     return;
@@ -214,10 +217,9 @@
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   if (!browser)
     return;
-  browser->window()->ShowIntentPickerBubble(std::move(apps),
-                                            /*enable_stay_in_chrome=*/true,
-                                            show_persistence_options,
-                                            std::move(callback));
+  browser->window()->ShowIntentPickerBubble(
+      std::move(apps), show_stay_in_chrome, show_remember_selection,
+      std::move(callback));
 }
 
 AppsNavigationThrottle::AppsNavigationThrottle(
@@ -359,6 +361,9 @@
   // if only PWAs are present.
   // TODO(crbug.com/826982): Provide the "Remember my choice" option when the
   // app registry can support persistence for PWAs.
+  // This function is also used to hide the "Stay In Chrome" button when the
+  // "Remember my choice" option is hidden such that the bubble is easy to
+  // understand.
   return !ContainsOnlyPwas(apps);
 }
 
@@ -390,9 +395,11 @@
       break;
     case PickerShowState::kPopOut: {
       bool show_persistence_options = ShouldShowPersistenceOptions(apps);
-      ShowIntentPickerBubbleForApps(web_contents, std::move(apps),
-                                    show_persistence_options,
-                                    std::move(callback));
+      ShowIntentPickerBubbleForApps(
+          web_contents, std::move(apps),
+          /*show_stay_in_chrome=*/show_persistence_options,
+          /*show_remember_selection=*/show_persistence_options,
+          std::move(callback));
       break;
     }
     default:
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
index 62c1576..ef14b2f7 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
@@ -83,7 +83,8 @@
   static void ShowIntentPickerBubbleForApps(
       content::WebContents* web_contents,
       std::vector<IntentPickerAppInfo> apps,
-      bool show_persistence_options,
+      bool show_stay_in_chrome,
+      bool show_remember_selection,
       IntentPickerResponse callback);
 
   explicit AppsNavigationThrottle(content::NavigationHandle* navigation_handle);
diff --git a/chrome/browser/apps/launch_service/README.md b/chrome/browser/apps/launch_service/README.md
index ea7bb257..7fa3261c 100644
--- a/chrome/browser/apps/launch_service/README.md
+++ b/chrome/browser/apps/launch_service/README.md
@@ -5,7 +5,7 @@
 and BMO-based Desktop PWAs can be launched using this service.
 
 The intent is to merge LaunchService into the AppService,
-specifically AppServiceProxyImpl.
+specifically AppServiceProxy.
 
 See `//chrome/services/app_service/README.md` for a description of the
 App Service.
diff --git a/chrome/browser/apps/user_type_filter_unittest.cc b/chrome/browser/apps/user_type_filter_unittest.cc
index 5d7d0f8..652c69e 100644
--- a/chrome/browser/apps/user_type_filter_unittest.cc
+++ b/chrome/browser/apps/user_type_filter_unittest.cc
@@ -74,6 +74,7 @@
   DISALLOW_COPY_AND_ASSIGN(UserTypeFilterTest);
 };
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 TEST_F(UserTypeFilterTest, ChildUser) {
   const auto profile = CreateProfile();
   profile->SetSupervisedUserId(supervised_users::kChildAccountSUID);
@@ -82,6 +83,7 @@
   EXPECT_TRUE(Match(
       profile, CreateJsonWithFilter({kUserTypeUnmanaged, kUserTypeChild})));
 }
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 TEST_F(UserTypeFilterTest, GuestUser) {
   auto profile = CreateGuestProfile();
@@ -128,12 +130,16 @@
   EXPECT_TRUE(MatchDefault(profile, default_filter));
   // Guest user.
   EXPECT_TRUE(MatchDefault(CreateGuestProfile(), default_filter));
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // Child user.
   profile->SetSupervisedUserId(supervised_users::kChildAccountSUID);
   EXPECT_FALSE(MatchDefault(profile, default_filter));
   // Supervised user.
+  // TODO(crbug.com/971311): Remove the next assert test once legacy supervised
+  // user code has been fully removed.
   profile->SetSupervisedUserId("asdf");
   EXPECT_FALSE(MatchDefault(profile, default_filter));
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // Managed user.
   profile = CreateProfile();
   profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 805faf52..5f6724e5 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -425,6 +425,11 @@
   content::WebContents* active_tab = nullptr;
   if (active_browser)
     active_tab = active_browser->tab_strip_model()->GetActiveWebContents();
+  const AutocompleteInput empty_input;
+  if (!input)
+    input = &empty_input;
+  const GURL stripped_url = AutocompleteMatch::GURLToStrippedGURL(
+      url, *input, GetTemplateURLService(), base::string16());
   for (auto* browser : *BrowserList::GetInstance()) {
     // Only look at same profile (and anonymity level).
     if (browser->profile()->IsSameProfileAndType(profile_)) {
@@ -432,8 +437,8 @@
         content::WebContents* web_contents =
             browser->tab_strip_model()->GetWebContentsAt(i);
         if (web_contents != active_tab &&
-            StrippedURLsAreEqual(web_contents->GetLastCommittedURL(), url,
-                                 input))
+            IsURLEqualToStrippedURL(web_contents->GetLastCommittedURL(),
+                                    stripped_url, *input))
           return true;
       }
     }
@@ -463,3 +468,13 @@
          AutocompleteMatch::GURLToStrippedGURL(
              url2, *input, template_url_service, base::string16());
 }
+
+bool ChromeAutocompleteProviderClient::IsURLEqualToStrippedURL(
+    const GURL& url1,
+    const GURL& stripped_url2,
+    const AutocompleteInput& input) const {
+  const TemplateURLService* template_url_service = GetTemplateURLService();
+  return AutocompleteMatch::GURLToStrippedGURL(
+             url1, input, template_url_service, base::string16()) ==
+         stripped_url2;
+}
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index 043cd0a5..69052fe 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -87,6 +87,13 @@
                             const AutocompleteInput* input) const;
 
  private:
+  // Like StrippedURLsAreEqual(), but second URL is already stripped. The
+  // input corresponds to this second URL. This is a small optimization when
+  // comparing lots of URLs to a single one.
+  bool IsURLEqualToStrippedURL(const GURL& url1,
+                               const GURL& stripped_url2,
+                               const AutocompleteInput& input) const;
+
   Profile* profile_;
   ChromeAutocompleteSchemeClassifier scheme_classifier_;
   std::unique_ptr<OmniboxPedalProvider> pedal_provider_;
diff --git a/chrome/browser/availability/OWNERS b/chrome/browser/availability/OWNERS
new file mode 100644
index 0000000..fd70f95
--- /dev/null
+++ b/chrome/browser/availability/OWNERS
@@ -0,0 +1,3 @@
+file://components/previews/OWNERS
+
+# COMPONENT: Blink>Previews
diff --git a/chrome/browser/previews/previews_prober.cc b/chrome/browser/availability/availability_prober.cc
similarity index 77%
rename from chrome/browser/previews/previews_prober.cc
rename to chrome/browser/availability/availability_prober.cc
index 8662587..c960231 100644
--- a/chrome/browser/previews/previews_prober.cc
+++ b/chrome/browser/availability/availability_prober.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/previews/previews_prober.h"
+#include "chrome/browser/availability/availability_prober.h"
 
 #include <math.h>
 #include <cmath>
@@ -17,7 +17,7 @@
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "build/build_config.h"
-#include "chrome/browser/previews/proto/previews_prober_cache_entry.pb.h"
+#include "chrome/browser/availability/proto/availability_prober_cache_entry.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -37,24 +37,24 @@
 
 namespace {
 
-const char kCachePrefKeyPrefix[] = "previews.prober.cache";
+const char kCachePrefKeyPrefix[] = "Availability.Prober.cache";
 
-const char kSuccessHistogram[] = "Previews.Prober.DidSucceed";
-const char kTimeUntilSuccess[] = "Previews.Prober.TimeUntilSuccess";
-const char kTimeUntilFailure[] = "Previews.Prober.TimeUntilFailure";
+const char kSuccessHistogram[] = "Availability.Prober.DidSucceed";
+const char kTimeUntilSuccess[] = "Availability.Prober.TimeUntilSuccess";
+const char kTimeUntilFailure[] = "Availability.Prober.TimeUntilFailure";
 const char kAttemptsBeforeSuccessHistogram[] =
-    "Previews.Prober.NumAttemptsBeforeSuccess";
-const char kHttpRespCodeHistogram[] = "Previews.Prober.ResponseCode";
-const char kNetErrorHistogram[] = "Previews.Prober.NetError";
-const char kCacheEntryAgeHistogram[] = "Previews.Prober.CacheEntryAge";
+    "Availability.Prober.NumAttemptsBeforeSuccess";
+const char kHttpRespCodeHistogram[] = "Availability.Prober.ResponseCode";
+const char kNetErrorHistogram[] = "Availability.Prober.NetError";
+const char kCacheEntryAgeHistogram[] = "Availability.Prober.CacheEntryAge";
 
 // Please keep this up to date with logged histogram suffix
-// |Previews.Prober.Clients| in tools/metrics/histograms/histograms.xml.
+// |Availability.Prober.Clients| in tools/metrics/histograms/histograms.xml.
 // These names are also used in prefs so they should not be changed without
 // consideration for removing the old value.
-std::string NameForClient(PreviewsProber::ClientName name) {
+std::string NameForClient(AvailabilityProber::ClientName name) {
   switch (name) {
-    case PreviewsProber::ClientName::kLitepages:
+    case AvailabilityProber::ClientName::kLitepages:
       return "Litepages";
   }
   NOTREACHED();
@@ -65,24 +65,25 @@
   return base::StringPrintf("%s.%s", kCachePrefKeyPrefix, name.c_str());
 }
 
-std::string HttpMethodToString(PreviewsProber::HttpMethod http_method) {
+std::string HttpMethodToString(AvailabilityProber::HttpMethod http_method) {
   switch (http_method) {
-    case PreviewsProber::HttpMethod::kGet:
+    case AvailabilityProber::HttpMethod::kGet:
       return "GET";
-    case PreviewsProber::HttpMethod::kHead:
+    case AvailabilityProber::HttpMethod::kHead:
       return "HEAD";
   }
 }
 
 // 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) {
+base::TimeDelta ComputeNextTimeDeltaForBackoff(
+    AvailabilityProber::Backoff backoff,
+    base::TimeDelta base_interval,
+    size_t attempts_so_far) {
   switch (backoff) {
-    case PreviewsProber::Backoff::kLinear:
+    case AvailabilityProber::Backoff::kLinear:
       return base_interval;
-    case PreviewsProber::Backoff::kExponential:
+    case AvailabilityProber::Backoff::kExponential:
       return base_interval * pow(2, attempts_so_far);
   }
 }
@@ -122,7 +123,7 @@
 }
 
 base::Optional<base::Value> EncodeCacheEntryValue(
-    const PreviewsProberCacheEntry& entry) {
+    const AvailabilityProberCacheEntry& entry) {
   std::string serialized_entry;
   bool serialize_to_string_ok = entry.SerializeToString(&serialized_entry);
   if (!serialize_to_string_ok)
@@ -133,7 +134,7 @@
   return base::Value(base64_encoded);
 }
 
-base::Optional<PreviewsProberCacheEntry> DecodeCacheEntryValue(
+base::Optional<AvailabilityProberCacheEntry> DecodeCacheEntryValue(
     const base::Value& value) {
   if (!value.is_string())
     return base::nullopt;
@@ -142,7 +143,7 @@
   if (!base::Base64Decode(value.GetString(), &base64_decoded))
     return base::nullopt;
 
-  PreviewsProberCacheEntry entry;
+  AvailabilityProberCacheEntry entry;
   if (!entry.ParseFromString(base64_decoded))
     return base::nullopt;
 
@@ -150,7 +151,7 @@
 }
 
 base::Time LastModifiedTimeFromCacheEntry(
-    const PreviewsProberCacheEntry& entry) {
+    const AvailabilityProberCacheEntry& entry) {
   return base::Time::FromDeltaSinceWindowsEpoch(
       base::TimeDelta::FromMicroseconds(entry.last_modified()));
 }
@@ -161,7 +162,7 @@
   std::string oldest_key;
   base::Time oldest_mod_time = base::Time::Max();
   for (const auto& iter : dict->DictItems()) {
-    base::Optional<PreviewsProberCacheEntry> entry =
+    base::Optional<AvailabilityProberCacheEntry> entry =
         DecodeCacheEntryValue(iter.second);
     if (!entry.has_value()) {
       // Also remove anything that can't be decoded.
@@ -201,16 +202,16 @@
 
 }  // 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;
+AvailabilityProber::RetryPolicy::RetryPolicy() = default;
+AvailabilityProber::RetryPolicy::~RetryPolicy() = default;
+AvailabilityProber::RetryPolicy::RetryPolicy(
+    AvailabilityProber::RetryPolicy const&) = default;
+AvailabilityProber::TimeoutPolicy::TimeoutPolicy() = default;
+AvailabilityProber::TimeoutPolicy::~TimeoutPolicy() = default;
+AvailabilityProber::TimeoutPolicy::TimeoutPolicy(
+    AvailabilityProber::TimeoutPolicy const&) = default;
 
-PreviewsProber::PreviewsProber(
+AvailabilityProber::AvailabilityProber(
     Delegate* delegate,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     PrefService* pref_service,
@@ -223,22 +224,22 @@
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     const size_t max_cache_entries,
     base::TimeDelta revalidate_cache_after)
-    : PreviewsProber(delegate,
-                     url_loader_factory,
-                     pref_service,
-                     name,
-                     url,
-                     http_method,
-                     headers,
-                     retry_policy,
-                     timeout_policy,
-                     traffic_annotation,
-                     max_cache_entries,
-                     revalidate_cache_after,
-                     base::DefaultTickClock::GetInstance(),
-                     base::DefaultClock::GetInstance()) {}
+    : AvailabilityProber(delegate,
+                         url_loader_factory,
+                         pref_service,
+                         name,
+                         url,
+                         http_method,
+                         headers,
+                         retry_policy,
+                         timeout_policy,
+                         traffic_annotation,
+                         max_cache_entries,
+                         revalidate_cache_after,
+                         base::DefaultTickClock::GetInstance(),
+                         base::DefaultClock::GetInstance()) {}
 
-PreviewsProber::PreviewsProber(
+AvailabilityProber::AvailabilityProber(
     Delegate* delegate,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     PrefService* pref_service,
@@ -283,36 +284,36 @@
     AddSelfAsNetworkConnectionObserver(content::GetNetworkConnectionTracker());
   } else {
     content::GetNetworkConnectionTrackerFromUIThread(
-        base::BindOnce(&PreviewsProber::AddSelfAsNetworkConnectionObserver,
+        base::BindOnce(&AvailabilityProber::AddSelfAsNetworkConnectionObserver,
                        weak_factory_.GetWeakPtr()));
   }
   cached_probe_results_ =
       pref_service_->GetDictionary(pref_key_)->CreateDeepCopy();
 }
 
-PreviewsProber::~PreviewsProber() {
+AvailabilityProber::~AvailabilityProber() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (network_connection_tracker_)
     network_connection_tracker_->RemoveNetworkConnectionObserver(this);
 }
 
 // static
-void PreviewsProber::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  for (int i = 0; i <= static_cast<int>(PreviewsProber::ClientName::kMaxValue);
-       i++) {
+void AvailabilityProber::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  for (int i = 0;
+       i <= static_cast<int>(AvailabilityProber::ClientName::kMaxValue); i++) {
     registry->RegisterDictionaryPref(PrefKeyForName(
-        NameForClient(static_cast<PreviewsProber::ClientName>(i))));
+        NameForClient(static_cast<AvailabilityProber::ClientName>(i))));
   }
 }
 
-void PreviewsProber::AddSelfAsNetworkConnectionObserver(
+void AvailabilityProber::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() {
+void AvailabilityProber::ResetState() {
   time_when_set_active_ = base::nullopt;
   successive_retry_count_ = 0;
   successive_timeout_count_ = 0;
@@ -324,7 +325,7 @@
 #endif
 }
 
-void PreviewsProber::SendNowIfInactive(bool send_only_in_foreground) {
+void AvailabilityProber::SendNowIfInactive(bool send_only_in_foreground) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (time_when_set_active_.has_value())
@@ -336,8 +337,9 @@
     // base::Unretained is safe here because the callback is owned by
     // |application_status_listener_| which is owned by |this|.
     application_status_listener_ =
-        base::android::ApplicationStatusListener::New(base::BindRepeating(
-            &PreviewsProber::OnApplicationStateChange, base::Unretained(this)));
+        base::android::ApplicationStatusListener::New(
+            base::BindRepeating(&AvailabilityProber::OnApplicationStateChange,
+                                base::Unretained(this)));
     return;
   }
 #endif
@@ -346,7 +348,7 @@
 }
 
 #if defined(OS_ANDROID)
-void PreviewsProber::OnApplicationStateChange(
+void AvailabilityProber::OnApplicationStateChange(
     base::android::ApplicationState new_state) {
   DCHECK(application_status_listener_);
 
@@ -358,7 +360,8 @@
 }
 #endif
 
-void PreviewsProber::OnConnectionChanged(network::mojom::ConnectionType type) {
+void AvailabilityProber::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
@@ -367,7 +370,7 @@
   CreateAndStartURLLoader();
 }
 
-void PreviewsProber::CreateAndStartURLLoader() {
+void AvailabilityProber::CreateAndStartURLLoader() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!time_when_set_active_.has_value() || successive_retry_count_ > 0);
   DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
@@ -401,23 +404,23 @@
 
   url_loader_->DownloadToString(
       url_loader_factory_.get(),
-      base::BindOnce(&PreviewsProber::OnURLLoadComplete,
+      base::BindOnce(&AvailabilityProber::OnURLLoadComplete,
                      base::Unretained(this)),
       1024);
 
   // We don't use SimpleURLLoader's timeout functionality because it is not
-  // possible to test by PreviewsProberTest.
+  // possible to test by AvailabilityProberTest.
   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::BindOnce(&AvailabilityProber::ProcessProbeTimeout,
                                        base::Unretained(this)));
 }
 
-void PreviewsProber::OnURLLoadComplete(
+void AvailabilityProber::OnURLLoadComplete(
     std::unique_ptr<std::string> response_body) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -434,7 +437,7 @@
 
   bool was_successful = delegate_->IsResponseSuccess(
       static_cast<net::Error>(url_loader_->NetError()),
-      *url_loader_->ResponseInfo(), std::move(response_body));
+      url_loader_->ResponseInfo(), std::move(response_body));
 
   timeout_timer_.reset();
   url_loader_.reset();
@@ -447,7 +450,7 @@
   ProcessProbeFailure();
 }
 
-void PreviewsProber::ProcessProbeTimeout() {
+void AvailabilityProber::ProcessProbeTimeout() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(url_loader_);
 
@@ -461,7 +464,7 @@
   ProcessProbeFailure();
 }
 
-void PreviewsProber::ProcessProbeFailure() {
+void AvailabilityProber::ProcessProbeFailure() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
   DCHECK(!timeout_timer_ || !timeout_timer_->IsRunning());
@@ -488,9 +491,10 @@
 
     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)));
+    retry_timer_->Start(
+        FROM_HERE, interval,
+        base::BindOnce(&AvailabilityProber::CreateAndStartURLLoader,
+                       base::Unretained(this)));
 
     successive_retry_count_++;
     return;
@@ -499,7 +503,7 @@
   ResetState();
 }
 
-void PreviewsProber::ProcessProbeSuccess() {
+void AvailabilityProber::ProcessProbeSuccess() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
   DCHECK(!timeout_timer_ || !timeout_timer_->IsRunning());
@@ -529,7 +533,7 @@
   ResetState();
 }
 
-base::Optional<bool> PreviewsProber::LastProbeWasSuccessful() {
+base::Optional<bool> AvailabilityProber::LastProbeWasSuccessful() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   base::Value* cache_entry =
@@ -537,7 +541,7 @@
   if (!cache_entry)
     return base::nullopt;
 
-  base::Optional<PreviewsProberCacheEntry> entry =
+  base::Optional<AvailabilityProberCacheEntry> entry =
       DecodeCacheEntryValue(*cache_entry);
   if (!entry.has_value())
     return base::nullopt;
@@ -562,13 +566,13 @@
   return entry.value().is_success();
 }
 
-void PreviewsProber::SetOnCompleteCallback(
-    PreviewsProberOnCompleteCallback callback) {
+void AvailabilityProber::SetOnCompleteCallback(
+    AvailabilityProberOnCompleteCallback callback) {
   on_complete_callback_ = std::move(callback);
 }
 
-void PreviewsProber::RecordProbeResult(bool success) {
-  PreviewsProberCacheEntry entry;
+void AvailabilityProber::RecordProbeResult(bool success) {
+  AvailabilityProberCacheEntry entry;
   entry.set_is_success(success);
   entry.set_last_modified(
       clock_->Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
@@ -596,14 +600,14 @@
       ->Add(success);
 }
 
-std::string PreviewsProber::GetCacheKeyForCurrentNetwork() const {
+std::string AvailabilityProber::GetCacheKeyForCurrentNetwork() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return base::StringPrintf(
       "%s;%s:%d", GenerateNetworkID(network_connection_tracker_).c_str(),
       url_.host().c_str(), url_.EffectiveIntPort());
 }
 
-std::string PreviewsProber::AppendNameToHistogram(
+std::string AvailabilityProber::AppendNameToHistogram(
     const std::string& histogram) const {
   return base::StringPrintf("%s.%s", histogram.c_str(), name_.c_str());
 }
diff --git a/chrome/browser/previews/previews_prober.h b/chrome/browser/availability/availability_prober.h
similarity index 93%
rename from chrome/browser/previews/previews_prober.h
rename to chrome/browser/availability/availability_prober.h
index c118278..43706f06 100644
--- a/chrome/browser/previews/previews_prober.h
+++ b/chrome/browser/availability/availability_prober.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_PREVIEWS_PREVIEWS_PROBER_H_
-#define CHROME_BROWSER_PREVIEWS_PREVIEWS_PROBER_H_
+#ifndef CHROME_BROWSER_AVAILABILITY_AVAILABILITY_PROBER_H_
+#define CHROME_BROWSER_AVAILABILITY_AVAILABILITY_PROBER_H_
 
 #include <stdint.h>
 #include <memory>
@@ -41,14 +41,15 @@
 class SharedURLLoaderFactory;
 }  // namespace network
 
-typedef base::RepeatingCallback<void(bool)> PreviewsProberOnCompleteCallback;
+typedef base::RepeatingCallback<void(bool)>
+    AvailabilityProberOnCompleteCallback;
 
 // 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.
 // This class may live on either UI or IO thread but should remain on the thread
 // that it was created on.
-class PreviewsProber
+class AvailabilityProber
     : public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   class Delegate {
@@ -63,7 +64,7 @@
     // delegate returns true, no more probes would be attempted until there is a
     // change in the network or |SendNowIfInactive| is called.
     virtual bool IsResponseSuccess(net::Error net_error,
-                                   const network::ResourceResponseHead& head,
+                                   const network::ResourceResponseHead* head,
                                    std::unique_ptr<std::string> body) = 0;
   };
 
@@ -131,7 +132,7 @@
     kHead,
   };
 
-  PreviewsProber(
+  AvailabilityProber(
       Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
@@ -144,7 +145,7 @@
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       const size_t max_cache_entries,
       base::TimeDelta revalidate_cache_after);
-  ~PreviewsProber() override;
+  ~AvailabilityProber() override;
 
   // Registers the prefs used in this class.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
@@ -168,11 +169,11 @@
 
   // Sets a repeating callback to notify the completion of a probe and whether
   // it was successful.
-  void SetOnCompleteCallback(PreviewsProberOnCompleteCallback callback);
+  void SetOnCompleteCallback(AvailabilityProberOnCompleteCallback callback);
 
  protected:
   // Exposes |tick_clock| and |clock| for testing.
-  PreviewsProber(
+  AvailabilityProber(
       Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
@@ -290,13 +291,13 @@
 
   // An optional callback to notify of a completed probe. This callback passes a
   // bool to indicate success of the completed probe.
-  PreviewsProberOnCompleteCallback on_complete_callback_;
+  AvailabilityProberOnCompleteCallback on_complete_callback_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  base::WeakPtrFactory<PreviewsProber> weak_factory_{this};
+  base::WeakPtrFactory<AvailabilityProber> weak_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(PreviewsProber);
+  DISALLOW_COPY_AND_ASSIGN(AvailabilityProber);
 };
 
-#endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_PROBER_H_
+#endif  // CHROME_BROWSER_AVAILABILITY_AVAILABILITY_PROBER_H_
diff --git a/chrome/browser/availability/availability_prober_browsertest.cc b/chrome/browser/availability/availability_prober_browsertest.cc
new file mode 100644
index 0000000..57e35db
--- /dev/null
+++ b/chrome/browser/availability/availability_prober_browsertest.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 <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "build/build_config.h"
+#include "chrome/browser/availability/availability_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 "content/public/test/network_connection_change_simulator.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 "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/mojom/network_service_test.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace {
+
+void WaitForCompletedProbe(AvailabilityProber* prober) {
+  while (true) {
+    if (prober->LastProbeWasSuccessful().has_value())
+      return;
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
+}  // namespace
+
+class TestDelegate : public AvailabilityProber::Delegate {
+ public:
+  TestDelegate() = default;
+  ~TestDelegate() = default;
+
+  bool ShouldSendNextProbe() override { return should_send_next_probe_; }
+
+  bool IsResponseSuccess(net::Error net_error,
+                         const network::ResourceResponseHead* head,
+                         std::unique_ptr<std::string> body) override {
+    got_head_ = head;
+    return net_error == net::OK && head &&
+           head->headers->response_code() == net::HTTP_OK;
+  }
+
+  void set_should_send_next_probe(bool should_send_next_probe) {
+    should_send_next_probe_ = should_send_next_probe;
+  }
+
+  bool got_head() const { return got_head_; }
+
+ private:
+  bool should_send_next_probe_ = true;
+  bool got_head_ = false;
+};
+
+class AvailabilityProberBrowserTest : public InProcessBrowserTest {
+ public:
+  AvailabilityProberBrowserTest() = default;
+  ~AvailabilityProberBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    https_server_.reset(
+        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
+    https_server_->RegisterRequestHandler(base::BindRepeating(
+        &AvailabilityProberBrowserTest::HandleRequest, base::Unretained(this)));
+    ASSERT_TRUE(https_server_->Start());
+  }
+
+  void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitchASCII("host-rules", "MAP test.com 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(AvailabilityProberBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(AvailabilityProberBrowserTest, OK) {
+  GURL url = TestURLWithPath("/ok");
+  TestDelegate delegate;
+  net::HttpRequestHeaders headers;
+  AvailabilityProber::RetryPolicy retry_policy;
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+
+  AvailabilityProber prober(
+      &delegate, browser()->profile()->GetURLLoaderFactory(),
+      browser()->profile()->GetPrefs(),
+      AvailabilityProber::ClientName::kLitepages, url,
+      AvailabilityProber::HttpMethod::kGet, headers, retry_policy,
+      timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
+      base::TimeDelta::FromDays(1));
+  prober.SendNowIfInactive(false);
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
+}
+
+IN_PROC_BROWSER_TEST_F(AvailabilityProberBrowserTest, Timeout) {
+  GURL url = TestURLWithPath("/timeout");
+  TestDelegate delegate;
+  net::HttpRequestHeaders headers;
+
+  AvailabilityProber::RetryPolicy retry_policy;
+  retry_policy.max_retries = 0;
+
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+  timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1);
+
+  AvailabilityProber prober(
+      &delegate, browser()->profile()->GetURLLoaderFactory(),
+      browser()->profile()->GetPrefs(),
+      AvailabilityProber::ClientName::kLitepages, url,
+      AvailabilityProber::HttpMethod::kGet, headers, retry_policy,
+      timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
+      base::TimeDelta::FromDays(1));
+  prober.SendNowIfInactive(false);
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_FALSE(prober.LastProbeWasSuccessful().value());
+}
+
+IN_PROC_BROWSER_TEST_F(AvailabilityProberBrowserTest, NetworkChange) {
+  content::NetworkConnectionChangeSimulator().SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_2G);
+
+  GURL url = TestURLWithPath("/ok");
+  TestDelegate delegate;
+  net::HttpRequestHeaders headers;
+  AvailabilityProber::RetryPolicy retry_policy;
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+
+  AvailabilityProber prober(
+      &delegate, browser()->profile()->GetURLLoaderFactory(),
+      browser()->profile()->GetPrefs(),
+      AvailabilityProber::ClientName::kLitepages, url,
+      AvailabilityProber::HttpMethod::kGet, headers, retry_policy,
+      timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
+      base::TimeDelta::FromDays(1));
+
+  content::NetworkConnectionChangeSimulator().SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_4G);
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
+}
+
+IN_PROC_BROWSER_TEST_F(AvailabilityProberBrowserTest, BadServer) {
+  GURL url("https://invalid.com");
+  TestDelegate delegate;
+  net::HttpRequestHeaders headers;
+  AvailabilityProber::RetryPolicy retry_policy;
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+
+  AvailabilityProber prober(
+      &delegate, browser()->profile()->GetURLLoaderFactory(),
+      browser()->profile()->GetPrefs(),
+      AvailabilityProber::ClientName::kLitepages, url,
+      AvailabilityProber::HttpMethod::kGet, headers, retry_policy,
+      timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
+      base::TimeDelta::FromDays(1));
+  prober.SendNowIfInactive(false);
+  WaitForCompletedProbe(&prober);
+
+  EXPECT_FALSE(delegate.got_head());
+  EXPECT_FALSE(prober.LastProbeWasSuccessful().value());
+}
diff --git a/chrome/browser/previews/previews_prober_unittest.cc b/chrome/browser/availability/availability_prober_unittest.cc
similarity index 67%
rename from chrome/browser/previews/previews_prober_unittest.cc
rename to chrome/browser/availability/availability_prober_unittest.cc
index 3b0653e..45229d69 100644
--- a/chrome/browser/previews/previews_prober_unittest.cc
+++ b/chrome/browser/availability/availability_prober_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/previews/previews_prober.h"
+#include "chrome/browser/availability/availability_prober.h"
 
 #include <cmath>
 
@@ -30,7 +30,7 @@
 const base::TimeDelta kCacheRevalidateAfter(base::TimeDelta::FromDays(1));
 }  // namespace
 
-class TestDelegate : public PreviewsProber::Delegate {
+class TestDelegate : public AvailabilityProber::Delegate {
  public:
   TestDelegate() = default;
   ~TestDelegate() = default;
@@ -38,10 +38,10 @@
   bool ShouldSendNextProbe() override { return should_send_next_probe_; }
 
   bool IsResponseSuccess(net::Error net_error,
-                         const network::ResourceResponseHead& head,
+                         const network::ResourceResponseHead* head,
                          std::unique_ptr<std::string> body) override {
-    return net_error == net::OK &&
-           head.headers->response_code() == net::HTTP_OK;
+    return net_error == net::OK && head &&
+           head->headers->response_code() == net::HTTP_OK;
   }
 
   void set_should_send_next_probe(bool should_send_next_probe) {
@@ -52,13 +52,13 @@
   bool should_send_next_probe_ = true;
 };
 
-class TestPreviewsProber : public PreviewsProber {
+class TestAvailabilityProber : public AvailabilityProber {
  public:
-  TestPreviewsProber(
-      PreviewsProber::Delegate* delegate,
+  TestAvailabilityProber(
+      AvailabilityProber::Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
-      const PreviewsProber::ClientName name,
+      const AvailabilityProber::ClientName name,
       const GURL& url,
       const HttpMethod http_method,
       const net::HttpRequestHeaders headers,
@@ -69,25 +69,25 @@
       base::TimeDelta revalidate_cache_after,
       const base::TickClock* tick_clock,
       const base::Clock* clock)
-      : PreviewsProber(delegate,
-                       url_loader_factory,
-                       pref_service,
-                       name,
-                       url,
-                       http_method,
-                       headers,
-                       retry_policy,
-                       timeout_policy,
-                       traffic_annotation,
-                       max_cache_entries,
-                       revalidate_cache_after,
-                       tick_clock,
-                       clock) {}
+      : AvailabilityProber(delegate,
+                           url_loader_factory,
+                           pref_service,
+                           name,
+                           url,
+                           http_method,
+                           headers,
+                           retry_policy,
+                           timeout_policy,
+                           traffic_annotation,
+                           max_cache_entries,
+                           revalidate_cache_after,
+                           tick_clock,
+                           clock) {}
 };
 
-class PreviewsProberTest : public testing::Test {
+class AvailabilityProberTest : public testing::Test {
  public:
-  PreviewsProberTest()
+  AvailabilityProberTest()
       : thread_bundle_(
             base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME),
         test_shared_loader_factory_(
@@ -97,42 +97,43 @@
         test_prefs_() {}
 
   void SetUp() override {
-    PreviewsProber::RegisterProfilePrefs(test_prefs_.registry());
+    AvailabilityProber::RegisterProfilePrefs(test_prefs_.registry());
   }
 
-  std::unique_ptr<PreviewsProber> NewProber() {
-    return NewProberWithPolicies(PreviewsProber::RetryPolicy(),
-                                 PreviewsProber::TimeoutPolicy());
+  std::unique_ptr<AvailabilityProber> NewProber() {
+    return NewProberWithPolicies(AvailabilityProber::RetryPolicy(),
+                                 AvailabilityProber::TimeoutPolicy());
   }
 
-  std::unique_ptr<PreviewsProber> NewProberWithRetryPolicy(
-      const PreviewsProber::RetryPolicy& retry_policy) {
-    return NewProberWithPolicies(retry_policy, PreviewsProber::TimeoutPolicy());
+  std::unique_ptr<AvailabilityProber> NewProberWithRetryPolicy(
+      const AvailabilityProber::RetryPolicy& retry_policy) {
+    return NewProberWithPolicies(retry_policy,
+                                 AvailabilityProber::TimeoutPolicy());
   }
 
-  std::unique_ptr<PreviewsProber> NewProberWithPolicies(
-      const PreviewsProber::RetryPolicy& retry_policy,
-      const PreviewsProber::TimeoutPolicy& timeout_policy) {
+  std::unique_ptr<AvailabilityProber> NewProberWithPolicies(
+      const AvailabilityProber::RetryPolicy& retry_policy,
+      const AvailabilityProber::TimeoutPolicy& timeout_policy) {
     return NewProberWithPoliciesAndDelegate(&test_delegate_, retry_policy,
                                             timeout_policy);
   }
 
-  std::unique_ptr<PreviewsProber> NewProberWithPoliciesAndDelegate(
-      PreviewsProber::Delegate* delegate,
-      const PreviewsProber::RetryPolicy& retry_policy,
-      const PreviewsProber::TimeoutPolicy& timeout_policy) {
+  std::unique_ptr<AvailabilityProber> NewProberWithPoliciesAndDelegate(
+      AvailabilityProber::Delegate* delegate,
+      const AvailabilityProber::RetryPolicy& retry_policy,
+      const AvailabilityProber::TimeoutPolicy& timeout_policy) {
     net::HttpRequestHeaders headers;
     headers.SetHeader("X-Testing", "Hello world");
-    std::unique_ptr<TestPreviewsProber> prober =
-        std::make_unique<TestPreviewsProber>(
+    std::unique_ptr<TestAvailabilityProber> prober =
+        std::make_unique<TestAvailabilityProber>(
             delegate, test_shared_loader_factory_, &test_prefs_,
-            PreviewsProber::ClientName::kLitepages, kTestUrl,
-            PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+            AvailabilityProber::ClientName::kLitepages, kTestUrl,
+            AvailabilityProber::HttpMethod::kGet, headers, retry_policy,
             timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
             kCacheRevalidateAfter, thread_bundle_.GetMockTickClock(),
             thread_bundle_.GetMockClock());
     prober->SetOnCompleteCallback(base::BindRepeating(
-        &PreviewsProberTest::OnProbeComplete, base::Unretained(this)));
+        &AvailabilityProberTest::OnProbeComplete, base::Unretained(this)));
     return prober;
   }
 
@@ -205,9 +206,9 @@
   base::Optional<bool> callback_result_;
 };
 
-TEST_F(PreviewsProberTest, OK) {
+TEST_F(AvailabilityProberTest, OK) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -217,19 +218,19 @@
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      true, 1);
   histogram_tester.ExpectUniqueSample(
-      "Previews.Prober.NumAttemptsBeforeSuccess.Litepages", 1, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.ResponseCode.Litepages",
-                                      net::HTTP_OK, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+      "Availability.Prober.DidSucceed.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.NumAttemptsBeforeSuccess.Litepages", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 1);
 }
 
-TEST_F(PreviewsProberTest, OK_Callback) {
+TEST_F(AvailabilityProberTest, OK_Callback) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -242,16 +243,16 @@
   EXPECT_TRUE(callback_result().has_value());
   EXPECT_TRUE(callback_result().value());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      true, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.ResponseCode.Litepages",
-                                      net::HTTP_OK, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 1);
 }
 
-TEST_F(PreviewsProberTest, MultipleStart) {
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+TEST_F(AvailabilityProberTest, MultipleStart) {
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   // Calling |SendNowIfInactive| many times should result in only one url
@@ -262,8 +263,8 @@
   VerifyRequest();
 }
 
-TEST_F(PreviewsProberTest, NetworkChangeStartsProber) {
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+TEST_F(AvailabilityProberTest, NetworkChangeStartsProber) {
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
   EXPECT_FALSE(prober->is_active());
 
@@ -274,12 +275,12 @@
   EXPECT_TRUE(prober->is_active());
 }
 
-TEST_F(PreviewsProberTest, NetworkConnectionShardsCache) {
+TEST_F(AvailabilityProberTest, NetworkConnectionShardsCache) {
   network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
       network::mojom::ConnectionType::CONNECTION_3G);
   RunUntilIdle();
 
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -306,12 +307,12 @@
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 }
 
-TEST_F(PreviewsProberTest, CacheMaxSize) {
+TEST_F(AvailabilityProberTest, CacheMaxSize) {
   network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
       network::mojom::ConnectionType::CONNECTION_3G);
   RunUntilIdle();
 
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -346,8 +347,8 @@
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 }
 
-TEST_F(PreviewsProberTest, CacheAutoRevalidation) {
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+TEST_F(AvailabilityProberTest, CacheAutoRevalidation) {
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -368,9 +369,9 @@
   EXPECT_TRUE(prober->is_active());
 }
 
-TEST_F(PreviewsProberTest, PersistentCache) {
+TEST_F(AvailabilityProberTest, PersistentCache) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -391,17 +392,17 @@
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
   EXPECT_TRUE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      true, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.ResponseCode.Litepages",
-                                      net::HTTP_OK, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", true, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 1);
 }
 
 #if defined(OS_ANDROID)
-TEST_F(PreviewsProberTest, StartInForeground) {
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+TEST_F(AvailabilityProberTest, StartInForeground) {
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
   EXPECT_FALSE(prober->is_active());
 
@@ -409,8 +410,8 @@
   EXPECT_TRUE(prober->is_active());
 }
 
-TEST_F(PreviewsProberTest, DoesntCallSendInForegroundIfInactive) {
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+TEST_F(AvailabilityProberTest, DoesntCallSendInForegroundIfInactive) {
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
   EXPECT_FALSE(prober->is_active());
 
@@ -420,9 +421,9 @@
 }
 #endif
 
-TEST_F(PreviewsProberTest, NetError) {
+TEST_F(AvailabilityProberTest, NetError) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -432,17 +433,17 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 4);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 4);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_FAILED), 4);
 }
 
-TEST_F(PreviewsProberTest, NetError_Callback) {
+TEST_F(AvailabilityProberTest, NetError_Callback) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -455,17 +456,17 @@
   EXPECT_TRUE(callback_result().has_value());
   EXPECT_FALSE(callback_result().value());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 4);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 4);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_FAILED), 4);
 }
 
-TEST_F(PreviewsProberTest, HttpError) {
+TEST_F(AvailabilityProberTest, HttpError) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -475,17 +476,17 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 4);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.ResponseCode.Litepages",
-                                      net::HTTP_NOT_FOUND, 4);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 4);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.ResponseCode.Litepages", net::HTTP_NOT_FOUND, 4);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::OK), 4);
 }
 
-TEST_F(PreviewsProberTest, TimeUntilSuccess) {
+TEST_F(AvailabilityProberTest, TimeUntilSuccess) {
   base::HistogramTester histogram_tester;
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -498,18 +499,18 @@
   EXPECT_FALSE(prober->is_active());
 
   histogram_tester.ExpectTotalCount(
-      "Previews.Prober.TimeUntilFailure.Litepages", 0);
+      "Availability.Prober.TimeUntilFailure.Litepages", 0);
   histogram_tester.ExpectUniqueSample(
-      "Previews.Prober.TimeUntilSuccess.Litepages", 11000, 1);
+      "Availability.Prober.TimeUntilSuccess.Litepages", 11000, 1);
 }
 
-TEST_F(PreviewsProberTest, TimeUntilFailure) {
+TEST_F(AvailabilityProberTest, TimeUntilFailure) {
   base::HistogramTester histogram_tester;
 
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 0;
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithRetryPolicy(retry_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -523,17 +524,17 @@
   EXPECT_FALSE(prober->is_active());
 
   histogram_tester.ExpectTotalCount(
-      "Previews.Prober.TimeUntilSuccess.Litepages", 0);
+      "Availability.Prober.TimeUntilSuccess.Litepages", 0);
   histogram_tester.ExpectUniqueSample(
-      "Previews.Prober.TimeUntilFailure.Litepages", 11000, 1);
+      "Availability.Prober.TimeUntilFailure.Litepages", 11000, 1);
 }
 
-TEST_F(PreviewsProberTest, RandomGUID) {
-  PreviewsProber::RetryPolicy retry_policy;
+TEST_F(AvailabilityProberTest, RandomGUID) {
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.use_random_urls = true;
   retry_policy.max_retries = 0;
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithRetryPolicy(retry_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -545,14 +546,14 @@
   EXPECT_FALSE(prober->is_active());
 }
 
-TEST_F(PreviewsProberTest, RetryLinear) {
+TEST_F(AvailabilityProberTest, RetryLinear) {
   base::HistogramTester histogram_tester;
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 2;
-  retry_policy.backoff = PreviewsProber::Backoff::kLinear;
+  retry_policy.backoff = AvailabilityProber::Backoff::kLinear;
   retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithRetryPolicy(retry_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -580,24 +581,24 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 3);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 3);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_FAILED), 3);
   histogram_tester.ExpectTotalCount(
-      "Previews.Prober.NumAttemptsBeforeSuccess.Litepages", 0);
+      "Availability.Prober.NumAttemptsBeforeSuccess.Litepages", 0);
 }
 
-TEST_F(PreviewsProberTest, RetryThenSucceed) {
+TEST_F(AvailabilityProberTest, RetryThenSucceed) {
   base::HistogramTester histogram_tester;
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 2;
-  retry_policy.backoff = PreviewsProber::Backoff::kLinear;
+  retry_policy.backoff = AvailabilityProber::Backoff::kLinear;
   retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithRetryPolicy(retry_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -625,29 +626,30 @@
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectBucketCount("Previews.Prober.DidSucceed.Litepages",
+  histogram_tester.ExpectBucketCount("Availability.Prober.DidSucceed.Litepages",
                                      false, 2);
-  histogram_tester.ExpectBucketCount("Previews.Prober.DidSucceed.Litepages",
+  histogram_tester.ExpectBucketCount("Availability.Prober.DidSucceed.Litepages",
                                      true, 1);
   histogram_tester.ExpectUniqueSample(
-      "Previews.Prober.NumAttemptsBeforeSuccess.Litepages", 3, 1);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.ResponseCode.Litepages",
-                                      net::HTTP_OK, 1);
-  histogram_tester.ExpectBucketCount("Previews.Prober.NetError.Litepages",
+      "Availability.Prober.NumAttemptsBeforeSuccess.Litepages", 3, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.ResponseCode.Litepages", net::HTTP_OK, 1);
+  histogram_tester.ExpectBucketCount("Availability.Prober.NetError.Litepages",
                                      std::abs(net::ERR_FAILED), 2);
-  histogram_tester.ExpectBucketCount("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectBucketCount("Availability.Prober.NetError.Litepages",
                                      std::abs(net::OK), 1);
-  histogram_tester.ExpectTotalCount("Previews.Prober.NetError.Litepages", 3);
+  histogram_tester.ExpectTotalCount("Availability.Prober.NetError.Litepages",
+                                    3);
 }
 
-TEST_F(PreviewsProberTest, RetryExponential) {
+TEST_F(AvailabilityProberTest, RetryExponential) {
   base::HistogramTester histogram_tester;
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 2;
-  retry_policy.backoff = PreviewsProber::Backoff::kExponential;
+  retry_policy.backoff = AvailabilityProber::Backoff::kExponential;
   retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithRetryPolicy(retry_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -675,25 +677,25 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 3);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 3);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_FAILED), 3);
 }
 
-TEST_F(PreviewsProberTest, TimeoutLinear) {
+TEST_F(AvailabilityProberTest, TimeoutLinear) {
   base::HistogramTester histogram_tester;
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::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;
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+  timeout_policy.backoff = AvailabilityProber::Backoff::kLinear;
   timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithPolicies(retry_policy, timeout_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -719,25 +721,25 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 2);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 2);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_TIMED_OUT), 2);
 }
 
-TEST_F(PreviewsProberTest, TimeoutExponential) {
+TEST_F(AvailabilityProberTest, TimeoutExponential) {
   base::HistogramTester histogram_tester;
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::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;
+  AvailabilityProber::TimeoutPolicy timeout_policy;
+  timeout_policy.backoff = AvailabilityProber::Backoff::kExponential;
   timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober =
+  std::unique_ptr<AvailabilityProber> prober =
       NewProberWithPolicies(retry_policy, timeout_policy);
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
@@ -763,26 +765,26 @@
   EXPECT_FALSE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.DidSucceed.Litepages",
-                                      false, 2);
-  histogram_tester.ExpectTotalCount("Previews.Prober.ResponseCode.Litepages",
-                                    0);
-  histogram_tester.ExpectUniqueSample("Previews.Prober.NetError.Litepages",
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.DidSucceed.Litepages", false, 2);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.ResponseCode.Litepages", 0);
+  histogram_tester.ExpectUniqueSample("Availability.Prober.NetError.Litepages",
                                       std::abs(net::ERR_TIMED_OUT), 2);
 }
 
-TEST_F(PreviewsProberTest, DelegateStopsFirstProbe) {
+TEST_F(AvailabilityProberTest, DelegateStopsFirstProbe) {
   base::HistogramTester histogram_tester;
   TestDelegate delegate;
   delegate.set_should_send_next_probe(false);
 
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 2;
-  retry_policy.backoff = PreviewsProber::Backoff::kLinear;
+  retry_policy.backoff = AvailabilityProber::Backoff::kLinear;
   retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober = NewProberWithPoliciesAndDelegate(
-      &delegate, retry_policy, PreviewsProber::TimeoutPolicy());
+  std::unique_ptr<AvailabilityProber> prober = NewProberWithPoliciesAndDelegate(
+      &delegate, retry_policy, AvailabilityProber::TimeoutPolicy());
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -790,19 +792,20 @@
   EXPECT_FALSE(prober->is_active());
   VerifyNoRequests();
 
-  histogram_tester.ExpectTotalCount("Previews.Prober.DidSucceed.Litepages", 0);
+  histogram_tester.ExpectTotalCount("Availability.Prober.DidSucceed.Litepages",
+                                    0);
 }
 
-TEST_F(PreviewsProberTest, DelegateStopsRetries) {
+TEST_F(AvailabilityProberTest, DelegateStopsRetries) {
   TestDelegate delegate;
 
-  PreviewsProber::RetryPolicy retry_policy;
+  AvailabilityProber::RetryPolicy retry_policy;
   retry_policy.max_retries = 2;
-  retry_policy.backoff = PreviewsProber::Backoff::kLinear;
+  retry_policy.backoff = AvailabilityProber::Backoff::kLinear;
   retry_policy.base_interval = base::TimeDelta::FromMilliseconds(1000);
 
-  std::unique_ptr<PreviewsProber> prober = NewProberWithPoliciesAndDelegate(
-      &delegate, retry_policy, PreviewsProber::TimeoutPolicy());
+  std::unique_ptr<AvailabilityProber> prober = NewProberWithPoliciesAndDelegate(
+      &delegate, retry_policy, AvailabilityProber::TimeoutPolicy());
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -822,10 +825,10 @@
   VerifyNoRequests();
 }
 
-TEST_F(PreviewsProberTest, CacheEntryAge) {
+TEST_F(AvailabilityProberTest, CacheEntryAge) {
   base::HistogramTester histogram_tester;
 
-  std::unique_ptr<PreviewsProber> prober = NewProber();
+  std::unique_ptr<AvailabilityProber> prober = NewProber();
   EXPECT_EQ(prober->LastProbeWasSuccessful(), base::nullopt);
 
   prober->SendNowIfInactive(false);
@@ -834,16 +837,16 @@
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
   EXPECT_FALSE(prober->is_active());
 
-  histogram_tester.ExpectUniqueSample("Previews.Prober.CacheEntryAge.Litepages",
-                                      0, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Availability.Prober.CacheEntryAge.Litepages", 0, 1);
 
   FastForward(base::TimeDelta::FromHours(24));
   EXPECT_TRUE(prober->LastProbeWasSuccessful().value());
 
-  histogram_tester.ExpectBucketCount("Previews.Prober.CacheEntryAge.Litepages",
-                                     0, 1);
-  histogram_tester.ExpectBucketCount("Previews.Prober.CacheEntryAge.Litepages",
-                                     24, 1);
-  histogram_tester.ExpectTotalCount("Previews.Prober.CacheEntryAge.Litepages",
-                                    2);
+  histogram_tester.ExpectBucketCount(
+      "Availability.Prober.CacheEntryAge.Litepages", 0, 1);
+  histogram_tester.ExpectBucketCount(
+      "Availability.Prober.CacheEntryAge.Litepages", 24, 1);
+  histogram_tester.ExpectTotalCount(
+      "Availability.Prober.CacheEntryAge.Litepages", 2);
 }
diff --git a/chrome/browser/previews/proto/previews_prober_cache_entry.proto b/chrome/browser/availability/proto/availability_prober_cache_entry.proto
similarity index 90%
rename from chrome/browser/previews/proto/previews_prober_cache_entry.proto
rename to chrome/browser/availability/proto/availability_prober_cache_entry.proto
index 6a7b3a44..59ebfd24 100644
--- a/chrome/browser/previews/proto/previews_prober_cache_entry.proto
+++ b/chrome/browser/availability/proto/availability_prober_cache_entry.proto
@@ -6,7 +6,7 @@
 
 option optimize_for = LITE_RUNTIME;
 
-message PreviewsProberCacheEntry {
+message AvailabilityProberCacheEntry {
   // Whether the probe was successful.
   optional bool is_success = 1;
 
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index a0267e12..98dcd7f5 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -399,7 +399,7 @@
 
   primary_icon_url_ = data.primary_icon_url;
   primary_icon_ = *data.primary_icon;
-
+  has_maskable_primary_icon_ = data.has_maskable_primary_icon;
 
   // If we triggered the installability check on page load, then it's possible
   // we don't have enough engagement yet. If that's the case, return here but
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index c806b10..6186fda2 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -322,6 +322,9 @@
   // The primary icon object.
   SkBitmap primary_icon_;
 
+  // Whether or not the primary icon is maskable.
+  bool has_maskable_primary_icon_;
+
   // The current banner pipeline state for this page load.
   State state_;
 
diff --git a/chrome/browser/banners/app_banner_manager_android.cc b/chrome/browser/banners/app_banner_manager_android.cc
index 1fd1bc2a..a3dd56d3 100644
--- a/chrome/browser/banners/app_banner_manager_android.cc
+++ b/chrome/browser/banners/app_banner_manager_android.cc
@@ -210,7 +210,8 @@
         weak_factory_.GetWeakPtr(),
         ShortcutHelper::CreateShortcutInfo(manifest_url_, manifest_,
                                            primary_icon_url_, badge_icon_url_),
-        primary_icon_, badge_icon_, install_source, can_install_webapk_);
+        primary_icon_, badge_icon_, install_source, can_install_webapk_,
+        has_maskable_primary_icon_);
   } else {
     ui_delegate_ = AppBannerUiDelegateAndroid::Create(
         weak_factory_.GetWeakPtr(), native_app_title_,
diff --git a/chrome/browser/banners/app_banner_ui_delegate_android.cc b/chrome/browser/banners/app_banner_ui_delegate_android.cc
index 2e100ba..d167eb48 100644
--- a/chrome/browser/banners/app_banner_ui_delegate_android.cc
+++ b/chrome/browser/banners/app_banner_ui_delegate_android.cc
@@ -34,11 +34,12 @@
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
     WebappInstallSource install_source,
-    bool is_webapk) {
+    bool is_webapk,
+    bool has_primary_maskable_icon) {
   return std::unique_ptr<AppBannerUiDelegateAndroid>(
       new AppBannerUiDelegateAndroid(weak_manager, std::move(shortcut_info),
                                      primary_icon, badge_icon, install_source,
-                                     is_webapk));
+                                     is_webapk, has_primary_maskable_icon));
 }
 
 // static
@@ -239,12 +240,14 @@
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
     WebappInstallSource install_source,
-    bool is_webapk)
+    bool is_webapk,
+    bool has_primary_maskable_icon)
     : weak_manager_(weak_manager),
       app_title_(shortcut_info->name),
       shortcut_info_(std::move(shortcut_info)),
       primary_icon_(primary_icon),
       badge_icon_(badge_icon),
+      has_primary_maskable_icon_(has_primary_maskable_icon),
       type_(is_webapk ? AppType::WEBAPK : AppType::LEGACY_WEBAPP),
       install_source_(install_source),
       has_user_interaction_(false) {
@@ -305,8 +308,8 @@
       web_contents, shortcut_info_->url.spec(), AppBannerSettingsHelper::WEB);
 
   WebApkInstallService::Get(web_contents->GetBrowserContext())
-      ->InstallAsync(web_contents, *shortcut_info_, primary_icon_, badge_icon_,
-                     install_source_);
+      ->InstallAsync(web_contents, *shortcut_info_, primary_icon_,
+                     has_primary_maskable_icon_, badge_icon_, install_source_);
 }
 
 void AppBannerUiDelegateAndroid::InstallLegacyWebApp(
diff --git a/chrome/browser/banners/app_banner_ui_delegate_android.h b/chrome/browser/banners/app_banner_ui_delegate_android.h
index 3fede8c..88933d66 100644
--- a/chrome/browser/banners/app_banner_ui_delegate_android.h
+++ b/chrome/browser/banners/app_banner_ui_delegate_android.h
@@ -44,7 +44,8 @@
       const SkBitmap& primary_icon,
       const SkBitmap& badge_icon,
       WebappInstallSource install_source,
-      bool is_webapk);
+      bool is_webapk,
+      bool has_primary_maskable_icon);
 
   // Creates a delegate for promoting the installation of an Android app.
   static std::unique_ptr<AppBannerUiDelegateAndroid> Create(
@@ -112,7 +113,8 @@
                              const SkBitmap& primary_icon,
                              const SkBitmap& badge_icon,
                              WebappInstallSource install_source,
-                             bool is_webapk);
+                             bool is_webapk,
+                             bool has_primary_maskable_icon);
 
   // Delegate for promoting an Android app.
   AppBannerUiDelegateAndroid(
@@ -145,6 +147,8 @@
   const SkBitmap primary_icon_;
   const SkBitmap badge_icon_;
 
+  bool has_primary_maskable_icon_;
+
   std::string package_name_;
 
   AppType type_;
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 2dee7b6..9c61a9e 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -20,6 +20,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/common/buildflags.h"
 #include "media/media_buildflags.h"
 
 class BackgroundModeManager;
@@ -248,8 +249,10 @@
 
   virtual component_updater::ComponentUpdateService* component_updater() = 0;
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   virtual component_updater::SupervisedUserWhitelistInstaller*
   supervised_user_whitelist_installer() = 0;
+#endif
 
   virtual MediaFileSystemRegistry* media_file_system_registry() = 0;
 
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index ca4cf3d8..ec674868 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -39,7 +39,6 @@
 #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"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/devtools/devtools_auto_opener.h"
 #include "chrome/browser/devtools/remote_debugging_server.h"
@@ -195,6 +194,10 @@
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
+#endif
+
 #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
 // How often to check if the persistent instance of Chrome needs to restart
 // to install an update.
@@ -376,9 +379,11 @@
   notification_ui_manager_.reset();
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // The SupervisedUserWhitelistInstaller observes the ProfileAttributesStorage,
   // so it needs to be shut down before the ProfileManager.
   supervised_user_whitelist_installer_.reset();
+#endif
 
   // Debugger must be cleaned up before ProfileManager.
   remote_debugging_server_.reset();
@@ -1031,6 +1036,7 @@
   return component_updater_.get();
 }
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 component_updater::SupervisedUserWhitelistInstaller*
 BrowserProcessImpl::supervised_user_whitelist_installer() {
   if (!supervised_user_whitelist_installer_) {
@@ -1042,6 +1048,7 @@
   }
   return supervised_user_whitelist_installer_.get();
 }
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 void BrowserProcessImpl::OnKeepAliveStateChanged(bool is_keeping_alive) {
   if (is_keeping_alive)
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 8029837..c02e294 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -180,8 +180,10 @@
 #endif
 
   component_updater::ComponentUpdateService* component_updater() override;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   component_updater::SupervisedUserWhitelistInstaller*
   supervised_user_whitelist_installer() override;
+#endif
   MediaFileSystemRegistry* media_file_system_registry() override;
   WebRtcLogUploader* webrtc_log_uploader() override;
   network_time::NetworkTimeTracker* network_time_tracker() override;
@@ -366,8 +368,10 @@
   // but some users of component updater only install per-user.
   std::unique_ptr<component_updater::ComponentUpdateService> component_updater_;
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   std::unique_ptr<component_updater::SupervisedUserWhitelistInstaller>
       supervised_user_whitelist_installer_;
+#endif
 
 #if BUILDFLAG(ENABLE_PLUGINS)
   std::unique_ptr<PluginsResourceService> plugins_resource_service_;
diff --git a/chrome/browser/browsing_data/browsing_data_appcache_helper.cc b/chrome/browser/browsing_data/browsing_data_appcache_helper.cc
index 6be57b42..8aded4c2 100644
--- a/chrome/browser/browsing_data/browsing_data_appcache_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_appcache_helper.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/storage_usage_info.h"
+#include "content/public/common/content_features.h"
 #include "net/base/completion_once_callback.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
 
@@ -30,7 +31,6 @@
     BrowsingDataAppCacheHelper::FetchCallback callback,
     scoped_refptr<content::AppCacheInfoCollection> info_collection,
     int /*rv*/) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
 
   std::list<content::StorageUsageInfo> result;
@@ -57,9 +57,13 @@
     result.emplace_back(origin, total_size, last_modified);
   }
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(std::move(callback), std::move(result)));
+  if (base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)) {
+    std::move(callback).Run(std::move(result));
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(std::move(callback), std::move(result)));
+  }
 }
 
 }  // namespace
@@ -72,25 +76,33 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&BrowsingDataAppCacheHelper::StartFetchingOnIOThread, this,
-                     std::move(callback)));
+  if (base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)) {
+    StartFetchingOnLoaderThread(std::move(callback));
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&BrowsingDataAppCacheHelper::StartFetchingOnLoaderThread,
+                       this, std::move(callback)));
+  }
 }
 
 void BrowsingDataAppCacheHelper::DeleteAppCaches(const url::Origin& origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&BrowsingDataAppCacheHelper::DeleteAppCachesOnIOThread,
-                     this, origin));
+  if (base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)) {
+    DeleteAppCachesOnLoaderThread(origin);
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(
+            &BrowsingDataAppCacheHelper::DeleteAppCachesOnLoaderThread, this,
+            origin));
+  }
 }
 
 BrowsingDataAppCacheHelper::~BrowsingDataAppCacheHelper() {}
 
-void BrowsingDataAppCacheHelper::StartFetchingOnIOThread(
+void BrowsingDataAppCacheHelper::StartFetchingOnLoaderThread(
     FetchCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
 
   scoped_refptr<content::AppCacheInfoCollection> info_collection =
@@ -102,9 +114,8 @@
                      info_collection));
 }
 
-void BrowsingDataAppCacheHelper::DeleteAppCachesOnIOThread(
+void BrowsingDataAppCacheHelper::DeleteAppCachesOnLoaderThread(
     const url::Origin& origin) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   appcache_service_->DeleteAppCachesForOrigin(origin,
                                               net::CompletionOnceCallback());
 }
diff --git a/chrome/browser/browsing_data/browsing_data_appcache_helper.h b/chrome/browser/browsing_data/browsing_data_appcache_helper.h
index 5615ca401..ae536da 100644
--- a/chrome/browser/browsing_data/browsing_data_appcache_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_appcache_helper.h
@@ -41,8 +41,8 @@
   virtual ~BrowsingDataAppCacheHelper();
 
  private:
-  void StartFetchingOnIOThread(FetchCallback completion_callback);
-  void DeleteAppCachesOnIOThread(const url::Origin& origin);
+  void StartFetchingOnLoaderThread(FetchCallback completion_callback);
+  void DeleteAppCachesOnLoaderThread(const url::Origin& origin);
 
   // Owned by the profile.
   content::AppCacheService* appcache_service_;
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 5125ede2..b9e3c15 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -61,9 +61,9 @@
 #include "chrome/browser/component_updater/optimization_hints_component_installer.h"
 #include "chrome/browser/component_updater/origin_trials_component_installer.h"
 #include "chrome/browser/component_updater/pepper_flash_component_installer.h"
+#include "chrome/browser/component_updater/safety_tips_component_installer.h"
 #include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/component_updater/subresource_filter_component_installer.h"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -341,6 +341,10 @@
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
+#endif
+
 using content::BrowserThread;
 
 namespace {
@@ -485,9 +489,11 @@
     RegisterPnaclComponent(cus);
 #endif  // BUILDFLAG(ENABLE_NACL) && !defined(OS_ANDROID)
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   component_updater::SupervisedUserWhitelistInstaller* whitelist_installer =
       g_browser_process->supervised_user_whitelist_installer();
   whitelist_installer->RegisterComponents();
+#endif
 
   RegisterSubresourceFilterComponent(cus);
   RegisterOnDeviceHeadSuggestComponent(cus);
@@ -547,6 +553,8 @@
     component_updater::RegisterVrAssetsComponent(cus);
   }
 #endif
+
+  RegisterSafetyTipsComponent(cus, path);
 }
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index bdb41ce..0b29bc11f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5209,13 +5209,6 @@
                                                               relying_party_id);
 }
 
-#if defined(OS_MACOSX)
-bool ChromeContentBrowserClient::
-    IsWebAuthenticationTouchIdAuthenticatorSupported() {
-  return true;
-}
-#endif
-
 std::unique_ptr<net::ClientCertStore>
 ChromeContentBrowserClient::CreateClientCertStore(
     content::ResourceContext* resource_context) {
@@ -5844,6 +5837,7 @@
   const ChromeSubresourceFilterClient* client =
       ChromeSubresourceFilterClient::FromWebContents(web_contents);
   if (client && client->GetThrottleManager()->IsFrameTaggedAsAd(frame_host)) {
+    download_policy->SetAllowed(content::NavigationDownloadType::kAdFrame);
     if (!user_gesture) {
       if (base::FeatureList::IsEnabled(
               blink::features::
@@ -5854,9 +5848,6 @@
         download_policy->SetAllowed(
             content::NavigationDownloadType::kAdFrameNoGesture);
       }
-    } else {
-      download_policy->SetAllowed(
-          content::NavigationDownloadType::kAdFrameGesture);
     }
   }
 }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 30a7226..069f597 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -521,9 +521,6 @@
   GetWebAuthenticationRequestDelegate(
       content::RenderFrameHost* render_frame_host,
       const std::string& relying_party_id) override;
-#if defined(OS_MACOSX)
-  bool IsWebAuthenticationTouchIdAuthenticatorSupported() override;
-#endif
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       content::ResourceContext* resource_context) override;
   std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index 1a0c83d..e2df7f6 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -44,11 +44,6 @@
   // DEPRECATED: Use BrowserListObserver::OnBrowserAdded()
   NOTIFICATION_BROWSER_OPENED = NOTIFICATION_CHROME_START,
 
-  // This message is sent after a window has been closed.  The source is a
-  // Source<Browser> containing the affected Browser.  No details are expected.
-  // DEPRECATED: Use BrowserListObserver::OnBrowserRemoved()
-  NOTIFICATION_BROWSER_CLOSED,
-
   // This message is sent when closing a browser has been cancelled, either by
   // the user cancelling a beforeunload dialog, or IsClosingPermitted()
   // disallowing closing. This notification implies that no BROWSER_CLOSING or
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index fb0a07d4..c6e19a5 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -72,7 +72,6 @@
     "//chrome/common",
     "//chrome/common/extensions/api",
     "//chrome/services/app_service:lib",
-    "//chrome/services/app_service/public/cpp:app_service_proxy",
     "//chrome/services/app_service/public/cpp:app_update",
     "//chrome/services/cups_proxy",
     "//chrome/services/cups_proxy/public/mojom",
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
index d1800dc..e70df52 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
@@ -15,6 +15,8 @@
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chromeos/components/multidevice/logging/logging.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "extensions/common/extension.h"
 
 namespace chromeos {
@@ -26,8 +28,16 @@
 const PwaDomain kDomains[] = {PwaDomain::kProdAndroid, PwaDomain::kProdGoogle,
                               PwaDomain::kStaging};
 
+const char kLastSuccessfulDomainPref[] = "android_sms.last_successful_domain";
+
 }  // namespace
 
+// static
+void AndroidSmsAppManagerImpl::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(kLastSuccessfulDomainPref, std::string());
+}
+
 AndroidSmsAppManagerImpl::PwaDelegate::PwaDelegate() = default;
 
 AndroidSmsAppManagerImpl::PwaDelegate::~PwaDelegate() = default;
@@ -50,11 +60,13 @@
 AndroidSmsAppManagerImpl::AndroidSmsAppManagerImpl(
     Profile* profile,
     AndroidSmsAppSetupController* setup_controller,
+    PrefService* pref_service,
     app_list::AppListSyncableService* app_list_syncable_service,
     scoped_refptr<base::TaskRunner> task_runner)
     : profile_(profile),
       setup_controller_(setup_controller),
       app_list_syncable_service_(app_list_syncable_service),
+      pref_service_(pref_service),
       installed_url_at_last_notify_(GetCurrentAppUrl()),
       pwa_delegate_(std::make_unique<PwaDelegate>()),
       weak_ptr_factory_(this) {
@@ -89,12 +101,14 @@
   if (migrating_from && *migrating_from == GetPreferredPwaDomain())
     migrating_from.reset();
 
+  GURL install_url = GetAndroidMessagesURL(true /* use_install_url */);
+
   is_new_app_setup_in_progress_ = true;
   setup_controller_->SetUpApp(
-      GetAndroidMessagesURL() /* app_url */,
-      GetAndroidMessagesURL(true /* use_install_url */) /* install_url */,
+      GetAndroidMessagesURL() /* app_url */, install_url,
       base::BindOnce(&AndroidSmsAppManagerImpl::OnSetUpNewAppResult,
-                     weak_ptr_factory_.GetWeakPtr(), migrating_from));
+                     weak_ptr_factory_.GetWeakPtr(), migrating_from,
+                     install_url));
 }
 
 void AndroidSmsAppManagerImpl::SetUpAndLaunchAndroidSmsApp() {
@@ -103,6 +117,8 @@
 }
 
 void AndroidSmsAppManagerImpl::TearDownAndroidSmsApp() {
+  pref_service_->SetString(kLastSuccessfulDomainPref, std::string());
+
   base::Optional<GURL> installed_app_url = GetCurrentAppUrl();
   if (!installed_app_url)
     return;
@@ -111,6 +127,12 @@
                                                          base::DoNothing());
 }
 
+bool AndroidSmsAppManagerImpl::HasAppBeenManuallyUninstalledByUser() {
+  GURL url = GetAndroidMessagesURL(true /* use_install_url */);
+  return pref_service_->GetString(kLastSuccessfulDomainPref) == url.spec() &&
+         !setup_controller_->GetPwa(url);
+}
+
 base::Optional<PwaDomain> AndroidSmsAppManagerImpl::GetInstalledPwaDomain() {
   for (auto* it = std::begin(kDomains); it != std::end(kDomains); ++it) {
     if (setup_controller_->GetPwa(
@@ -153,6 +175,7 @@
 
 void AndroidSmsAppManagerImpl::OnSetUpNewAppResult(
     const base::Optional<PwaDomain>& migrating_from,
+    const GURL& install_url,
     bool success) {
   is_new_app_setup_in_progress_ = false;
 
@@ -165,6 +188,9 @@
     return;
   }
 
+  if (success)
+    pref_service_->SetString(kLastSuccessfulDomainPref, install_url.spec());
+
   // If there is no PWA installed at the old URL, no migration is needed and
   // setup is finished.
   if (!migrating_from) {
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
index b701e17e..9380caf 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
@@ -18,6 +18,9 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "url/gurl.h"
 
+class PrefRegistrySimple;
+class PrefService;
+
 namespace app_list {
 class AppListSyncableService;
 }  // namespace app_list
@@ -38,10 +41,12 @@
   AndroidSmsAppManagerImpl(
       Profile* profile,
       AndroidSmsAppSetupController* setup_controller,
+      PrefService* pref_service,
       app_list::AppListSyncableService* app_list_syncable_service,
       scoped_refptr<base::TaskRunner> task_runner =
           base::ThreadTaskRunnerHandle::Get());
   ~AndroidSmsAppManagerImpl() override;
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
  private:
   friend class AndroidSmsAppManagerImplTest;
@@ -65,11 +70,13 @@
   void SetUpAndroidSmsApp() override;
   void SetUpAndLaunchAndroidSmsApp() override;
   void TearDownAndroidSmsApp() override;
+  bool HasAppBeenManuallyUninstalledByUser() override;
 
   base::Optional<PwaDomain> GetInstalledPwaDomain();
   void CompleteAsyncInitialization();
   void NotifyInstalledAppUrlChangedIfNecessary();
   void OnSetUpNewAppResult(const base::Optional<PwaDomain>& migrating_from,
+                           const GURL& install_url,
                            bool success);
   void OnRemoveOldAppResult(const base::Optional<PwaDomain>& migrating_from,
                             bool success);
@@ -80,6 +87,7 @@
   Profile* profile_;
   AndroidSmsAppSetupController* setup_controller_;
   app_list::AppListSyncableService* app_list_syncable_service_;
+  PrefService* pref_service_;
 
   // True if installation is in currently in progress.
   bool is_new_app_setup_in_progress_ = false;
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
index e621a5e..bd6e664 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,6 +28,7 @@
 
 const char kNewAppId[] = "newAppId";
 const char kOldAppId[] = "oldAppId";
+const char kLastSuccessfulDomainPref[] = "android_sms.last_successful_domain";
 
 GURL GetAndroidMessagesURLOld(bool use_install_url = false) {
   // For this test, consider the staging server to be the "old" URL.
@@ -100,10 +102,15 @@
         std::make_unique<FakeAndroidSmsAppSetupController>();
 
     test_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    test_pref_service_ =
+        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+    AndroidSmsAppManagerImpl::RegisterProfilePrefs(
+        test_pref_service_->registry());
 
     android_sms_app_manager_ = std::make_unique<AndroidSmsAppManagerImpl>(
         &profile_, fake_android_sms_app_setup_controller_.get(),
-        nullptr /* app_list_syncable_service */, test_task_runner_);
+        test_pref_service_.get(), nullptr /* app_list_syncable_service */,
+        test_task_runner_);
 
     auto test_pwa_delegate = std::make_unique<TestPwaDelegate>();
     test_pwa_delegate_ = test_pwa_delegate.get();
@@ -132,10 +139,16 @@
     return android_sms_app_manager_.get();
   }
 
+  sync_preferences::TestingPrefServiceSyncable* test_pref_service() {
+    return test_pref_service_.get();
+  }
+
  private:
   content::TestBrowserThreadBundle thread_bundle_;
 
   TestingProfile profile_;
+  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
+      test_pref_service_;
   std::unique_ptr<FakeAndroidSmsAppSetupController>
       fake_android_sms_app_setup_controller_;
   scoped_refptr<base::TestSimpleTaskRunner> test_task_runner_;
@@ -163,18 +176,19 @@
       GetAndroidMessagesURL(true /* use_install_url */)));
   EXPECT_FALSE(android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(0u, test_observer()->num_installed_app_url_changed_events());
+  EXPECT_EQ(std::string(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
 }
 
 TEST_F(AndroidSmsAppManagerImplTest,
        TestSetUpMessages_ThenTearDown_NoPreviousApp) {
   CompleteAsyncInitialization();
+  const GURL install_url = GetAndroidMessagesURL(true /* use_install_url */);
 
   android_sms_app_manager()->SetUpAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
       GetAndroidMessagesURL() /* expected_app_url */,
-      GetAndroidMessagesURL(
-          true /* use_install_url */) /* expected_install_url */,
-      kNewAppId);
+      install_url /* expected_install_url */, kNewAppId);
 
   // Verify that the app was installed and observers were notified.
   EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
@@ -182,12 +196,13 @@
                                true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(
-                      GetAndroidMessagesURL(true /* use_install_url */))
+                  ->GetAppMetadataAtUrl(install_url)
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
             *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
+  EXPECT_EQ(install_url.spec(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
 
   // Now, tear down the app, which should remove the DefaultToPersist cookie.
   android_sms_app_manager()->TearDownAndroidSmsApp();
@@ -199,17 +214,18 @@
                    ->GetAppMetadataAtUrl(
                        GetAndroidMessagesURL(true /* use_install_url */))
                    ->is_cookie_present);
+  EXPECT_EQ(std::string(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
 }
 
 TEST_F(AndroidSmsAppManagerImplTest, TestSetUpMessagesAndLaunch_NoPreviousApp) {
   CompleteAsyncInitialization();
+  const GURL install_url = GetAndroidMessagesURL(true /* use_install_url */);
 
   android_sms_app_manager()->SetUpAndLaunchAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
       GetAndroidMessagesURL() /* expected_app_url */,
-      GetAndroidMessagesURL(
-          true /* use_install_url */) /* expected_install_url */,
-      kNewAppId);
+      install_url /* expected_install_url */, kNewAppId);
 
   // Verify that the app was installed and observers were notified.
   EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
@@ -217,12 +233,15 @@
                                true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(
-                      GetAndroidMessagesURL(true /* use_install_url */))
+                  ->GetAppMetadataAtUrl(install_url)
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
             *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
+  EXPECT_EQ(install_url.spec(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
+  EXPECT_FALSE(
+      android_sms_app_manager()->HasAppBeenManuallyUninstalledByUser());
 
   // The app should have been launched.
   EXPECT_EQ(kNewAppId, test_pwa_delegate()->opened_app_ids()[0]);
@@ -270,11 +289,10 @@
   CompleteAsyncInitialization();
 
   // This should trigger the new app to be installed.
+  const GURL install_url = GetAndroidMessagesURL(true /* use_install_url */);
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
       GetAndroidMessagesURL() /* expected_app_url */,
-      GetAndroidMessagesURL(
-          true /* use_install_url */) /* expected_install_url */,
-      kNewAppId /* id_for_app */);
+      install_url /* expected_install_url */, kNewAppId /* id_for_app */);
 
   // Verify that the app was installed and attributes were transferred. By this
   // point, observers should not have been notified yet since the old app was
@@ -289,6 +307,8 @@
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
             *android_sms_app_manager()->GetCurrentAppUrl());
+  EXPECT_EQ(install_url.spec(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
   EXPECT_EQ(std::make_pair(std::string(kOldAppId), std::string(kNewAppId)),
             test_pwa_delegate()->transfer_item_attribute_params()[0]);
   EXPECT_EQ(0u, test_observer()->num_installed_app_url_changed_events());
@@ -302,6 +322,37 @@
       GetAndroidMessagesURL() /* expected_migrated_to_app_url */,
       true /* success */);
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
+  EXPECT_FALSE(
+      android_sms_app_manager()->HasAppBeenManuallyUninstalledByUser());
+}
+
+TEST_F(AndroidSmsAppManagerImplTest, TestManualUninstall) {
+  const GURL install_url = GetAndroidMessagesURL(true /* use_install_url */);
+  CompleteAsyncInitialization();
+
+  android_sms_app_manager()->SetUpAndroidSmsApp();
+  fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
+      GetAndroidMessagesURL() /* expected_app_url */,
+      install_url /* expected_install_url */, kNewAppId);
+
+  // Verify that the app was installed and observers were notified.
+  EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
+                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL(
+                               true /* use_install_url */))
+                           ->pwa->id());
+  EXPECT_TRUE(fake_android_sms_app_setup_controller()
+                  ->GetAppMetadataAtUrl(install_url)
+                  ->is_cookie_present);
+  EXPECT_EQ(GetAndroidMessagesURL(),
+            *android_sms_app_manager()->GetCurrentAppUrl());
+  EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
+  EXPECT_EQ(install_url.spec(),
+            test_pref_service()->GetString(kLastSuccessfulDomainPref));
+
+  // Now uninstall the app and verify that the app manager registers it.
+  fake_android_sms_app_setup_controller()->SetAppAtUrl(install_url,
+                                                       base::nullopt);
+  EXPECT_TRUE(android_sms_app_manager()->HasAppBeenManuallyUninstalledByUser());
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.cc b/chrome/browser/chromeos/android_sms/android_sms_service.cc
index 8634823..f7b9a30 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_service.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_service.cc
@@ -40,6 +40,7 @@
       android_sms_app_manager_(std::make_unique<AndroidSmsAppManagerImpl>(
           profile_,
           andoid_sms_app_setup_controller_.get(),
+          profile_->GetPrefs(),
           app_list_syncable_service)),
       android_sms_pairing_state_tracker_(
           std::make_unique<AndroidSmsPairingStateTrackerImpl>(
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
index 9c58176..0e9e1c7 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
@@ -102,6 +102,7 @@
 void AndroidSmsServiceFactory::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   PairingLostNotifier::RegisterProfilePrefs(registry);
+  AndroidSmsAppManagerImpl::RegisterProfilePrefs(registry);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
index b231235..6c0c37e 100644
--- a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
@@ -76,6 +76,8 @@
       } else {
         close_reason = apps::IntentPickerCloseReason::ERROR_AFTER_PICKER;
       }
+      RecordUma(launch_name, app_type, close_reason, apps::Source::kHttpOrHttps,
+                should_persist);
       return;
     case apps::mojom::AppType::kUnknown:
       // TODO(crbug.com/826982): This workaround can be removed when preferences
@@ -136,7 +138,9 @@
       FindPwaForUrl(web_contents, url, std::move(apps));
   bool show_persistence_options = ShouldShowPersistenceOptions(apps_for_picker);
   apps::AppsNavigationThrottle::ShowIntentPickerBubbleForApps(
-      web_contents, std::move(apps_for_picker), show_persistence_options,
+      web_contents, std::move(apps_for_picker),
+      /*show_stay_in_chrome=*/show_persistence_options,
+      /*show_remember_selection=*/show_persistence_options,
       base::BindOnce(&OnIntentPickerClosed, web_contents,
                      ui_auto_display_service, url));
 }
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc
index eac2110f..05a47460 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc
@@ -480,7 +480,7 @@
   const bool stay_in_chrome = IsChromeAnAppCandidate(handlers);
   IntentPickerTabHelper::SetShouldShowIcon(web_contents, true);
   browser->window()->ShowIntentPickerBubble(
-      std::move(app_info), stay_in_chrome, /*show_persistence_options=*/true,
+      std::move(app_info), stay_in_chrome, /*show_remember_selection=*/true,
       base::BindOnce(OnIntentPickerClosed, render_process_host_id, routing_id,
                      url, safe_to_bypass_ui, std::move(handlers)));
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import.cc b/chrome/browser/chromeos/crostini/crostini_export_import.cc
index b901db0..ad3b9026 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import.cc
+++ b/chrome/browser/chromeos/crostini/crostini_export_import.cc
@@ -7,8 +7,10 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/files/file_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
@@ -175,12 +177,26 @@
 
   switch (type) {
     case ExportImportType::EXPORT:
-      guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
-          kCrostiniDefaultVmName, path.DirName(), false,
-          base::BindOnce(&CrostiniExportImport::ExportAfterSharing,
-                         weak_ptr_factory_.GetWeakPtr(),
-                         std::move(container_id), path.BaseName(),
-                         std::move(callback)));
+      base::PostTaskWithTraitsAndReply(
+          FROM_HERE, {base::MayBlock()},
+          // Ensure file exists so that it can be shared.
+          base::BindOnce(
+              [](const base::FilePath& path) {
+                base::File file(path, base::File::FLAG_CREATE_ALWAYS |
+                                          base::File::FLAG_WRITE);
+                DCHECK(file.IsValid()) << path << " is invalid";
+              },
+              path),
+          base::BindOnce(
+              &guest_os::GuestOsSharePath::SharePath,
+              base::Unretained(
+                  guest_os::GuestOsSharePath::GetForProfile(profile_)),
+              kCrostiniDefaultVmName, path, false,
+              base::BindOnce(&CrostiniExportImport::ExportAfterSharing,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(container_id), std::move(callback))
+
+                  ));
       break;
     case ExportImportType::IMPORT:
       guest_os::GuestOsSharePath::GetForProfile(profile_)->SharePath(
@@ -194,7 +210,6 @@
 
 void CrostiniExportImport::ExportAfterSharing(
     const ContainerId& container_id,
-    const base::FilePath& filename,
     CrostiniManager::CrostiniResultCallback callback,
     const base::FilePath& container_path,
     bool result,
@@ -209,8 +224,7 @@
     return;
   }
   CrostiniManager::GetForProfile(profile_)->ExportLxdContainer(
-      kCrostiniDefaultVmName, kCrostiniDefaultContainerName,
-      container_path.Append(filename),
+      kCrostiniDefaultVmName, kCrostiniDefaultContainerName, container_path,
       base::BindOnce(&CrostiniExportImport::OnExportComplete,
                      weak_ptr_factory_.GetWeakPtr(), base::Time::Now(),
                      container_id, std::move(callback)));
@@ -238,6 +252,10 @@
     }
   } else {
     LOG(ERROR) << "Error exporting " << int(result);
+    base::PostTaskWithTraits(
+        FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+        base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                       it->second->path(), false));
     switch (result) {
       case CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED:
         enum_hist_result = ExportContainerResult::kFailedVmStopped;
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import.h b/chrome/browser/chromeos/crostini/crostini_export_import.h
index 64a92b2..c80a381 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import.h
+++ b/chrome/browser/chromeos/crostini/crostini_export_import.h
@@ -131,7 +131,6 @@
                                  uint64_t minimum_required_space) override;
 
   void ExportAfterSharing(const ContainerId& container_id,
-                          const base::FilePath& filename,
                           CrostiniManager::CrostiniResultCallback callback,
                           const base::FilePath& container_path,
                           bool result,
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import_notification.h b/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
index b8a2fca..8a0593f5 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
+++ b/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
@@ -55,6 +55,7 @@
 
   Status status() const { return status_; }
   ExportImportType type() const { return type_; }
+  const base::FilePath& path() const { return path_; }
   // Getters for testing.
   message_center::Notification* get_notification() {
     return notification_.get();
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc b/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
index adf4be9..3ecf41e 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_export_import_unittest.cc
@@ -174,6 +174,9 @@
       vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE);
   EXPECT_EQ(notification->status(),
             CrostiniExportImportNotification::Status::DONE);
+  // CrostiniExportImport should've created the exported file.
+  thread_bundle_.RunUntilIdle();
+  EXPECT_TRUE(base::PathExists(tarball_));
 }
 
 TEST_F(CrostiniExportImportTest, TestExportSuccess) {
@@ -229,6 +232,9 @@
       vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE);
   EXPECT_EQ(notification->status(),
             CrostiniExportImportNotification::Status::DONE);
+  // CrostiniExportImport should've created the exported file.
+  thread_bundle_.RunUntilIdle();
+  EXPECT_TRUE(base::PathExists(tarball_));
 }
 
 TEST_F(CrostiniExportImportTest, TestExportFail) {
@@ -243,6 +249,9 @@
       vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_FAILED);
   EXPECT_EQ(notification->status(),
             CrostiniExportImportNotification::Status::FAILED);
+  // CrostiniExportImport should cleanup the file if an export fails.
+  thread_bundle_.RunUntilIdle();
+  EXPECT_FALSE(base::PathExists(tarball_));
 }
 
 TEST_F(CrostiniExportImportTest, TestImportSuccess) {
diff --git a/chrome/browser/chromeos/crostini/crostini_test_helper.cc b/chrome/browser/chromeos/crostini/crostini_test_helper.cc
index 91a8453..1390ab7b 100644
--- a/chrome/browser/chromeos/crostini/crostini_test_helper.cc
+++ b/chrome/browser/chromeos/crostini/crostini_test_helper.cc
@@ -5,7 +5,8 @@
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 
 #include "base/feature_list.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
@@ -115,7 +116,7 @@
     //
     // We therefore manually have the App Service re-examine whether Crostini
     // is enabled for this profile.
-    auto* proxy = apps::AppServiceProxyImpl::GetImplForTesting(profile_);
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile_);
     proxy->ReInitializeCrostiniForTesting(profile_);
     proxy->FlushMojoCallsForTesting();
   }
diff --git a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
index 9cfed79..7215886 100644
--- a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/first_run/goodies_displayer.h"
 
 #include "base/command_line.h"
+#include "base/run_loop.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -13,7 +14,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/test_launcher_utils.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
 
 namespace chromeos {
@@ -31,11 +31,8 @@
   // Set up windowless browser and GoodiesDisplayer.  |delta_days| is +/- delta
   // in days from kMaxDaysAfterOobeForGoodies; <= 0: "show", > 0: "don't show".
   Browser* CreateBrowserAndDisplayer(int delta_days) {
-    // Create a new browser and wait for completion.
-    ui_test_utils::BrowserAddedObserver browser_added_observer;
     Browser* browser = new Browser(
         Browser::CreateParams(ProfileManager::GetActiveUserProfile(), true));
-    browser_added_observer.WaitForSingleNewBrowser();
 
     // Set up Goodies Displayer and set fake age of device.
     setup_info_.days_since_oobe =
@@ -74,10 +71,9 @@
 
     // Wait for GoodiesDisplayer setup completion.  The completion callback will
     // shut down the message loop.
-    scoped_refptr<content::MessageLoopRunner> message_loop_runner =
-        new content::MessageLoopRunner;
-    setup_info_.on_setup_complete_callback = message_loop_runner->QuitClosure();
-    message_loop_runner->Run();
+    base::RunLoop run_loop;
+    setup_info_.on_setup_complete_callback = run_loop.QuitClosure();
+    run_loop.Run();
     setup_info_.on_setup_complete_callback.Reset();
     EXPECT_TRUE(setup_info_.setup_complete);
   }
diff --git a/chrome/browser/chromeos/guest_os/guest_os_share_path.cc b/chrome/browser/chromeos/guest_os/guest_os_share_path.cc
index 29240b3..ffc927a5 100644
--- a/chrome/browser/chromeos/guest_os/guest_os_share_path.cc
+++ b/chrome/browser/chromeos/guest_os/guest_os_share_path.cc
@@ -632,18 +632,20 @@
     return;
   }
 
+  // VolumeManager will be nullptr if running inside a test.
+  auto* vmgr = file_manager::VolumeManager::Get(profile_);
+  if (!vmgr) {
+    return;
+  }
   // If we can't find the path, check if the volume was unmounted.
   // FileWatchers may fire before VolumeManager::OnVolumeUnmounted.
-  bool volume_still_mounted = false;
-  const std::vector<base::WeakPtr<file_manager::Volume>>& volume_list =
-      file_manager::VolumeManager::Get(profile_)->GetVolumeList();
-  for (const auto& volume : volume_list) {
-    if ((path == volume->mount_path() || volume->mount_path().IsParent(path)) &&
-        base::PathExists(volume->mount_path())) {
-      volume_still_mounted = true;
-      break;
-    }
-  }
+  const auto volume_list = vmgr->GetVolumeList();
+  const bool volume_still_mounted = std::any_of(
+      volume_list.begin(), volume_list.end(), [&path](const auto& volume) {
+        return (path == volume->mount_path() ||
+                volume->mount_path().IsParent(path)) &&
+               base::PathExists(volume->mount_path());
+      });
   if (!volume_still_mounted) {
     return;
   }
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
index b851d17..d6ea8e30 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h"
 
+#include <vector>
+
 #include "base/bind.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -390,6 +392,10 @@
   return kDefaultKerberosConfig;
 }
 
+bool KerberosCredentialsManager::IsKerberosEnabled() {
+  return local_state_->GetBoolean(prefs::kKerberosEnabled);
+}
+
 void KerberosCredentialsManager::OnPolicyUpdated(
     const policy::PolicyNamespace& ns,
     const policy::PolicyMap& previous,
@@ -450,17 +456,15 @@
 
   LogError("AddAccountAndAuthenticate", error);
 
-  // Note: For managed accounts don't change the active principal.
   if (Succeeded(error)) {
-    // Don't change the active account if an account is added by policy.
-    if (!is_managed)
-      SetActivePrincipalName(updated_principal);
-
-    // Set active account.
+    // Set active account. Be sure not to wipe user selection if the
+    // account was added automatically by policy.
     // TODO(https://crbug.com/948121): Wait until the files have been saved.
     // This is important when this code is triggered directly through a page
     // that requires Kerberos auth.
-    if (GetActivePrincipalName() == updated_principal)
+    if (!is_managed || GetActivePrincipalName().empty())
+      SetActivePrincipalName(updated_principal);
+    else if (GetActivePrincipalName() == updated_principal)
       GetKerberosFiles();
 
     // Bring the merry news to the observers, but only if there is no
@@ -491,11 +495,9 @@
     const kerberos::RemoveAccountResponse& response) {
   LogError("RemoveAccount", response.error());
   if (Succeeded(response.error())) {
-    // Clear out active credentials.
-    if (GetActivePrincipalName() == principal_name) {
-      kerberos_files_handler_.DeleteFiles();
-      ClearActivePrincipalName();
-    }
+    // Reassign active principal if it got deleted.
+    if (GetActivePrincipalName() == principal_name)
+      ValidateActivePrincipal();
 
     // Express our condolence to the observers.
     NotifyAccountsChanged();
@@ -506,19 +508,35 @@
 
 void KerberosCredentialsManager::ClearAccounts(ResultCallback callback) {
   kerberos::ClearAccountsRequest request;
+  request.set_mode(kerberos::CLEAR_ALL);
   KerberosClient::Get()->ClearAccounts(
       request, base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
-                              weak_factory_.GetWeakPtr(), std::move(callback)));
+                              weak_factory_.GetWeakPtr(), request.mode(),
+                              std::move(callback)));
 }
 
 void KerberosCredentialsManager::OnClearAccounts(
+    kerberos::ClearMode mode,
     ResultCallback callback,
     const kerberos::ClearAccountsResponse& response) {
   LogError("ClearAccounts", response.error());
   if (Succeeded(response.error())) {
-    // Clear out active credentials.
-    kerberos_files_handler_.DeleteFiles();
-    ClearActivePrincipalName();
+    // Depending on the mode, we might have to check if the active principal is
+    // still valid.
+    if (!GetActivePrincipalName().empty()) {
+      switch (mode) {
+        case kerberos::CLEAR_ALL:
+        case kerberos::CLEAR_ONLY_MANAGED_ACCOUNTS:
+        case kerberos::CLEAR_ONLY_UNMANAGED_ACCOUNTS:
+          // Check if the active account was wiped and if so, replace it.
+          ValidateActivePrincipal();
+          break;
+
+        case kerberos::CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS:
+          // We're good, only passwords got wiped, not accounts.
+          break;
+      }
+    }
 
     // Tattle on the lost accounts to the observers.
     NotifyAccountsChanged();
@@ -538,8 +556,8 @@
     ListAccountsCallback callback,
     const kerberos::ListAccountsResponse& response) {
   LogError("ListAccounts", response.error());
-  // Lazily validate principal here.
-  ValidateActivePrincipal(response);
+  // Lazily validate principal here while we're at it.
+  DoValidateActivePrincipal(response);
   std::move(callback).Run(response);
 }
 
@@ -548,10 +566,7 @@
   if (!NormalizePrincipal(&principal_name))
     return kerberos::ERROR_PARSE_PRINCIPAL_FAILED;
 
-  // Don't early out if names are equal, this might be required to bootstrap
-  // Kerberos credentials.
   SetActivePrincipalName(principal_name);
-  GetKerberosFiles();
   NotifyAccountsChanged();
   return kerberos::ERROR_NONE;
 }
@@ -697,22 +712,35 @@
 
 void KerberosCredentialsManager::SetActivePrincipalName(
     const std::string& principal_name) {
+  // Don't early out if names are equal, this might be required to bootstrap
+  // Kerberos credentials.
   primary_profile_->GetPrefs()->SetString(prefs::kKerberosActivePrincipalName,
                                           principal_name);
+  GetKerberosFiles();
 }
 
 void KerberosCredentialsManager::ClearActivePrincipalName() {
   primary_profile_->GetPrefs()->ClearPref(prefs::kKerberosActivePrincipalName);
+  kerberos_files_handler_.DeleteFiles();
 }
 
-void KerberosCredentialsManager::ValidateActivePrincipal(
+void KerberosCredentialsManager::ValidateActivePrincipal() {
+  kerberos::ListAccountsRequest request;
+  KerberosClient::Get()->ListAccounts(
+      request,
+      base::BindOnce(&KerberosCredentialsManager::DoValidateActivePrincipal,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void KerberosCredentialsManager::DoValidateActivePrincipal(
     const kerberos::ListAccountsResponse& response) {
   const std::string& active_principal = GetActivePrincipalName();
   bool found = false;
   for (int n = 0; n < response.accounts_size() && !found; ++n)
     found |= response.accounts(n).principal_name() == active_principal;
+
   if (!found) {
-    LOG(ERROR) << "Active principal does not exist. Restoring.";
+    LOG(ERROR) << "Active principal got removed. Restoring.";
     if (response.accounts_size() > 0)
       SetActivePrincipalName(response.accounts(0).principal_name());
     else
@@ -721,15 +749,16 @@
 }
 
 void KerberosCredentialsManager::UpdateEnabledFromPref() {
-  if (local_state_->GetBoolean(prefs::kKerberosEnabled)) {
+  if (IsKerberosEnabled()) {
     // Kerberos got enabled, re-populate managed accounts.
+    VLOG(1) << "Kerberos got enabled, populating managed accounts";
     UpdateAccountsFromPref();
     return;
   }
 
   // Note that ClearAccounts logs an error if the operation fails.
   VLOG(1) << "Kerberos got disabled, clearing accounts";
-  ClearAccounts(base::BindOnce([](kerberos::ErrorType) {}));
+  ClearAccounts(EmptyResultCallback());
 }
 
 void KerberosCredentialsManager::UpdateRememberPasswordEnabledFromPref() {
@@ -740,9 +769,9 @@
   kerberos::ClearAccountsRequest request;
   request.set_mode(kerberos::CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS);
   KerberosClient::Get()->ClearAccounts(
-      request,
-      base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
-                     weak_factory_.GetWeakPtr(), EmptyResultCallback()));
+      request, base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
+                              weak_factory_.GetWeakPtr(), request.mode(),
+                              EmptyResultCallback()));
 }
 
 void KerberosCredentialsManager::UpdateAddAccountsAllowedFromPref() {
@@ -753,27 +782,32 @@
   kerberos::ClearAccountsRequest request;
   request.set_mode(kerberos::CLEAR_ONLY_UNMANAGED_ACCOUNTS);
   KerberosClient::Get()->ClearAccounts(
-      request,
-      base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
-                     weak_factory_.GetWeakPtr(), EmptyResultCallback()));
+      request, base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
+                              weak_factory_.GetWeakPtr(), request.mode(),
+                              EmptyResultCallback()));
 }
 
 void KerberosCredentialsManager::UpdateAccountsFromPref() {
-  if (!local_state_->GetBoolean(prefs::kKerberosEnabled)) {
+  if (!IsKerberosEnabled()) {
     VLOG(1) << "Kerberos disabled";
     NotifyRequiresLoginPassword(false);
+    // All managed accounts have already been removed here. No need to call
+    // RemoveAllManagedAccountsExcept().
     return;
   }
 
+  // Principal names of all accounts added.
   const base::Value* accounts = local_state_->GetList(prefs::kKerberosAccounts);
   if (!accounts) {
     VLOG(1) << "No KerberosAccounts policy";
     NotifyRequiresLoginPassword(false);
+    RemoveAllManagedAccountsExcept({});
     return;
   }
 
   VLOG(1) << accounts->GetList().size() << " accounts in KerberosAccounts";
   bool requires_login_password = false;
+  std::vector<std::string> managed_accounts_added;
   for (const auto& account : accounts->GetList()) {
     // Get the principal. Should always be set.
     const base::Value* principal_value = account.FindPath(kPrincipal);
@@ -788,10 +822,6 @@
       continue;
     }
 
-    // Kickstart active principal if it's not set yet.
-    if (GetActivePrincipalName().empty())
-      SetActivePrincipalName(principal);
-
     // Get the password, default to not set.
     const std::string* password_str = account.FindStringKey(kPassword);
     base::Optional<std::string> password;
@@ -825,10 +855,27 @@
     add_account_runners_.push_back(std::make_unique<KerberosAddAccountRunner>(
         this, principal, true /* is_managed */, password, remember_password,
         krb5_conf, true /* allow_existing */, EmptyResultCallback()));
+    managed_accounts_added.push_back(principal);
   }
 
   // Let UserSessionManager know whether it should keep the login password.
   NotifyRequiresLoginPassword(requires_login_password);
+  RemoveAllManagedAccountsExcept(std::move(managed_accounts_added));
+}
+
+void KerberosCredentialsManager::RemoveAllManagedAccountsExcept(
+    std::vector<std::string> keep_list) {
+  VLOG(1) << "Clearing out managed accounts except for " << keep_list.size();
+
+  kerberos::ClearAccountsRequest request;
+  request.set_mode(kerberos::CLEAR_ONLY_MANAGED_ACCOUNTS);
+  for (const std::string& principal_name : keep_list)
+    *request.add_principal_names_to_ignore() = principal_name;
+
+  KerberosClient::Get()->ClearAccounts(
+      request, base::BindOnce(&KerberosCredentialsManager::OnClearAccounts,
+                              weak_factory_.GetWeakPtr(), request.mode(),
+                              EmptyResultCallback()));
 }
 
 void KerberosCredentialsManager::NotifyRequiresLoginPassword(
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
index 73294e1..0e19b8f 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h
@@ -73,6 +73,9 @@
   // Returns the default Kerberos configuration (krb5.conf).
   static const char* GetDefaultKerberosConfig();
 
+  // Returns true if the Kerberos feature is enabled.
+  bool IsKerberosEnabled();
+
   // PolicyService:
   void OnPolicyUpdated(const policy::PolicyNamespace& ns,
                        const policy::PolicyMap& previous,
@@ -160,7 +163,8 @@
                        const kerberos::RemoveAccountResponse& response);
 
   // Callback for ClearAccounts().
-  void OnClearAccounts(ResultCallback callback,
+  void OnClearAccounts(kerberos::ClearMode mode,
+                       ResultCallback callback,
                        const kerberos::ClearAccountsResponse& response);
 
   // Callback for RemoveAccount().
@@ -201,11 +205,15 @@
   void SetActivePrincipalName(const std::string& principal_name);
   void ClearActivePrincipalName();
 
+  // Gets the current account list and calls DoValidateActivePrincipal().
+  void ValidateActivePrincipal();
+
   // Checks whether the active principal is contained in the given |response|.
   // If not, resets it to the first principal or clears it if the list is empty.
   // It's not expected that this ever triggers, but it provides a fail safe if
   // the active principal should ever break for whatever reason.
-  void ValidateActivePrincipal(const kerberos::ListAccountsResponse& response);
+  void DoValidateActivePrincipal(
+      const kerberos::ListAccountsResponse& response);
 
   // Notification shown when the Kerberos ticket is about to expire.
   void ShowTicketExpiryNotification();
@@ -216,6 +224,12 @@
   void UpdateAddAccountsAllowedFromPref();
   void UpdateAccountsFromPref();
 
+  // Does the main work for UpdateAccountsFromPref(). To clean up stale managed
+  // accounts, an up-to-date accounts list is needed. UpdateAccountsFromPref()
+  // first gets a list of accounts (asynchronously) and calls into this method
+  // to set new accounts and clean up old ones.
+  void RemoveAllManagedAccountsExcept(std::vector<std::string> keep_list);
+
   // Informs session manager whether it needs to store the login password in the
   // kernel keyring. That's the case when '${PASSWORD}' is used as password in
   // the KerberosAccounts policy. The Kerberos daemon expands that to the login
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index c1e128dfc..4af7571b 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -1026,8 +1026,9 @@
       l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_BUTTON)));
   const base::string16 title =
       l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_TITLE);
-  const base::string16 message = l10n_util::GetStringUTF16(
-      IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING);
+  const base::string16 message = l10n_util::GetStringFUTF16(
+      IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING,
+      base::UTF8ToUTF16(connector->GetEnterpriseDisplayDomain()));
   auto delegate =
       base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
           base::BindRepeating([](base::Optional<int> button_index) {
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index fe11888..79fafcec 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -619,8 +619,8 @@
 }
 
 void UserSelectionScreen::CheckUserStatus(const AccountId& account_id) {
-  // No checks on lock screen.
-  if (ScreenLocker::default_screen_locker())
+  // No checks on the multi-profiles signin or locker screen.
+  if (user_manager::UserManager::Get()->IsUserLoggedIn())
     return;
 
   if (!token_handle_util_.get()) {
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
index aae0761..ec94c87 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
 #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
 #include "chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 #include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
@@ -104,10 +106,15 @@
     // features (TPM firmware update) depend on system services running, which
     // is in turn blocked on the 'login-prompt-visible' signal.
     PrefService* local_state = g_browser_process->local_state();
-    if (local_state->GetBoolean(prefs::kFactoryResetRequested))
+    if (local_state->GetBoolean(prefs::kFactoryResetRequested)) {
       host_->StartWizard(ResetView::kScreenId);
-    else if (local_state->GetBoolean(prefs::kDebuggingFeaturesRequested))
+    } else if (local_state->GetBoolean(prefs::kDebuggingFeaturesRequested)) {
       host_->StartWizard(EnableDebuggingScreenView::kScreenId);
+    } else if (!KioskAppManager::Get()->GetAutoLaunchApp().empty() &&
+               KioskAppManager::Get()->IsAutoLaunchRequested()) {
+      VLOG(0) << "Showing auto-launch warning";
+      host_->StartWizard(KioskAutolaunchScreenView::kScreenId);
+    }
   }
 }
 
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc b/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc
index 3b1ddfb..cae9121 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc
+++ b/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc
@@ -55,6 +55,7 @@
 constexpr char kTestUserGaiaId[] = "1234567890";
 constexpr char kWifiServicePath[] = "/service/wifi";
 constexpr char kWifiGuid[] = "wifi";
+constexpr char kProbeUrl[] = "http://play.googleapis.com/generate_204";
 
 void ErrorCallbackFunction(const std::string& error_name,
                            const std::string& error_message) {
@@ -95,6 +96,10 @@
         dbus::ObjectPath(kWifiServicePath), shill::kStateProperty,
         base::Value(shill::kStateRedirectFound), base::DoNothing(),
         base::Bind(&ErrorCallbackFunction));
+    DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
+        dbus::ObjectPath(kWifiServicePath), shill::kProbeUrlProperty,
+        base::Value(kProbeUrl), base::DoNothing(),
+        base::Bind(&ErrorCallbackFunction));
 
     display_service_ = std::make_unique<NotificationDisplayServiceTester>(
         nullptr /* profile */);
@@ -175,6 +180,7 @@
   // No notification until portal detection is completed.
   EXPECT_FALSE(display_service_->GetNotification(kNotificationId));
   RestartDetection();
+  EXPECT_EQ(kProbeUrl, get_probe_url());
   CompleteURLFetch(net::OK, 200, nullptr);
 
   // Check that wifi is marked as behind the portal and that notification
diff --git a/chrome/browser/component_updater/component_updater_prefs.cc b/chrome/browser/component_updater/component_updater_prefs.cc
index 7a6604e..e9de57ac 100644
--- a/chrome/browser/component_updater/component_updater_prefs.cc
+++ b/chrome/browser/component_updater/component_updater_prefs.cc
@@ -7,7 +7,11 @@
 #include "chrome/browser/component_updater/chrome_component_updater_configurator.h"
 #include "chrome/browser/component_updater/recovery_component_installer.h"
 #include "chrome/browser/component_updater/recovery_improved_component_installer.h"
+#include "chrome/common/buildflags.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
+#endif
 
 namespace component_updater {
 
@@ -15,7 +19,9 @@
   RegisterPrefsForChromeComponentUpdaterConfigurator(registry);
   RegisterPrefsForRecoveryComponent(registry);
   RegisterPrefsForRecoveryImprovedComponent(registry);
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   SupervisedUserWhitelistInstaller::RegisterPrefs(registry);
+#endif
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/safety_tips_component_installer.cc b/chrome/browser/component_updater/safety_tips_component_installer.cc
new file mode 100644
index 0000000..2b98359
--- /dev/null
+++ b/chrome/browser/component_updater/safety_tips_component_installer.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 "chrome/browser/component_updater/safety_tips_component_installer.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "chrome/browser/lookalikes/safety_tips/safety_tips.pb.h"
+#include "chrome/browser/lookalikes/safety_tips/safety_tips_config.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace {
+
+const base::FilePath::CharType kSafetyTipsConfigBinaryPbFileName[] =
+    FILE_PATH_LITERAL("safety_tips.pb");
+
+// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
+// The extension id is: jflookgnkcckhobaglndicnbbgbonegd
+const uint8_t kSafetyTipsPublicKeySHA256[32] = {
+    0x95, 0xbe, 0xea, 0x6d, 0xa2, 0x2a, 0x7e, 0x10, 0x6b, 0xd3, 0x82,
+    0xd1, 0x16, 0x1e, 0xd4, 0x63, 0x21, 0xfe, 0x79, 0x5d, 0x02, 0x30,
+    0xc2, 0xcf, 0x4a, 0x9c, 0x8a, 0x39, 0xcc, 0x4a, 0x00, 0xce};
+
+std::unique_ptr<chrome_browser_safety_tips::SafetyTipsConfig>
+LoadSafetyTipsProtoFromDisk(const base::FilePath& pb_path) {
+  std::string binary_pb;
+  if (!base::ReadFileToString(pb_path, &binary_pb)) {
+    // The file won't exist on new installations, so this is not always an
+    // error.
+    DVLOG(1) << "Failed reading from " << pb_path.value();
+    return nullptr;
+  }
+  auto proto = std::make_unique<chrome_browser_safety_tips::SafetyTipsConfig>();
+  if (!proto->ParseFromString(binary_pb)) {
+    DVLOG(1) << "Failed parsing proto " << pb_path.value();
+    return nullptr;
+  }
+  return proto;
+}
+
+}  // namespace
+
+namespace component_updater {
+
+SafetyTipsComponentInstallerPolicy::SafetyTipsComponentInstallerPolicy() =
+    default;
+
+SafetyTipsComponentInstallerPolicy::~SafetyTipsComponentInstallerPolicy() =
+    default;
+
+bool SafetyTipsComponentInstallerPolicy::
+    SupportsGroupPolicyEnabledComponentUpdates() const {
+  return false;
+}
+
+bool SafetyTipsComponentInstallerPolicy::RequiresNetworkEncryption() const {
+  return false;
+}
+
+update_client::CrxInstaller::Result
+SafetyTipsComponentInstallerPolicy::OnCustomInstall(
+    const base::DictionaryValue& /* manifest */,
+    const base::FilePath& /* install_dir */) {
+  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
+}
+
+void SafetyTipsComponentInstallerPolicy::OnCustomUninstall() {}
+
+base::FilePath SafetyTipsComponentInstallerPolicy::GetInstalledPath(
+    const base::FilePath& base) {
+  return base.Append(kSafetyTipsConfigBinaryPbFileName);
+}
+
+void SafetyTipsComponentInstallerPolicy::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& install_dir,
+    std::unique_ptr<base::DictionaryValue> /* manifest */) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DVLOG(1) << "Component ready, version " << version.GetString() << " in "
+           << install_dir.value();
+
+  const base::FilePath pb_path = GetInstalledPath(install_dir);
+  if (pb_path.empty())
+    return;
+
+  // The default proto will always be a placeholder since the updated versions
+  // are not checked in to the repo. Simply load whatever the component updater
+  // gave us without checking the default proto from the resource bundle.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&LoadSafetyTipsProtoFromDisk, pb_path),
+      base::BindOnce(&safety_tips::SetProto));
+}
+
+// Called during startup and installation before ComponentReady().
+bool SafetyTipsComponentInstallerPolicy::VerifyInstallation(
+    const base::DictionaryValue& /* manifest */,
+    const base::FilePath& install_dir) const {
+  // No need to actually validate the proto here, since we'll do the checking
+  // in PopulateFromDynamicUpdate().
+  return base::PathExists(GetInstalledPath(install_dir));
+}
+
+base::FilePath SafetyTipsComponentInstallerPolicy::GetRelativeInstallDir()
+    const {
+  return base::FilePath(FILE_PATH_LITERAL("SafetyTips"));
+}
+
+void SafetyTipsComponentInstallerPolicy::GetHash(
+    std::vector<uint8_t>* hash) const {
+  hash->assign(
+      kSafetyTipsPublicKeySHA256,
+      kSafetyTipsPublicKeySHA256 + base::size(kSafetyTipsPublicKeySHA256));
+}
+
+std::string SafetyTipsComponentInstallerPolicy::GetName() const {
+  return "Safety Tips";
+}
+
+update_client::InstallerAttributes
+SafetyTipsComponentInstallerPolicy::GetInstallerAttributes() const {
+  return update_client::InstallerAttributes();
+}
+
+std::vector<std::string> SafetyTipsComponentInstallerPolicy::GetMimeTypes()
+    const {
+  return std::vector<std::string>();
+}
+
+void RegisterSafetyTipsComponent(ComponentUpdateService* cus,
+                                 const base::FilePath& /* user_data_dir */) {
+  DVLOG(1) << "Registering Safety Tips component.";
+
+  auto installer = base::MakeRefCounted<ComponentInstaller>(
+      std::make_unique<SafetyTipsComponentInstallerPolicy>());
+  installer->Register(cus, base::OnceClosure());
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/safety_tips_component_installer.h b/chrome/browser/component_updater/safety_tips_component_installer.h
new file mode 100644
index 0000000..eeeb8cab
--- /dev/null
+++ b/chrome/browser/component_updater/safety_tips_component_installer.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_SAFETY_TIPS_COMPONENT_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_SAFETY_TIPS_COMPONENT_INSTALLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/component_updater/component_installer.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace component_updater {
+
+class SafetyTipsComponentInstallerPolicy : public ComponentInstallerPolicy {
+ public:
+  SafetyTipsComponentInstallerPolicy();
+  ~SafetyTipsComponentInstallerPolicy() override;
+
+ private:
+  // ComponentInstallerPolicy methods:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+  bool RequiresNetworkEncryption() const override;
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::DictionaryValue& manifest,
+      const base::FilePath& install_dir) override;
+  void OnCustomUninstall() override;
+  bool VerifyInstallation(const base::DictionaryValue& manifest,
+                          const base::FilePath& install_dir) const override;
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      std::unique_ptr<base::DictionaryValue> manifest) override;
+  base::FilePath GetRelativeInstallDir() const override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetName() const override;
+  update_client::InstallerAttributes GetInstallerAttributes() const override;
+  std::vector<std::string> GetMimeTypes() const override;
+
+  static base::FilePath GetInstalledPath(const base::FilePath& base);
+
+  DISALLOW_COPY_AND_ASSIGN(SafetyTipsComponentInstallerPolicy);
+};
+
+void RegisterSafetyTipsComponent(ComponentUpdateService* cus,
+                                 const base::FilePath& user_data_dir);
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_SAFETY_TIPS_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/downgrade/user_data_downgrade.cc b/chrome/browser/downgrade/user_data_downgrade.cc
index 9f7c3a882..c59e480 100644
--- a/chrome/browser/downgrade/user_data_downgrade.cc
+++ b/chrome/browser/downgrade/user_data_downgrade.cc
@@ -110,8 +110,8 @@
   if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
     return;
   const base::Version current_version(chrome::kChromeVersion);
-  const base::Version downgrade_version = InstallUtil::GetDowngradeVersion();
-  if (downgrade_version.IsValid() && downgrade_version > current_version) {
+  // Note: nullopt is always less than any value.
+  if (InstallUtil::GetDowngradeVersion() > current_version) {
     base::FilePath disk_cache_dir(GetDiskCacheDir());
     // Without the browser process singleton protection, the directory may be
     // copied multiple times. In order to prevent that from happening, the temp
diff --git a/chrome/browser/download/download_frame_policy_browsertest.cc b/chrome/browser/download/download_frame_policy_browsertest.cc
index 6d2f412..33ef238 100644
--- a/chrome/browser/download/download_frame_policy_browsertest.cc
+++ b/chrome/browser/download/download_frame_policy_browsertest.cc
@@ -331,11 +331,10 @@
       !enable_blocking_downloads_in_sandbox_without_user_activation ||
       initiate_with_gesture ||
       sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
-
+  bool sandboxed =
+      sandbox_option == SandboxOption::kDisallowDownloadsWithoutUserActivation;
   bool expect_download_in_sandbox_without_user_activation =
-      sandbox_option ==
-          SandboxOption::kDisallowDownloadsWithoutUserActivation &&
-      !initiate_with_gesture;
+      sandboxed && !initiate_with_gesture;
 
   InitializeHistogramTesterAndWebFeatureWaiter();
   SetNumDownloadsExpectation(expect_download);
@@ -348,6 +347,10 @@
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadPostPolicyCheck);
   }
+  if (sandboxed) {
+    GetWebFeatureWaiter()->AddWebFeatureExpectation(
+        blink::mojom::WebFeature::kDownloadInSandbox);
+  }
   if (expect_download_in_sandbox_without_user_activation) {
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadInSandboxWithoutUserGesture);
@@ -423,8 +426,6 @@
   bool expect_download =
       !enable_blocking_downloads_in_ad_frame_without_user_activation ||
       initiate_with_gesture || !is_ad_frame;
-  bool expect_download_in_ad_frame_with_user_activation =
-      is_ad_frame && initiate_with_gesture;
   bool expect_download_in_ad_frame_without_user_activation =
       is_ad_frame && !initiate_with_gesture;
 
@@ -439,9 +440,9 @@
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadPostPolicyCheck);
   }
-  if (expect_download_in_ad_frame_with_user_activation) {
+  if (is_ad_frame) {
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
-        blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture);
+        blink::mojom::WebFeature::kDownloadInAdFrame);
   }
   if (expect_download_in_ad_frame_without_user_activation) {
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
@@ -519,6 +520,8 @@
 
   GetWebFeatureWaiter()->AddWebFeatureExpectation(
       blink::mojom::WebFeature::kDownloadPrePolicyCheck);
+  GetWebFeatureWaiter()->AddWebFeatureExpectation(
+      blink::mojom::WebFeature::kDownloadInSandbox);
   if (!expect_gesture) {
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadInSandboxWithoutUserGesture);
@@ -631,10 +634,10 @@
 
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadPrePolicyCheck);
-    if (expect_gesture) {
-      GetWebFeatureWaiter()->AddWebFeatureExpectation(
-          blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture);
-    } else {
+    GetWebFeatureWaiter()->AddWebFeatureExpectation(
+        blink::mojom::WebFeature::kDownloadInAdFrame);
+
+    if (!expect_gesture) {
       GetWebFeatureWaiter()->AddWebFeatureExpectation(
           blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture);
     }
@@ -724,11 +727,10 @@
       !enable_blocking_downloads_in_sandbox_without_user_activation ||
       initiate_with_gesture ||
       sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
-
+  bool sandboxed =
+      sandbox_option == SandboxOption::kDisallowDownloadsWithoutUserActivation;
   bool expect_download_in_sandbox_without_user_activation =
-      sandbox_option ==
-          SandboxOption::kDisallowDownloadsWithoutUserActivation &&
-      !initiate_with_gesture;
+      sandboxed && !initiate_with_gesture;
 
   InitializeHistogramTesterAndWebFeatureWaiter();
   SetNumDownloadsExpectation(expect_download);
@@ -740,6 +742,10 @@
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadPostPolicyCheck);
   }
+  if (sandboxed) {
+    GetWebFeatureWaiter()->AddWebFeatureExpectation(
+        blink::mojom::WebFeature::kDownloadInSandbox);
+  }
   if (expect_download_in_sandbox_without_user_activation) {
     GetWebFeatureWaiter()->AddWebFeatureExpectation(
         blink::mojom::WebFeature::kDownloadInSandboxWithoutUserGesture);
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 0a86a59..e26b2ba 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -770,6 +770,7 @@
   ]
   deps = [
     "//apps",
+    "//build:branding_buildflags",
     "//chrome:extra_resources",
     "//chrome:resources",
     "//chrome:strings",
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
index c871542..42b5813 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
@@ -67,6 +67,7 @@
 const char kCertificateProviderNoActiveDialog[] =
     "No active dialog from extension.";
 const char kCertificateProviderInvalidId[] = "Invalid signRequestId";
+const char kCertificateProviderInvalidAttemptsLeft[] = "Invalid attemptsLeft";
 const char kCertificateProviderOtherFlowInProgress[] = "Other flow in progress";
 const char kCertificateProviderPreviousDialogActive[] =
     "Previous request not finished";
@@ -292,8 +293,13 @@
           browser_context());
   DCHECK(service);
 
-  int attempts_left =
-      params->details.attempts_left ? *params->details.attempts_left : -1;
+  int attempts_left = -1;
+  if (params->details.attempts_left) {
+    if (*params->details.attempts_left < 0)
+      return RespondNow(Error(kCertificateProviderInvalidAttemptsLeft));
+    attempts_left = *params->details.attempts_left;
+  }
+
   const chromeos::PinDialogManager::RequestPinResult result =
       service->pin_dialog_manager()->RequestPin(
           extension()->id(), extension()->name(),
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index c69cff3..2a26615 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -569,6 +569,16 @@
   EXPECT_TRUE(listener.WaitUntilSatisfied());
 }
 
+// Extension erroneously passes a negative attempts left count.
+IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, NegativeAttempts) {
+  AddFakeSignRequest();
+  NavigateTo("operated.html");
+
+  EXPECT_TRUE(SendCommandAndWaitForMessage(
+      "RequestWithNegativeAttempts", "request1:error:Invalid attemptsLeft"));
+  EXPECT_FALSE(GetActivePinDialogView());
+}
+
 // Extension erroneously attempts to close a non-existing dialog.
 IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, CloseNonExisting) {
   AddFakeSignRequest();
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index a9f1a55e..ef0c3b6b 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -32,7 +32,6 @@
 #include "chrome/common/extensions/api/extension_action/action_info.h"
 #include "content/public/browser/notification_service.h"
 #include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_function_registry.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
@@ -90,24 +89,7 @@
     DestructorAtExit g_extension_action_api_factory = LAZY_INSTANCE_INITIALIZER;
 
 ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
-    : browser_context_(context),
-      extension_prefs_(nullptr) {
-  ExtensionFunctionRegistry& registry =
-      ExtensionFunctionRegistry::GetInstance();
-
-  // Actions
-  // TODO(devlin): Remove this bespoke registration when action.enable() and
-  // action.disable() have appropriate tests.
-  registry.RegisterFunction<ActionSetIconFunction>();
-  registry.RegisterFunction<ActionGetPopupFunction>();
-  registry.RegisterFunction<ActionSetPopupFunction>();
-  registry.RegisterFunction<ActionGetTitleFunction>();
-  registry.RegisterFunction<ActionSetTitleFunction>();
-  registry.RegisterFunction<ActionGetBadgeTextFunction>();
-  registry.RegisterFunction<ActionSetBadgeTextFunction>();
-  registry.RegisterFunction<ActionGetBadgeBackgroundColorFunction>();
-  registry.RegisterFunction<ActionSetBadgeBackgroundColorFunction>();
-}
+    : browser_context_(context), extension_prefs_(nullptr) {}
 
 ExtensionActionAPI::~ExtensionActionAPI() {
 }
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.h b/chrome/browser/extensions/api/extension_action/extension_action_api.h
index 63b1accf..f4d9ea2 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.h
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.h
@@ -330,7 +330,21 @@
   ~ActionSetBadgeBackgroundColorFunction() override {}
 };
 
-// TODO(devlin): Add the rest of the action APIs here.
+class ActionEnableFunction : public ExtensionActionShowFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.enable", ACTION_ENABLE)
+
+ protected:
+  ~ActionEnableFunction() override {}
+};
+
+class ActionDisableFunction : public ExtensionActionHideFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("action.disable", ACTION_DISABLE)
+
+ protected:
+  ~ActionDisableFunction() override {}
+};
 
 //
 // browserAction.* aliases for supported browserAction APIs.
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 8a6c1f5..c90241d 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -234,8 +234,7 @@
   }
 
   // Ensures the |action| is enabled on the tab with the given |tab_id|.
-  void EnsureActionIsEnabledOnActiveTab(ExtensionAction* action) {
-    const int tab_id = GetActiveTabId();
+  void EnsureActionIsEnabledOnTab(ExtensionAction* action, int tab_id) {
     if (action->GetIsVisible(tab_id))
       return;
     action->SetIsVisible(tab_id, true);
@@ -246,6 +245,11 @@
     extension_action_api->NotifyChange(action, GetActiveTab(), profile());
   }
 
+  // Ensures the |action| is enabled on the currently-active tab.
+  void EnsureActionIsEnabledOnActiveTab(ExtensionAction* action) {
+    EnsureActionIsEnabledOnTab(action, GetActiveTabId());
+  }
+
   // Returns the id of the currently-active tab.
   int GetActiveTabId() const {
     content::WebContents* web_contents = GetActiveTab();
@@ -912,6 +916,121 @@
   }
 }
 
+// Tests the functions to enable and disable extension actions.
+IN_PROC_BROWSER_TEST_P(MultiActionAPITest, EnableAndDisable) {
+  constexpr char kManifestTemplate[] =
+      R"({
+           "name": "enabled/disabled action test",
+           "version": "0.1",
+           "manifest_version": 2,
+           "%s": {},
+           "background": {"scripts": ["background.js"]}
+         })";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      base::StringPrintf(kManifestTemplate, GetManifestKey(GetParam())));
+  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
+                     "// This space left blank.");
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  ExtensionAction* action = GetExtensionAction(*extension);
+  ASSERT_TRUE(action);
+
+  const int tab_id1 = GetActiveTabId();
+  EnsureActionIsEnabledOnTab(action, tab_id1);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("chrome://newtab"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  const int tab_id2 = GetActiveTabId();
+  EnsureActionIsEnabledOnTab(action, tab_id2);
+
+  EXPECT_NE(tab_id1, tab_id2);
+
+  const char* enable_function = nullptr;
+  const char* disable_function = nullptr;
+  switch (GetParam()) {
+    case ActionInfo::TYPE_ACTION:
+    case ActionInfo::TYPE_BROWSER:
+      enable_function = "enable";
+      disable_function = "disable";
+      break;
+    case ActionInfo::TYPE_PAGE:
+      enable_function = "show";
+      disable_function = "hide";
+      break;
+  }
+
+  // Start by toggling the extension action on the current tab.
+  {
+    constexpr char kScriptTemplate[] =
+        R"(chrome.%s.%s(%d, () => {
+            chrome.test.assertNoLastError();
+            chrome.test.notifyPass();
+           });)";
+    RunTestAndWaitForSuccess(
+        profile(), extension->id(),
+        base::StringPrintf(kScriptTemplate, GetAPIName(GetParam()),
+                           disable_function, tab_id2));
+    EXPECT_FALSE(action->GetIsVisible(tab_id2));
+    EXPECT_TRUE(action->GetIsVisible(tab_id1));
+  }
+
+  {
+    constexpr char kScriptTemplate[] =
+        R"(chrome.%s.%s(%d, () => {
+            chrome.test.assertNoLastError();
+            chrome.test.notifyPass();
+           });)";
+    RunTestAndWaitForSuccess(
+        profile(), extension->id(),
+        base::StringPrintf(kScriptTemplate, GetAPIName(GetParam()),
+                           enable_function, tab_id2));
+    EXPECT_TRUE(action->GetIsVisible(tab_id2));
+    EXPECT_TRUE(action->GetIsVisible(tab_id1));
+  }
+
+  // Page actions can't be enabled/disabled globally, but others can. Try
+  // toggling global state by omitting the tab id if the type isn't a page
+  // action.
+  if (GetParam() == ActionInfo::TYPE_PAGE)
+    return;
+
+  // We need to undo the explicit enable from above, since tab-specific
+  // values take precedence.
+  action->ClearAllValuesForTab(tab_id2);
+  {
+    constexpr char kScriptTemplate[] =
+        R"(chrome.%s.%s(() => {
+            chrome.test.assertNoLastError();
+            chrome.test.notifyPass();
+           });)";
+    RunTestAndWaitForSuccess(
+        profile(), extension->id(),
+        base::StringPrintf(kScriptTemplate, GetAPIName(GetParam()),
+                           disable_function));
+    EXPECT_EQ(false, action->GetIsVisible(tab_id2));
+    EXPECT_EQ(false, action->GetIsVisible(tab_id1));
+  }
+
+  {
+    constexpr char kScriptTemplate[] =
+        R"(chrome.%s.%s(() => {
+            chrome.test.assertNoLastError();
+            chrome.test.notifyPass();
+           });)";
+    RunTestAndWaitForSuccess(
+        profile(), extension->id(),
+        base::StringPrintf(kScriptTemplate, GetAPIName(GetParam()),
+                           enable_function));
+    EXPECT_EQ(true, action->GetIsVisible(tab_id2));
+    EXPECT_EQ(true, action->GetIsVisible(tab_id1));
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          MultiActionAPITest,
                          testing::Values(ActionInfo::TYPE_ACTION,
diff --git a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
index 312afc2b..af20dd9 100644
--- a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
@@ -51,10 +51,6 @@
 
 // Tests chrome.management.uninstallSelf API.
 IN_PROC_BROWSER_TEST_P(ManagementApiNonPersistentApiTest, UninstallSelf) {
-  if (GetParam() == ContextType::kEventPage) {
-    // Test has been flaky on all platforms. https://crbug.com/985936.
-    return;
-  }
   constexpr char kEventPageBackgroundScript[] = R"({"scripts": ["script.js"]})";
   constexpr char kServiceWorkerBackgroundScript[] =
       R"({"service_worker": "script.js"})";
@@ -86,7 +82,9 @@
       extensions::ExtensionRegistry::Get(browser()->profile()));
 
   base::FilePath path = test_dir.Pack();
-  scoped_refptr<const Extension> extension = LoadExtension(path);
+  // NOTE: Do not use a scoped_refptr<Extension> as the |extension| might get
+  // uninstalled right away.
+  const Extension* extension = LoadExtension(path);
 
   EXPECT_EQ(extension, observer.WaitForExtensionUninstalled());
 }
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.cc b/chrome/browser/extensions/chrome_process_manager_delegate.cc
index 9d7ba9e9..e79841d 100644
--- a/chrome/browser/extensions/chrome_process_manager_delegate.cc
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_service.h"
@@ -33,8 +34,7 @@
 namespace extensions {
 
 ChromeProcessManagerDelegate::ChromeProcessManagerDelegate() {
-  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
-                 content::NotificationService::AllSources());
+  BrowserList::AddObserver(this);
   registrar_.Add(this,
                  chrome::NOTIFICATION_PROFILE_CREATED,
                  content::NotificationService::AllSources());
@@ -44,6 +44,7 @@
 }
 
 ChromeProcessManagerDelegate::~ChromeProcessManagerDelegate() {
+  BrowserList::RemoveObserver(this);
 }
 
 bool ChromeProcessManagerDelegate::AreBackgroundPagesAllowedForContext(
@@ -111,7 +112,7 @@
 
   // There are no browser windows open and the browser process was
   // started to show the app launcher. Background hosts will be loaded later
-  // via NOTIFICATION_BROWSER_OPENED. http://crbug.com/178260
+  // via OnBrowserAdded(). http://crbug.com/178260
   return chrome::GetBrowserCount(profile) == 0 &&
          base::CommandLine::ForCurrentProcess()->HasSwitch(
              ::switches::kShowAppList);
@@ -122,11 +123,6 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
   switch (type) {
-    case chrome::NOTIFICATION_BROWSER_OPENED: {
-      Browser* browser = content::Source<Browser>(source).ptr();
-      OnBrowserOpened(browser);
-      break;
-    }
     case chrome::NOTIFICATION_PROFILE_CREATED: {
       Profile* profile = content::Source<Profile>(source).ptr();
       OnProfileCreated(profile);
@@ -142,7 +138,7 @@
   }
 }
 
-void ChromeProcessManagerDelegate::OnBrowserOpened(Browser* browser) {
+void ChromeProcessManagerDelegate::OnBrowserAdded(Browser* browser) {
   Profile* profile = browser->profile();
   DCHECK(profile);
 
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.h b/chrome/browser/extensions/chrome_process_manager_delegate.h
index bc670a6..f4bac25c 100644
--- a/chrome/browser/extensions/chrome_process_manager_delegate.h
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "chrome/browser/ui/browser_list_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/process_manager_delegate.h"
@@ -19,12 +20,13 @@
 // Support for ProcessManager. Controls cases where Chrome wishes to disallow
 // extension background pages or defer their creation.
 class ChromeProcessManagerDelegate : public ProcessManagerDelegate,
-                                     public content::NotificationObserver {
+                                     public content::NotificationObserver,
+                                     public BrowserListObserver {
  public:
   ChromeProcessManagerDelegate();
   ~ChromeProcessManagerDelegate() override;
 
-  // ProcessManagerDelegate implementation:
+  // ProcessManagerDelegate:
   bool AreBackgroundPagesAllowedForContext(
       content::BrowserContext* context) const override;
   bool IsExtensionBackgroundPageAllowed(
@@ -33,14 +35,16 @@
   bool DeferCreatingStartupBackgroundHosts(
       content::BrowserContext* context) const override;
 
-  // content::NotificationObserver implementation:
+  // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override;
+
  private:
   // Notification handlers.
-  void OnBrowserOpened(Browser* browser);
   void OnProfileCreated(Profile* profile);
   void OnProfileDestroyed(Profile* profile);
 
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 02c665c..a83f450 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -1097,4 +1097,40 @@
 TEST_F(ExtensionPrefsRuntimeGrantedPermissions,
        ExtensionPrefsRuntimeGrantedPermissions) {}
 
+// Tests the removal of obsolete keys from extension pref entries.
+class ExtensionPrefsObsoletePrefRemoval : public ExtensionPrefsTest {
+ public:
+  ExtensionPrefsObsoletePrefRemoval() = default;
+  ~ExtensionPrefsObsoletePrefRemoval() override = default;
+
+  void Initialize() override {
+    extension_ = prefs_.AddExtension("a");
+    constexpr char kTestValue[] = "test_value";
+    prefs()->UpdateExtensionPref(extension_->id(),
+                                 ExtensionPrefs::kFakeObsoletePrefForTesting,
+                                 std::make_unique<base::Value>(kTestValue));
+    std::string str_value;
+    EXPECT_TRUE(prefs()->ReadPrefAsString(
+        extension_->id(), ExtensionPrefs::kFakeObsoletePrefForTesting,
+        &str_value));
+    EXPECT_EQ(kTestValue, str_value);
+
+    prefs()->MigrateObsoleteExtensionPrefs();
+  }
+
+  void Verify() override {
+    std::string str_value;
+    EXPECT_FALSE(prefs()->ReadPrefAsString(
+        extension_->id(), ExtensionPrefs::kFakeObsoletePrefForTesting,
+        &str_value));
+  }
+
+ private:
+  scoped_refptr<const Extension> extension_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionPrefsObsoletePrefRemoval);
+};
+
+TEST_F(ExtensionPrefsObsoletePrefRemoval, ExtensionPrefsObsoletePrefRemoval) {}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc
index 8c45fd7..2ed99c2d 100644
--- a/chrome/browser/extensions/extension_service_sync_unittest.cc
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc
@@ -86,15 +86,18 @@
 
 namespace {
 
-const char autoupdate[] = "ogjcoiohnmldgjemafoockdghcjciccf";
 const char good0[] = "behllobkkfkfnphdnhnkndlbkcpglgmj";
 const char good2[] = "bjafgdebaacbbbecmhlhpofkepfkgcpa";
-const char good2048[] = "dfhpodpjggiioolfhoimofdbfjibmedp";
 const char good_crx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
 const char page_action[] = "obcimlgaoabeegjmmpldobjndiealpln";
-const char permissions_increase[] = "pgdpcfcocojkjfbgpiianjngphoopgmo";
 const char theme2_crx[] = "ibcijncamhmjjdodjamgiipcgnnaeagd";
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+const char autoupdate[] = "ogjcoiohnmldgjemafoockdghcjciccf";
+const char good2048[] = "dfhpodpjggiioolfhoimofdbfjibmedp";
+const char permissions_increase[] = "pgdpcfcocojkjfbgpiianjngphoopgmo";
+#endif
+
 ExtensionSyncData GetDisableSyncData(const Extension& extension,
                                      int disable_reasons) {
   bool enabled = false;
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 855468eb..8e9ffd2 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -20,6 +20,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "base/version.h"
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/browser_process.h"
@@ -782,7 +783,7 @@
         bundled_extension_creation_flags));
 
     // Define a per-user source of external extensions.
-#if defined(OS_MACOSX) || (defined(OS_LINUX) && defined(CHROMIUM_BUILD))
+#if defined(OS_MACOSX) || (defined(OS_LINUX) && BUILDFLAG(CHROMIUM_BRANDING))
     provider_list->push_back(std::make_unique<ExternalProviderImpl>(
         service,
         new ExternalPrefLoader(chrome::DIR_USER_EXTERNAL_EXTENSIONS,
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 0dd8dc6..264ed9c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1524,6 +1524,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-process-sharing-with-default-site-instances",
+    "owners": [ "acolwell" ],
+    "expiry_milestone": 81
+  },
+  {
     "name": "enable-process-sharing-with-strict-site-instances",
     "owners": [ "japhet" ],
     "expiry_milestone": 80
@@ -1569,6 +1574,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-filtering-scroll-events",
+    "owners": [ "axantoine", "eirage", "nzolghadr", "input-dev" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "enable-resource-load-scheduler",
     "owners": [ "toyoshim" ],
     "expiry_milestone": 78
@@ -2535,11 +2545,6 @@
     "expiry_milestone": 80
   },
   {
-    "name": "omnibox-new-answer-layout",
-    "owners": [ "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "omnibox-on-device-head-suggestions",
     "owners": [ "cechen", "suggest-2g@google.com" ],
     "expiry_milestone": 80
@@ -3229,6 +3234,11 @@
     "name": "enable-streamlined-usb-printer-setup",
     "owners": [ "baileyberro" ],
     "expiry_milestone": 77
+  },
+  {
+    "name": "update-hover-at-begin-frame",
+    "owners": [ "lanwei", "input-dev" ],
+    "expiry_milestone": 81
   }
 
   // This is an alphabetized list; please do your part to keep it organized by
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ec94e32..5f751702 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1035,6 +1035,10 @@
     "Enables granting and removing access to features through the "
     "Feature-Policy HTTP header.";
 
+const char kFilteringScrollPredictionName[] = "Filtering scroll prediction";
+const char kFilteringScrollPredictionDescription[] =
+    "Enable filtering of predicted scroll events";
+
 const char kForceEffectiveConnectionTypeName[] =
     "Override effective connection type";
 const char kForceEffectiveConnectionTypeDescription[] =
@@ -1304,6 +1308,11 @@
     "Enable support for using the native notification toasts and notification "
     "center on platforms where these are available.";
 
+const char kUpdateHoverAtBeginFrameName[] = "Update hover at the begin frame";
+const char kUpdateHoverAtBeginFrameDescription[] =
+    "Recompute hover state at BeginFrame for layout and scroll based mouse "
+    "moves, rather than old timing-based mechanism.";
+
 const char kUseMultiloginEndpointName[] = "Use Multilogin endpoint.";
 const char kUseMultiloginEndpointDescription[] =
     "Use Gaia OAuth multilogin for identity consistency.";
@@ -1346,10 +1355,6 @@
     "Use material design weather icons in the omnibox when displaying weather "
     "answers.";
 
-const char kOmniboxNewAnswerLayoutName[] = "Omnibox new answer layout";
-const char kOmniboxNewAnswerLayoutDescription[] =
-    "Modernize omnibox answers using an enhanced layout with larger icons.";
-
 const char kOmniboxRichEntitySuggestionsName[] =
     "Omnibox rich entity suggestions";
 const char kOmniboxRichEntitySuggestionsDescription[] =
@@ -2501,6 +2506,16 @@
 const char kPasswordManagerOnboardingAndroidDescription[] =
     "This flag enables showing the password manager onboarding experience.";
 
+extern const char kProcessSharingWithDefaultSiteInstancesName[] =
+    "Process sharing with default site instances";
+extern const char kProcessSharingWithDefaultSiteInstancesDescription[] =
+    "When site isolation is disabled, this mode changes how sites are lumped "
+    "in to shared processes. For sites that do not require isolation, this "
+    "feature groups them into a single 'default' site instance (per browsing "
+    "instance) instead of creating unique site instances for each one. This "
+    "enables resource savings by creating fewer processes for sites that do "
+    "not need isolation.";
+
 extern const char kProcessSharingWithStrictSiteInstancesName[] =
     "Process sharing with strict site instances";
 extern const char kProcessSharingWithStrictSiteInstancesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d4230b8..d17b825 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -614,6 +614,9 @@
 extern const char kFeaturePolicyName[];
 extern const char kFeaturePolicyDescription[];
 
+extern const char kFilteringScrollPredictionName[];
+extern const char kFilteringScrollPredictionDescription[];
+
 extern const char kForceEffectiveConnectionTypeName[];
 extern const char kForceEffectiveConnectionTypeDescription[];
 extern const char kEffectiveConnectionTypeUnknownDescription[];
@@ -815,9 +818,6 @@
 extern const char kOmniboxMaterialDesignWeatherIconsName[];
 extern const char kOmniboxMaterialDesignWeatherIconsDescription[];
 
-extern const char kOmniboxNewAnswerLayoutName[];
-extern const char kOmniboxNewAnswerLayoutDescription[];
-
 extern const char kOmniboxRichEntitySuggestionsName[];
 extern const char kOmniboxRichEntitySuggestionsDescription[];
 
@@ -898,6 +898,9 @@
 extern const char kOverlayStrategiesUnoccluded[];
 extern const char kOverlayStrategiesOccludedAndUnoccluded[];
 
+extern const char kUpdateHoverAtBeginFrameName[];
+extern const char kUpdateHoverAtBeginFrameDescription[];
+
 extern const char kUseNewAcceptLanguageHeaderName[];
 extern const char kUseNewAcceptLanguageHeaderDescription[];
 
@@ -1480,6 +1483,9 @@
 extern const char kPasswordManagerOnboardingAndroidName[];
 extern const char kPasswordManagerOnboardingAndroidDescription[];
 
+extern const char kProcessSharingWithDefaultSiteInstancesName[];
+extern const char kProcessSharingWithDefaultSiteInstancesDescription[];
+
 extern const char kProcessSharingWithStrictSiteInstancesName[];
 extern const char kProcessSharingWithStrictSiteInstancesDescription[];
 
diff --git a/chrome/browser/installable/installable_logging.cc b/chrome/browser/installable/installable_logging.cc
index 2fd33da..a3b53188 100644
--- a/chrome/browser/installable/installable_logging.cc
+++ b/chrome/browser/installable/installable_logging.cc
@@ -30,7 +30,7 @@
 static const char kManifestMissingSuitableIconMessage[] =
     "Manifest does not contain a suitable icon - PNG format of at least "
     "%dpx is required, the sizes attribute must be set, and the purpose "
-    "attribute, if set, must include \"any\".";
+    "attribute, if set, must include \"any\" or \"maskable\".";
 static const char kNoMatchingServiceWorkerMessage[] =
     "No matching service worker detected. You may need to reload the page, or "
     "check that the service worker for the current page also controls the "
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index 64c3808..6bbb97e 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -72,9 +72,13 @@
 
 using IconPurpose = blink::Manifest::ImageResource::Purpose;
 
-// Returns true if |manifest| specifies a PNG icon with IconPurpose::ANY and of
-// height and width >= kMinimumPrimaryIconSizeInPx (or size "any").
-bool DoesManifestContainRequiredIcon(const blink::Manifest& manifest) {
+// Returns true if |manifest| specifies a PNG icon of height and width >=
+// kMinimumPrimaryIconSizeInPx (or size "any"), and either
+// 1. with IconPurpose::ANY or IconPurpose::MASKABLE, if maskable icon is
+// preferred, or
+// 2. with IconPurpose::ANY if maskable icon is not preferred.
+bool DoesManifestContainRequiredIcon(const blink::Manifest& manifest,
+                                     bool prefer_maskable_icon) {
   for (const auto& icon : manifest.icons) {
     // The type field is optional. If it isn't present, fall back on checking
     // the src extension, and allow the icon if the extension ends with png.
@@ -84,8 +88,12 @@
             base::CompareCase::INSENSITIVE_ASCII)))
       continue;
 
-    if (!base::Contains(icon.purpose,
-                        blink::Manifest::ImageResource::Purpose::ANY)) {
+    if (!(base::Contains(icon.purpose,
+                         blink::Manifest::ImageResource::Purpose::ANY) ||
+          (prefer_maskable_icon &&
+           base::Contains(
+               icon.purpose,
+               blink::Manifest::ImageResource::Purpose::MASKABLE)))) {
       continue;
     }
 
@@ -445,7 +453,8 @@
     CheckAndFetchBestIcon(GetIdealPrimaryIconSizeInPx(),
                           GetMinimumPrimaryIconSizeInPx(), IconPurpose::ANY);
   } else if (params.valid_manifest && !valid_manifest_->fetched) {
-    CheckManifestValid(params.check_webapp_manifest_display);
+    CheckManifestValid(params.check_webapp_manifest_display,
+                       params.prefer_maskable_icon);
   } else if (params.has_worker && !worker_->fetched) {
     CheckServiceWorker();
   } else if (params.valid_badge_icon && !IsIconFetched(IconPurpose::BADGE)) {
@@ -503,20 +512,21 @@
   WorkOnTask();
 }
 
-void InstallableManager::CheckManifestValid(
-    bool check_webapp_manifest_display) {
+void InstallableManager::CheckManifestValid(bool check_webapp_manifest_display,
+                                            bool prefer_maskable_icon) {
   DCHECK(!valid_manifest_->fetched);
   DCHECK(!manifest().IsEmpty());
 
-  valid_manifest_->is_valid =
-      IsManifestValidForWebApp(manifest(), check_webapp_manifest_display);
+  valid_manifest_->is_valid = IsManifestValidForWebApp(
+      manifest(), check_webapp_manifest_display, prefer_maskable_icon);
   valid_manifest_->fetched = true;
   WorkOnTask();
 }
 
 bool InstallableManager::IsManifestValidForWebApp(
     const blink::Manifest& manifest,
-    bool check_webapp_manifest_display) {
+    bool check_webapp_manifest_display,
+    bool prefer_maskable_icon) {
   bool is_valid = true;
   if (manifest.IsEmpty()) {
     valid_manifest_->errors.push_back(MANIFEST_EMPTY);
@@ -542,7 +552,7 @@
     is_valid = false;
   }
 
-  if (!DoesManifestContainRequiredIcon(manifest)) {
+  if (!DoesManifestContainRequiredIcon(manifest, prefer_maskable_icon)) {
     valid_manifest_->errors.push_back(MANIFEST_MISSING_SUITABLE_ICON);
     is_valid = false;
   }
diff --git a/chrome/browser/installable/installable_manager.h b/chrome/browser/installable/installable_manager.h
index 3803405f..d614b3d 100644
--- a/chrome/browser/installable/installable_manager.h
+++ b/chrome/browser/installable/installable_manager.h
@@ -189,9 +189,11 @@
   void OnDidGetManifest(const GURL& manifest_url,
                         const blink::Manifest& manifest);
 
-  void CheckManifestValid(bool check_webapp_manifest_display);
+  void CheckManifestValid(bool check_webapp_manifest_display,
+                          bool prefer_maskable_icon);
   bool IsManifestValidForWebApp(const blink::Manifest& manifest,
-                                bool check_webapp_manifest_display);
+                                bool check_webapp_manifest_display,
+                                bool prefer_maskable_icon);
   void CheckServiceWorker();
   void OnDidCheckHasServiceWorker(content::ServiceWorkerCapability capability);
 
diff --git a/chrome/browser/installable/installable_manager_unittest.cc b/chrome/browser/installable/installable_manager_unittest.cc
index 4ae9bbb..f19c4688 100644
--- a/chrome/browser/installable/installable_manager_unittest.cc
+++ b/chrome/browser/installable/installable_manager_unittest.cc
@@ -39,11 +39,12 @@
   }
 
   bool IsManifestValid(const blink::Manifest& manifest,
-                       bool check_webapp_manifest_display = true) {
+                       bool check_webapp_manifest_display = true,
+                       bool prefer_maskable_icon = false) {
     // Explicitly reset the error code before running the method.
     manager_->set_valid_manifest_error(NO_ERROR_DETECTED);
-    return manager_->IsManifestValidForWebApp(manifest,
-                                              check_webapp_manifest_display);
+    return manager_->IsManifestValidForWebApp(
+        manifest, check_webapp_manifest_display, prefer_maskable_icon);
   }
 
   InstallableStatusCode GetErrorCode() {
@@ -153,6 +154,32 @@
   EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
 }
 
+TEST_F(InstallableManagerUnitTest,
+       ManifestRequiresPurposeAnyOrMaskableWhenPreferringMaskable) {
+  blink::Manifest manifest = GetValidManifest();
+
+  EXPECT_TRUE(IsManifestValid(manifest, true, true));
+  EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
+
+  manifest.icons[0].purpose[0] = IconPurpose::MASKABLE;
+  EXPECT_TRUE(IsManifestValid(manifest, true, true));
+  EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
+
+  // The icon MUST have IconPurpose::ANY or IconPurpose::Maskable.
+  manifest.icons[0].purpose[0] = IconPurpose::BADGE;
+  EXPECT_FALSE(IsManifestValid(manifest, true, true));
+  EXPECT_EQ(MANIFEST_MISSING_SUITABLE_ICON, GetErrorCode());
+
+  // If one of the icon purposes match the requirement, it should be accepted.
+  manifest.icons[0].purpose.push_back(IconPurpose::ANY);
+  EXPECT_TRUE(IsManifestValid(manifest, true, true));
+  EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
+
+  manifest.icons[0].purpose[1] = IconPurpose::MASKABLE;
+  EXPECT_TRUE(IsManifestValid(manifest, true, true));
+  EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
+}
+
 TEST_F(InstallableManagerUnitTest, ManifestRequiresMinimalSize) {
   blink::Manifest manifest = GetValidManifest();
 
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tips_config.cc b/chrome/browser/lookalikes/safety_tips/safety_tips_config.cc
new file mode 100644
index 0000000..d673226
--- /dev/null
+++ b/chrome/browser/lookalikes/safety_tips/safety_tips_config.cc
@@ -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.
+
+#include "chrome/browser/lookalikes/safety_tips/safety_tips_config.h"
+
+#include "base/no_destructor.h"
+
+namespace {
+
+class SafetyTipsConfigSingleton {
+ public:
+  void SetProto(
+      std::unique_ptr<chrome_browser_safety_tips::SafetyTipsConfig> proto) {
+    proto_ = std::move(proto);
+  }
+
+  static SafetyTipsConfigSingleton& GetInstance() {
+    static base::NoDestructor<SafetyTipsConfigSingleton> instance;
+    return *instance;
+  }
+
+ private:
+  std::unique_ptr<chrome_browser_safety_tips::SafetyTipsConfig> proto_;
+};
+
+}  // namespace
+
+namespace safety_tips {
+
+// static
+void SetProto(
+    std::unique_ptr<chrome_browser_safety_tips::SafetyTipsConfig> proto) {
+  SafetyTipsConfigSingleton::GetInstance().SetProto(std::move(proto));
+}
+
+}  // namespace safety_tips
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tips_config.h b/chrome/browser/lookalikes/safety_tips/safety_tips_config.h
new file mode 100644
index 0000000..f084d2f
--- /dev/null
+++ b/chrome/browser/lookalikes/safety_tips/safety_tips_config.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 CHROME_BROWSER_LOOKALIKES_SAFETY_TIPS_SAFETY_TIPS_CONFIG_H_
+#define CHROME_BROWSER_LOOKALIKES_SAFETY_TIPS_SAFETY_TIPS_CONFIG_H_
+
+#include <memory>
+
+#include "chrome/browser/lookalikes/safety_tips/safety_tips.pb.h"
+
+namespace safety_tips {
+
+// Sets the global configuration for Safety Tips retrieved from the component
+// updater. The configuration proto contains the list of URLs that can trigger
+// a safety tip.
+void SetProto(
+    std::unique_ptr<chrome_browser_safety_tips::SafetyTipsConfig> proto);
+
+}  // namespace safety_tips
+
+#endif  // CHROME_BROWSER_LOOKALIKES_SAFETY_TIPS_SAFETY_TIPS_CONFIG_H_
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
index 985fd3e2..ba76413 100644
--- a/chrome/browser/mac/keystone_glue.mm
+++ b/chrome/browser/mac/keystone_glue.mm
@@ -24,6 +24,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/version.h"
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #import "chrome/browser/mac/keystone_registration.h"
 #include "chrome/common/channel_info.h"
@@ -44,7 +45,7 @@
 #if defined(GOOGLE_CHROME_BUILD)
 #define kStableBrandFileName @"Google Chrome Brand.plist"
 #define kCanaryBrandFileName @"Google Chrome Canary Brand.plist"
-#elif defined(CHROMIUM_BUILD)
+#elif BUILDFLAG(CHROMIUM_BRANDING)
 #define kStableBrandFileName @"Chromium Brand.plist"
 #define kCanaryBrandFileName @"Chromium Canary Brand.plist"
 #else
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index 1689408..29707ed 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -155,6 +155,7 @@
         "providers/openscreen/platform/task_runner.cc",
         "providers/openscreen/platform/task_runner.h",
         "providers/openscreen/platform/time.cc",
+        "providers/openscreen/platform/trace_logging_platform.cc",
       ]
 
       configs +=
diff --git a/chrome/browser/media/router/providers/openscreen/platform/trace_logging_platform.cc b/chrome/browser/media/router/providers/openscreen/platform/trace_logging_platform.cc
new file mode 100644
index 0000000..0e49107
--- /dev/null
+++ b/chrome/browser/media/router/providers/openscreen/platform/trace_logging_platform.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/openscreen/src/platform/api/trace_logging_platform.h"
+
+namespace openscreen {
+namespace platform {
+
+bool IsTraceLoggingEnabled(TraceCategory::Value category) {
+  return false;
+}
+
+}  // namespace platform
+}  // namespace openscreen
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 cb17b34..ced82c9 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -225,6 +225,14 @@
   UMA_HISTOGRAM_ENUMERATION("Windows.Kernel32Version",
                             os_info.Kernel32Version(),
                             base::win::Version::WIN_LAST);
+  int patch = os_info.version_number().patch;
+  int build = os_info.version_number().build;
+  int patch_level = 0;
+
+  if (patch < 65536 && build < 65536)
+    patch_level = MAKELONG(patch, build);
+  DCHECK(patch_level) << "Windows version too high!";
+  base::UmaHistogramSparse("Windows.PatchLevel", patch_level);
 
   UMA_HISTOGRAM_BOOLEAN("Windows.HasHighResolutionTimeTicks",
                         base::TimeTicks::IsHighResolution());
diff --git a/chrome/browser/notifications/scheduler/test/BUILD.gn b/chrome/browser/notifications/scheduler/test/BUILD.gn
index a1e45e3..ec4d221 100644
--- a/chrome/browser/notifications/scheduler/test/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/test/BUILD.gn
@@ -10,10 +10,16 @@
   sources = [
     "fake_clock.cc",
     "fake_clock.h",
+    "mock_display_agent.cc",
+    "mock_display_agent.h",
+    "mock_display_decider.cc",
+    "mock_display_decider.h",
     "mock_impression_history_tracker.cc",
     "mock_impression_history_tracker.h",
     "mock_notification_background_task_scheduler.cc",
     "mock_notification_background_task_scheduler.h",
+    "mock_scheduled_notification_manager.cc",
+    "mock_scheduled_notification_manager.h",
     "test_utils.cc",
     "test_utils.h",
   ]
diff --git a/chrome/browser/notifications/scheduler/test/mock_display_agent.cc b/chrome/browser/notifications/scheduler/test/mock_display_agent.cc
new file mode 100644
index 0000000..e4e8881
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_display_agent.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/test/mock_display_agent.h"
+
+namespace notifications {
+namespace test {
+
+MockDisplayAgent::MockDisplayAgent() = default;
+
+MockDisplayAgent::~MockDisplayAgent() = default;
+
+}  // namespace test
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/mock_display_agent.h b/chrome/browser/notifications/scheduler/test/mock_display_agent.h
new file mode 100644
index 0000000..99fa066e0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_display_agent.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_AGENT_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_AGENT_H_
+
+#include "chrome/browser/notifications/scheduler/public/display_agent.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace notifications {
+namespace test {
+
+class MockDisplayAgent : public DisplayAgent {
+ public:
+  MockDisplayAgent();
+  ~MockDisplayAgent() override;
+  MOCK_METHOD2(ShowNotification,
+               void(std::unique_ptr<NotificationData>,
+                    std::unique_ptr<SystemData>));
+};
+
+}  // namespace test
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_AGENT_H_
diff --git a/chrome/browser/notifications/scheduler/test/mock_display_decider.cc b/chrome/browser/notifications/scheduler/test/mock_display_decider.cc
new file mode 100644
index 0000000..4db7f16a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_display_decider.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/test/mock_display_decider.h"
+
+namespace notifications {
+namespace test {
+
+MockDisplayDecider::MockDisplayDecider() = default;
+
+MockDisplayDecider::~MockDisplayDecider() = default;
+
+}  // namespace test
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/mock_display_decider.h b/chrome/browser/notifications/scheduler/test/mock_display_decider.h
new file mode 100644
index 0000000..385811d0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_display_decider.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 CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_DECIDER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_DECIDER_H_
+
+#include "chrome/browser/notifications/scheduler/internal/display_decider.h"
+#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace notifications {
+namespace test {
+
+class MockDisplayDecider : public DisplayDecider {
+ public:
+  MockDisplayDecider();
+  ~MockDisplayDecider() override;
+  MOCK_METHOD7(FindNotificationsToShow,
+               void(const SchedulerConfig*,
+                    std::vector<SchedulerClientType>,
+                    std::unique_ptr<DistributionPolicy>,
+                    SchedulerTaskTime,
+                    Notifications,
+                    ClientStates,
+                    Results*));
+};
+
+}  // namespace test
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_DISPLAY_DECIDER_H_
diff --git a/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.cc
new file mode 100644
index 0000000..a838f54a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.h"
+
+namespace notifications {
+namespace test {
+
+MockScheduledNotificationManager::MockScheduledNotificationManager() = default;
+
+MockScheduledNotificationManager::~MockScheduledNotificationManager() = default;
+
+}  // namespace test
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.h
new file mode 100644
index 0000000..2737735
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.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_NOTIFICATIONS_SCHEDULER_TEST_MOCK_SCHEDULED_NOTIFICATION_MANAGER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_SCHEDULED_NOTIFICATION_MANAGER_H_
+
+#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace notifications {
+namespace test {
+
+class MockScheduledNotificationManager : public ScheduledNotificationManager {
+ public:
+  MockScheduledNotificationManager();
+  ~MockScheduledNotificationManager() override;
+
+  MOCK_METHOD2(Init, void(Delegate*, base::OnceCallback<void(bool)>));
+  MOCK_METHOD1(ScheduleNotification,
+               void(std::unique_ptr<notifications::NotificationParams>));
+  MOCK_METHOD1(DisplayNotification, void(const std::string&));
+  MOCK_CONST_METHOD1(GetAllNotifications, void(Notifications*));
+  MOCK_CONST_METHOD2(GetNotifications,
+                     void(SchedulerClientType,
+                          std::vector<const NotificationEntry*>*));
+  MOCK_METHOD1(DeleteNotifications, void(SchedulerClientType));
+};
+
+}  // namespace test
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_TEST_MOCK_SCHEDULED_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
index 6c16d93..90fcbacf 100644
--- a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
+++ b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
@@ -18,10 +18,7 @@
 #include "chrome/browser/ntp_tiles/chrome_popular_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/suggestions/suggestions_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_service.h"
-#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_service_observer.h"
-#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "chrome/common/buildflags.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/ntp_tiles/icon_cacher_impl.h"
@@ -33,8 +30,16 @@
 #include "chrome/browser/android/explore_sites/most_visited_client.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_service_observer.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#endif
+
 using suggestions::SuggestionsServiceFactory;
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 namespace {
 
 class SupervisorBridge : public ntp_tiles::MostVisitedSitesSupervisor,
@@ -110,6 +115,7 @@
 }
 
 }  // namespace
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 // static
 std::unique_ptr<ntp_tiles::MostVisitedSites>
@@ -140,7 +146,12 @@
               std::make_unique<ImageDecoderImpl>(),
               content::BrowserContext::GetDefaultStoragePartition(profile)
                   ->GetURLLoaderFactoryForBrowserProcess())),
-      std::make_unique<SupervisorBridge>(profile));
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+      std::make_unique<SupervisorBridge>(profile)
+#else
+      nullptr
+#endif
+  );
 #if defined(OS_ANDROID)
   most_visited_sites->SetExploreSitesClient(
       explore_sites::MostVisitedClient::Create());
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 e4125d7..7ddcb82 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -41,6 +41,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/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -97,6 +98,14 @@
 
 const int64_t kDownloadId = 42LL;
 
+// Returns the thread the navigation URL loader will run on. This determines
+// where the OfflinePageURLLoader should be created.
+content::BrowserThread::ID GetNavigationLoaderThreadID() {
+  return base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)
+             ? content::BrowserThread::UI
+             : content::BrowserThread::IO;
+}
+
 struct ResponseInfo {
   explicit ResponseInfo(int request_status) : request_status(request_status) {
     DCHECK_NE(net::OK, request_status);
@@ -262,15 +271,16 @@
 
  private:
   void OnHandleReady(MojoResult result, const mojo::HandleSignalsState& state);
-  void InterceptRequestOnIO(const GURL& url,
-                            const std::string& method,
-                            const net::HttpRequestHeaders& extra_headers,
-                            bool is_main_frame);
+  void InterceptRequestOnLoaderThread(
+      const GURL& url,
+      const std::string& method,
+      const net::HttpRequestHeaders& extra_headers,
+      bool is_main_frame);
   void MaybeStartLoader(
       const network::ResourceRequest& request,
       content::URLLoaderRequestInterceptor::RequestHandler request_handler);
   void ReadBody();
-  void ReadCompletedOnIO(const ResponseInfo& response);
+  void ReadCompletedOnLoaderThread(const ResponseInfo& response);
 
   OfflinePageRequestHandlerTest* test_;
   std::unique_ptr<ChromeNavigationUIData> navigation_ui_data_;
@@ -941,7 +951,8 @@
 
 void OfflinePageURLLoaderBuilder::OnReceiveRedirect(
     const GURL& redirected_url) {
-  InterceptRequestOnIO(redirected_url, "GET", net::HttpRequestHeaders(), true);
+  InterceptRequestOnLoaderThread(redirected_url, "GET",
+                                 net::HttpRequestHeaders(), true);
 }
 
 void OfflinePageURLLoaderBuilder::OnReceiveResponse(
@@ -958,19 +969,19 @@
     mime_type_.clear();
     body_.clear();
   }
-  ReadCompletedOnIO(
+  ReadCompletedOnLoaderThread(
       ResponseInfo(client_->completion_status().error_code, mime_type_, body_));
   // Clear intermediate data in preparation for next potential page loading.
   mime_type_.clear();
   body_.clear();
 }
 
-void OfflinePageURLLoaderBuilder::InterceptRequestOnIO(
+void OfflinePageURLLoaderBuilder::InterceptRequestOnLoaderThread(
     const GURL& url,
     const std::string& method,
     const net::HttpRequestHeaders& extra_headers,
     bool is_main_frame) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(GetNavigationLoaderThreadID());
 
   client_ = std::make_unique<TestURLLoaderClient>(this);
 
@@ -1000,21 +1011,25 @@
     bool is_main_frame) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&OfflinePageURLLoaderBuilder::InterceptRequestOnIO,
-                     base::Unretained(this), url, method, extra_headers,
-                     is_main_frame));
+  if (GetNavigationLoaderThreadID() == content::BrowserThread::UI) {
+    InterceptRequestOnLoaderThread(url, method, extra_headers, is_main_frame);
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::IO},
+        base::BindOnce(
+            &OfflinePageURLLoaderBuilder::InterceptRequestOnLoaderThread,
+            base::Unretained(this), url, method, extra_headers, is_main_frame));
+  }
   base::RunLoop().Run();
 }
 
 void OfflinePageURLLoaderBuilder::MaybeStartLoader(
     const network::ResourceRequest& request,
     content::URLLoaderRequestInterceptor::RequestHandler request_handler) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(GetNavigationLoaderThreadID());
 
   if (!request_handler) {
-    ReadCompletedOnIO(ResponseInfo(net::ERR_FAILED));
+    ReadCompletedOnLoaderThread(ResponseInfo(net::ERR_FAILED));
     return;
   }
 
@@ -1049,7 +1064,7 @@
 
     // The pipe was closed.
     if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
-      ReadCompletedOnIO(ResponseInfo(net::ERR_FAILED));
+      ReadCompletedOnLoaderThread(ResponseInfo(net::ERR_FAILED));
       return;
     }
 
@@ -1064,15 +1079,15 @@
     MojoResult result,
     const mojo::HandleSignalsState& state) {
   if (result != MOJO_RESULT_OK) {
-    ReadCompletedOnIO(ResponseInfo(net::ERR_FAILED));
+    ReadCompletedOnLoaderThread(ResponseInfo(net::ERR_FAILED));
     return;
   }
   ReadBody();
 }
 
-void OfflinePageURLLoaderBuilder::ReadCompletedOnIO(
+void OfflinePageURLLoaderBuilder::ReadCompletedOnLoaderThread(
     const ResponseInfo& response) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(GetNavigationLoaderThreadID());
 
   handle_watcher_.reset();
   client_.reset();
@@ -1085,11 +1100,15 @@
   if (offline_page_data && offline_page_data->is_offline_page())
     is_offline_page_set_in_navigation_data = true;
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&OfflinePageRequestHandlerTest::ReadCompleted,
-                     base::Unretained(test()), response,
-                     is_offline_page_set_in_navigation_data));
+  if (GetNavigationLoaderThreadID() == content::BrowserThread::UI) {
+    test()->ReadCompleted(response, is_offline_page_set_in_navigation_data);
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(&OfflinePageRequestHandlerTest::ReadCompleted,
+                       base::Unretained(test()), response,
+                       is_offline_page_set_in_navigation_data));
+  }
 }
 
 TEST_F(OfflinePageRequestHandlerTest, FailedToCreateRequestJob) {
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
index 050d1344..2a2473b5 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -86,7 +86,6 @@
           WebFeature::kSignedExchangeInnerResponseInMainFrame,
           WebFeature::kSignedExchangeInnerResponseInSubFrame,
           WebFeature::kWebShareShare,
-          WebFeature::kDownloadInAdFrameWithUserGesture,
           WebFeature::kDownloadInAdFrameWithoutUserGesture,
           WebFeature::kOpenWebDatabase,
           WebFeature::kV8MediaCapabilities_DecodingInfo_Method,
@@ -109,6 +108,11 @@
           WebFeature::kAccelerometerConstructor,
           WebFeature::kGyroscopeConstructor,
           WebFeature::kServiceWorkerInterceptedRequestFromOriginDirtyStyleSheet,
+          WebFeature::kDownloadPrePolicyCheck,
+          WebFeature::kDownloadPostPolicyCheck,
+          WebFeature::kDownloadInAdFrame,
+          WebFeature::kDownloadInSandbox,
+          WebFeature::kDownloadWithoutUserGesture,
       }));
   return *opt_in_features;
 }
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc
deleted file mode 100644
index 6ade8c8e..0000000
--- a/chrome/browser/password_manager/native_backend_gnome_x.cc
+++ /dev/null
@@ -1,759 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/password_manager/native_backend_gnome_x.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task/post_task.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/password_manager_util_linux.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/password_manager_util.h"
-#include "components/password_manager/core/browser/psl_matching_helper.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-using autofill::PasswordForm;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-using password_manager::MatchResult;
-using password_manager::PasswordStore;
-
-namespace {
-
-// Convert the attributes of a given keyring entry into a new PasswordForm.
-// Note: does *not* get the actual password, as that is not a key attribute!
-// Returns NULL if the attributes are for the wrong application.
-std::unique_ptr<PasswordForm> FormFromAttributes(
-    GnomeKeyringAttributeList* attrs) {
-  // Read the string and int attributes into the appropriate map.
-  std::map<std::string, std::string> string_attr_map;
-  std::map<std::string, uint32_t> uint_attr_map;
-  for (guint i = 0; i < attrs->len; ++i) {
-    GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
-    if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
-      string_attr_map[attr.name] = attr.value.string;
-    else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
-      uint_attr_map[attr.name] = attr.value.integer;
-  }
-  // Check to make sure this is a password we care about.
-  const std::string& app_value = string_attr_map["application"];
-  if (!base::StringPiece(app_value).starts_with(kLibsecretAndGnomeAppString))
-    return std::unique_ptr<PasswordForm>();
-
-  std::unique_ptr<PasswordForm> form(new PasswordForm());
-  form->origin = GURL(string_attr_map["origin_url"]);
-  form->action = GURL(string_attr_map["action_url"]);
-  form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
-  form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
-  form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
-  form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
-  form->signon_realm = string_attr_map["signon_realm"];
-  form->preferred = uint_attr_map["preferred"];
-  int64_t date_created = 0;
-  bool date_ok = base::StringToInt64(string_attr_map["date_created"],
-                                     &date_created);
-  DCHECK(date_ok);
-  // In the past |date_created| was stored as time_t. Currently is stored as
-  // base::Time's internal value. We need to distinguish, which format the
-  // number in |date_created| was stored in. We use the fact that
-  // kMaxPossibleTimeTValue interpreted as the internal value corresponds to an
-  // unlikely date back in 17th century, and anything above
-  // kMaxPossibleTimeTValue clearly must be in the internal value format.
-  form->date_created = date_created < kMaxPossibleTimeTValue
-                           ? base::Time::FromTimeT(date_created)
-                           : base::Time::FromInternalValue(date_created);
-  form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
-  form->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]);
-  form->times_used = uint_attr_map["times_used"];
-  form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
-  int64_t date_synced = 0;
-  base::StringToInt64(string_attr_map["date_synced"], &date_synced);
-  form->date_synced = base::Time::FromInternalValue(date_synced);
-  form->display_name = UTF8ToUTF16(string_attr_map["display_name"]);
-  form->icon_url = GURL(string_attr_map["avatar_url"]);
-  form->federation_origin =
-      url::Origin::Create(GURL(string_attr_map["federation_url"]));
-  form->skip_zero_click = uint_attr_map.count("should_skip_zero_click")
-                              ? uint_attr_map["should_skip_zero_click"]
-                              : true;
-  form->generation_upload_status =
-      static_cast<PasswordForm::GenerationUploadStatus>(
-          uint_attr_map["generation_upload_status"]);
-  if (!string_attr_map["form_data"].empty()) {
-    bool success = DeserializeFormDataFromBase64String(
-        string_attr_map["form_data"], &form->form_data);
-    password_manager::metrics_util::FormDeserializationStatus status =
-        success ? password_manager::metrics_util::GNOME_SUCCESS
-                : password_manager::metrics_util::GNOME_FAILURE;
-    LogFormDataDeserializationStatus(status);
-  }
-  return form;
-}
-
-// Converts native credentials in |found| to PasswordForms. If not NULL,
-// |lookup_form| is used to filter out results -- only credentials with signon
-// realms passing the PSL matching against |lookup_form->signon_realm| will be
-// kept. PSL matched results get their signon_realm, origin, and action
-// rewritten to those of |lookup_form_|, with the original signon_realm saved
-// into the result's original_signon_realm data member.
-std::vector<std::unique_ptr<PasswordForm>> ConvertFormList(
-    GList* found,
-    const PasswordStore::FormDigest* lookup_form) {
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  password_manager::PSLDomainMatchMetric psl_domain_match_metric =
-      password_manager::PSL_DOMAIN_MATCH_NONE;
-  for (GList* element = g_list_first(found); element;
-       element = g_list_next(element)) {
-    GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
-    GnomeKeyringAttributeList* attrs = data->attributes;
-
-    std::unique_ptr<PasswordForm> form(FormFromAttributes(attrs));
-    if (!form) {
-      LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
-      continue;
-    }
-
-    if (lookup_form) {
-      switch (GetMatchResult(*form, *lookup_form)) {
-        case MatchResult::NO_MATCH:
-          continue;
-        case MatchResult::EXACT_MATCH:
-          break;
-        case MatchResult::PSL_MATCH:
-          psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
-          form->is_public_suffix_match = true;
-          break;
-        case MatchResult::FEDERATED_MATCH:
-          break;
-        case MatchResult::FEDERATED_PSL_MATCH:
-          psl_domain_match_metric =
-              password_manager::PSL_DOMAIN_MATCH_FOUND_FEDERATED;
-          form->is_public_suffix_match = true;
-          break;
-      }
-    }
-
-    if (data->secret) {
-      form->password_value = UTF8ToUTF16(data->secret);
-    } else {
-      LOG(WARNING) << "Unable to access password from list element!";
-    }
-    forms.push_back(std::move(form));
-  }
-  if (lookup_form) {
-    const bool allow_psl_match = password_manager::ShouldPSLDomainMatchingApply(
-        password_manager::GetRegistryControlledDomain(
-            GURL(lookup_form->signon_realm)));
-    UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
-                              allow_psl_match
-                                  ? psl_domain_match_metric
-                                  : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
-                              password_manager::PSL_DOMAIN_MATCH_COUNT);
-  }
-  return forms;
-}
-
-// Schema is analogous to the fields in PasswordForm.
-const GnomeKeyringPasswordSchema kGnomeSchema = {
-    GNOME_KEYRING_ITEM_GENERIC_SECRET,
-    {{"origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"display_name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"avatar_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {"should_skip_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"generation_upload_status", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32},
-     {"form_data", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     // This field is always "chrome" so that we can search for it.
-     {"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
-     {nullptr}}};
-
-// Sadly, PasswordStore goes to great lengths to switch from the originally
-// calling thread to the DB thread, and to provide an asynchronous API to
-// callers while using a synchronous (virtual) API provided by subclasses like
-// PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
-// thread, which is the UI thread to us. So we end up having to switch threads
-// again, possibly back to the very same thread (in case the UI thread is the
-// caller, e.g. in the password management UI), and *block* the DB thread
-// waiting for a response from the UI thread to provide the synchronous API
-// PasswordStore expects of us. (It will then in turn switch back to the
-// original caller to send the asynchronous reply to the original request.)
-
-// This class represents a call to a GNOME Keyring method. A RunnableMethod
-// should be posted to the UI thread to call one of its action methods, and then
-// a WaitResult() method should be called to wait for the result. Each instance
-// supports only one outstanding method at a time, though multiple instances may
-// be used in parallel.
-class GKRMethod : public GnomeKeyringLoader {
- public:
-  GKRMethod(scoped_refptr<base::SequencedTaskRunner> main_task_runner,
-            scoped_refptr<base::SequencedTaskRunner> background_task_runner)
-      : main_task_runner_(std::move(main_task_runner)),
-        background_task_runner_(std::move(background_task_runner)),
-        event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-               base::WaitableEvent::InitialState::NOT_SIGNALED),
-        result_(GNOME_KEYRING_RESULT_CANCELLED) {}
-
-  // Action methods. These call gnome_keyring_* functions. Call from UI thread.
-  // See GetProfileSpecificAppString() for more information on the app string.
-  void AddLogin(const PasswordForm& form, const char* app_string);
-  void LoginSearch(const PasswordForm& form, const char* app_string);
-  void RemoveLogin(const PasswordForm& form, const char* app_string);
-  void GetLogins(const PasswordStore::FormDigest& form, const char* app_string);
-  void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
-  void GetAllLogins(const char* app_string);
-
-  // Use after AddLogin, RemoveLogin.
-  GnomeKeyringResult WaitResult();
-
-  // Use after LoginSearch, GetLogins, GetLoginsList, GetAllLogins. Replaces the
-  // content of |forms| with found logins.
-  GnomeKeyringResult WaitResult(
-      std::vector<std::unique_ptr<PasswordForm>>* forms);
-
- private:
-  struct GnomeKeyringAttributeListFreeDeleter {
-    inline void operator()(void* list) const {
-      gnome_keyring_attribute_list_free_ptr(
-          static_cast<GnomeKeyringAttributeList*>(list));
-    }
-  };
-
-  typedef std::unique_ptr<GnomeKeyringAttributeList,
-                          GnomeKeyringAttributeListFreeDeleter>
-      ScopedAttributeList;
-
-  // Helper methods to abbreviate Gnome Keyring long API names.
-  static void AppendString(ScopedAttributeList* list,
-                           const char* name,
-                           const char* value);
-  static void AppendString(ScopedAttributeList* list,
-                           const char* name,
-                           const std::string& value);
-  static void AppendUint32(ScopedAttributeList* list,
-                           const char* name,
-                           guint32 value);
-
-  // All these callbacks are called on UI thread.
-  static void OnOperationDone(GnomeKeyringResult result, gpointer data);
-
-  // This is marked as static, but acts on the GKRMethod instance that |data|
-  // points to. Saves |result| to |result_|. If the result is OK, overwrites
-  // |forms_| with the found credentials. Clears |forms_| otherwise.
-  static void OnOperationGetList(GnomeKeyringResult result, GList* list,
-                                 gpointer data);
-
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-  base::WaitableEvent event_;
-  GnomeKeyringResult result_;
-  std::vector<std::unique_ptr<PasswordForm>> forms_;
-  // If the credential search is specified by a single form and needs to use
-  // PSL matching, then the specifying form is stored in |lookup_form_|. If
-  // PSL matching is used to find a result, then the results signon realm and
-  // origin are stored are replaced by those of |lookup_form_|. Additionally,
-  // |lookup_form_->signon_realm| is also used to narrow down the found logins
-  // to those which indeed PSL-match the look-up. And finally, |lookup_form_|
-  // set to NULL means that PSL matching is not required.
-  std::unique_ptr<const PasswordStore::FormDigest> lookup_form_;
-};
-
-void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  int64_t date_created = form.date_created.ToInternalValue();
-  // If we are asked to save a password with 0 date, use the current time.
-  // We don't want to actually save passwords as though on January 1, 1601.
-  if (!date_created)
-    date_created = base::Time::Now().ToInternalValue();
-  int64_t date_synced = form.date_synced.ToInternalValue();
-  std::string form_data;
-  SerializeFormDataToBase64String(form.form_data, &form_data);
-  // clang-format off
-  gnome_keyring_store_password_ptr(
-      &kGnomeSchema,
-      nullptr,                     // Default keyring.
-      form.origin.spec().c_str(),  // Display name.
-      UTF16ToUTF8(form.password_value).c_str(), OnOperationDone,
-      this,     // data
-      nullptr,  // destroy_data
-      "origin_url", form.origin.spec().c_str(),
-      "action_url", form.action.spec().c_str(),
-      "username_element", UTF16ToUTF8(form.username_element).c_str(),
-      "username_value", UTF16ToUTF8(form.username_value).c_str(),
-      "password_element", UTF16ToUTF8(form.password_element).c_str(),
-      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
-      "signon_realm", form.signon_realm.c_str(),
-      "preferred", form.preferred,
-      "date_created", base::NumberToString(date_created).c_str(),
-      "blacklisted_by_user", form.blacklisted_by_user,
-      "type", form.type,
-      "times_used", form.times_used,
-      "scheme", form.scheme,
-      "date_synced", base::NumberToString(date_synced).c_str(),
-      "display_name", UTF16ToUTF8(form.display_name).c_str(),
-      "avatar_url", form.icon_url.spec().c_str(),
-      // We serialize unique origins as "", in order to make other systems that
-      // read from the login database happy. https://crbug.com/591310
-      "federation_url", form.federation_origin.opaque()
-          ? ""
-          : form.federation_origin.Serialize().c_str(),
-      "should_skip_zero_click", form.skip_zero_click,
-      "generation_upload_status", form.generation_upload_status,
-      "form_data", form_data.c_str(),
-      "application", app_string,
-      nullptr);
-  // clang-format on
-}
-
-void GKRMethod::LoginSearch(const PasswordForm& form,
-                               const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  lookup_form_.reset(nullptr);
-  // Search GNOME Keyring for matching passwords to update.
-  ScopedAttributeList attrs(gnome_keyring_attribute_list_new_ptr());
-  AppendString(&attrs, "origin_url", form.origin.spec());
-  AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
-  AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
-  AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
-  AppendString(&attrs, "signon_realm", form.signon_realm);
-  AppendString(&attrs, "application", app_string);
-  gnome_keyring_find_items_ptr(GNOME_KEYRING_ITEM_GENERIC_SECRET, attrs.get(),
-                               OnOperationGetList,
-                               /*data=*/this,
-                               /*destroy_data=*/nullptr);
-}
-
-void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  // We find forms using the same fields as LoginDatabase::RemoveLogin().
-  gnome_keyring_delete_password_ptr(
-      &kGnomeSchema,
-      OnOperationDone,
-      this,     // data
-      nullptr,  // destroy_data
-      "origin_url", form.origin.spec().c_str(),
-      "username_element", UTF16ToUTF8(form.username_element).c_str(),
-      "username_value", UTF16ToUTF8(form.username_value).c_str(),
-      "password_element", UTF16ToUTF8(form.password_element).c_str(),
-      "signon_realm", form.signon_realm.c_str(),
-      "application", app_string,
-      nullptr);
-}
-
-void GKRMethod::GetLogins(const PasswordStore::FormDigest& form,
-                          const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  lookup_form_.reset(new PasswordStore::FormDigest(form));
-  // Search GNOME Keyring for matching passwords.
-  ScopedAttributeList attrs(gnome_keyring_attribute_list_new_ptr());
-  if (!password_manager::ShouldPSLDomainMatchingApply(
-          password_manager::GetRegistryControlledDomain(
-              GURL(form.signon_realm))) &&
-      form.scheme != PasswordForm::Scheme::kHtml) {
-    // Don't retrieve the PSL matched and federated credentials.
-    AppendString(&attrs, "signon_realm", form.signon_realm);
-  }
-  AppendString(&attrs, "application", app_string);
-  gnome_keyring_find_items_ptr(GNOME_KEYRING_ITEM_GENERIC_SECRET, attrs.get(),
-                               OnOperationGetList,
-                               /*data=*/this,
-                               /*destroy_data=*/nullptr);
-}
-
-void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
-                              const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  lookup_form_.reset(nullptr);
-  // Search GNOME Keyring for matching passwords.
-  ScopedAttributeList attrs(gnome_keyring_attribute_list_new_ptr());
-  AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
-  AppendString(&attrs, "application", app_string);
-  gnome_keyring_find_items_ptr(GNOME_KEYRING_ITEM_GENERIC_SECRET, attrs.get(),
-                               OnOperationGetList,
-                               /*data=*/this,
-                               /*destroy_data=*/nullptr);
-}
-
-void GKRMethod::GetAllLogins(const char* app_string) {
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
-  lookup_form_.reset(nullptr);
-  // We need to search for something, otherwise we get no results - so
-  // we search for the fixed application string.
-  ScopedAttributeList attrs(gnome_keyring_attribute_list_new_ptr());
-  AppendString(&attrs, "application", app_string);
-  gnome_keyring_find_items_ptr(GNOME_KEYRING_ITEM_GENERIC_SECRET, attrs.get(),
-                               OnOperationGetList,
-                               /*data=*/this,
-                               /*destroy_data=*/nullptr);
-}
-
-GnomeKeyringResult GKRMethod::WaitResult() {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  event_.Wait();
-  return result_;
-}
-
-GnomeKeyringResult GKRMethod::WaitResult(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  event_.Wait();
-  *forms = std::move(forms_);
-  return result_;
-}
-
-// static
-void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
-                             const char* name,
-                             const char* value) {
-  gnome_keyring_attribute_list_append_string_ptr(list->get(), name, value);
-}
-
-// static
-void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
-                             const char* name,
-                             const std::string& value) {
-  AppendString(list, name, value.c_str());
-}
-
-// static
-void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
-                             const char* name,
-                             guint32 value) {
-  gnome_keyring_attribute_list_append_uint32_ptr(list->get(), name, value);
-}
-
-// static
-void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
-  GKRMethod* method = static_cast<GKRMethod*>(data);
-  method->result_ = result;
-  method->event_.Signal();
-}
-
-// static
-void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
-                                   gpointer data) {
-  GKRMethod* method = static_cast<GKRMethod*>(data);
-  method->result_ = result;
-  // |list| will be freed after this callback returns, so convert it now.
-  if (result == GNOME_KEYRING_RESULT_OK)
-    method->forms_ = ConvertFormList(list, method->lookup_form_.get());
-  else
-    method->forms_.clear();
-  method->lookup_form_.reset();
-  method->event_.Signal();
-}
-
-}  // namespace
-
-// This uses BrowserThread::UI for the main thread, because the Gnome Keyring
-// library needs to run there (see the comment at the top of this file).
-// This also uses USER_VISIBLE priority for the background task runner, because
-// the passwords obtained through tasks on the background runner influence what
-// the user sees. The need for WithBaseSyncPrimitives is caused by calls to
-// Wait() to synchronise the background task with its response from
-// GnomeKeyring which needs to be produced on the main task runner for library
-// limitations. While unfortunate, https://crbug.com/739897 provides the
-// discussion which resulted in accepting this until the whole backend is
-// deprecated.
-NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
-    : app_string_(GetProfileSpecificAppString(id)),
-      main_task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::UI})),
-      background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::WithBaseSyncPrimitives(),
-           base::TaskPriority::USER_VISIBLE})) {}
-
-NativeBackendGnome::~NativeBackendGnome() {
-}
-
-bool NativeBackendGnome::Init() {
-  return LoadGnomeKeyring() && gnome_keyring_is_available_ptr();
-}
-
-bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&GKRMethod::AddLogin, base::Unretained(&method),
-                                form, app_string_.c_str()));
-  GnomeKeyringResult result = method.WaitResult();
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring save failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-  return true;
-}
-
-password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin(
-    const PasswordForm& form) {
-  // Based on LoginDatabase::AddLogin(), we search for an existing match based
-  // on origin_url, username_element, username_value, password_element, submit
-  // element, and signon_realm first, remove that, and then add the new entry.
-  // We'd add the new one first, and then delete the original, but then the
-  // delete might actually delete the newly-added entry!
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::LoginSearch, base::Unretained(&method), form,
-                     app_string_.c_str()));
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  GnomeKeyringResult result = method.WaitResult(&forms);
-  if (result != GNOME_KEYRING_RESULT_OK &&
-      result != GNOME_KEYRING_RESULT_NO_MATCH) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return password_manager::PasswordStoreChangeList();
-  }
-  password_manager::PasswordStoreChangeList changes;
-  if (forms.size() > 0) {
-    password_manager::PasswordStoreChangeList temp_changes;
-    if (forms.size() > 1) {
-      LOG(WARNING) << "Adding login when there are " << forms.size()
-                   << " matching logins already!";
-    }
-    for (const auto& old_form : forms) {
-      if (!RemoveLogin(*old_form, &temp_changes))
-        return changes;
-    }
-    changes.push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::REMOVE, *forms[0]));
-  }
-  if (RawAddLogin(form)) {
-    changes.push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::ADD, form));
-  }
-  return changes;
-}
-
-bool NativeBackendGnome::UpdateLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
-  // origin_url, username_element, username_value, password_element, and
-  // signon_realm. We then compare the result to the updated form. If they
-  // differ in any of the mutable fields, then we remove the original, and
-  // then add the new entry. We'd add the new one first, and then delete the
-  // original, but then the delete might actually delete the newly-added entry!
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(changes);
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::LoginSearch, base::Unretained(&method), form,
-                     app_string_.c_str()));
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  GnomeKeyringResult result = method.WaitResult(&forms);
-  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-    return true;
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-  if (forms.size() == 1 && *forms.front() == form)
-    return true;
-
-  password_manager::PasswordStoreChangeList temp_changes;
-  for (const auto& keychain_form : forms) {
-    // Remove all the obsolete forms. Note that RemoveLogin can remove any form
-    // matching the unique key. Thus, it's important to call it the right number
-    // of times.
-    if (!RemoveLogin(*keychain_form, &temp_changes))
-      return false;
-  }
-
-  if (RawAddLogin(form)) {
-    password_manager::PasswordStoreChange change(
-        password_manager::PasswordStoreChange::UPDATE, form);
-    changes->push_back(change);
-    return true;
-  }
-  return false;
-}
-
-bool NativeBackendGnome::RemoveLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(changes);
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::RemoveLogin, base::Unretained(&method), form,
-                     app_string_.c_str()));
-  GnomeKeyringResult result = method.WaitResult();
-  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-    return true;
-
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring delete failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-  changes->push_back(password_manager::PasswordStoreChange(
-      password_manager::PasswordStoreChange::REMOVE, form));
-  return true;
-}
-
-bool NativeBackendGnome::RemoveLoginsCreatedBetween(
-    base::Time delete_begin,
-    base::Time delete_end,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  changes->clear();
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetAllLogins(&all_forms))
-    return false;
-
-  for (const auto& saved_form : all_forms) {
-    if (delete_begin <= saved_form->date_created &&
-        (delete_end.is_null() || saved_form->date_created < delete_end) &&
-        !RemoveLogin(*saved_form, changes)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool NativeBackendGnome::DisableAutoSignInForOrigins(
-    const base::Callback<bool(const GURL&)>& origin_filter,
-    password_manager::PasswordStoreChangeList* changes) {
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!GetAllLogins(&forms))
-    return false;
-
-  for (const std::unique_ptr<PasswordForm>& form : forms) {
-    if (origin_filter.Run(form->origin) && !form->skip_zero_click) {
-      form->skip_zero_click = true;
-      if (!UpdateLogin(*form, changes))
-        return false;
-    }
-  }
-
-  return true;
-}
-
-bool NativeBackendGnome::GetLogins(
-    const PasswordStore::FormDigest& form,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::GetLogins, base::Unretained(&method), form,
-                     app_string_.c_str()));
-  GnomeKeyringResult result = method.WaitResult(forms);
-  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-    return true;
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-  return true;
-}
-
-bool NativeBackendGnome::GetAutofillableLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(true, forms);
-}
-
-bool NativeBackendGnome::GetBlacklistLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(false, forms);
-}
-
-bool NativeBackendGnome::GetLoginsList(
-    bool autofillable,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-
-  uint32_t blacklisted_by_user = !autofillable;
-
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::GetLoginsList, base::Unretained(&method),
-                     blacklisted_by_user, app_string_.c_str()));
-  GnomeKeyringResult result = method.WaitResult(forms);
-  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-    return true;
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-
-  // Get rid of the forms with the same sync tags.
-  std::vector<std::unique_ptr<PasswordForm>> duplicates;
-  std::vector<std::vector<autofill::PasswordForm*>> tag_groups;
-  password_manager_util::FindDuplicates(forms, &duplicates, &tag_groups);
-  if (duplicates.empty())
-    return true;
-  for (const auto& group : tag_groups) {
-    if (group.size() > 1) {
-      // There are duplicates. Readd the first form. AddLogin() is smart enough
-      // to clean the previous ones.
-      password_manager::PasswordStoreChangeList changes = AddLogin(*group[0]);
-      if (changes.empty() ||
-          changes.back().type() != password_manager::PasswordStoreChange::ADD)
-        return false;
-    }
-  }
-  return true;
-}
-
-bool NativeBackendGnome::GetAllLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-  GKRMethod method(main_task_runner_, background_task_runner_);
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GKRMethod::GetAllLogins, base::Unretained(&method),
-                     app_string_.c_str()));
-  GnomeKeyringResult result = method.WaitResult(forms);
-  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-    return true;
-  if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message_ptr(result);
-    return false;
-  }
-  return true;
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-NativeBackendGnome::GetBackgroundTaskRunner() {
-  return background_task_runner_;
-}
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.h b/chrome/browser/password_manager/native_backend_gnome_x.h
deleted file mode 100644
index c9660898..0000000
--- a/chrome/browser/password_manager/native_backend_gnome_x.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/password_store_factory.h"
-#include "chrome/browser/password_manager/password_store_x.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/os_crypt/keyring_util_linux.h"
-
-namespace autofill {
-struct PasswordForm;
-}
-
-// NativeBackend implementation using GNOME Keyring.
-class NativeBackendGnome : public PasswordStoreX::NativeBackend,
-                           public GnomeKeyringLoader {
- public:
-  explicit NativeBackendGnome(LocalProfileId id);
-
-  ~NativeBackendGnome() override;
-
-  bool Init() override;
-
-  // Implements NativeBackend interface.
-  password_manager::PasswordStoreChangeList AddLogin(
-      const autofill::PasswordForm& form) override;
-  bool UpdateLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLoginsCreatedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool DisableAutoSignInForOrigins(
-      const base::Callback<bool(const GURL&)>& origin_filter,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool GetLogins(
-      const password_manager::PasswordStore::FormDigest& form,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAutofillableLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetBlacklistLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAllLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override;
-
- private:
-  // Adds a login form without checking for one to replace first.
-  bool RawAddLogin(const autofill::PasswordForm& form);
-
-  // Retrieves all autofillable or all blacklisted credentials (depending on
-  // |autofillable|) from the keyring into |forms|, overwriting the original
-  // contents of |forms|. Returns true on success.
-  bool GetLoginsList(bool autofillable,
-                     std::vector<std::unique_ptr<autofill::PasswordForm>>*
-                         forms) WARN_UNUSED_RESULT;
-
-  // The app string, possibly based on the local profile id.
-  std::string app_string_;
-
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeBackendGnome);
-};
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
diff --git a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
deleted file mode 100644
index 0551c69..0000000
--- a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
+++ /dev/null
@@ -1,1271 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner_util.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/native_backend_gnome_x.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/psl_matching_helper.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using autofill::PasswordForm;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-using password_manager::PasswordStore;
-using password_manager::PasswordStoreChange;
-using password_manager::PasswordStoreChangeList;
-using testing::Pointee;
-using testing::UnorderedElementsAre;
-
-namespace {
-
-// What follows is a very simple implementation of the subset of the GNOME
-// Keyring API that we actually use. It gets substituted for the real one by
-// MockGnomeKeyringLoader, which hooks into the facility normally used to load
-// the GNOME Keyring library at runtime to avoid a static dependency on it.
-
-struct MockKeyringItem {
-  MockKeyringItem() {}
-  MockKeyringItem(const char* keyring,
-                  const std::string& display_name,
-                  const std::string& password)
-    : keyring(keyring ? keyring : "login"),
-      display_name(display_name),
-      password(password) {}
-
-  struct ItemAttribute {
-    ItemAttribute() : type(UINT32), value_uint32(0) {}
-    explicit ItemAttribute(uint32_t value)
-      : type(UINT32), value_uint32(value) {}
-    explicit ItemAttribute(const std::string& value)
-      : type(STRING), value_string(value) {}
-
-    bool Equals(const ItemAttribute& x) const {
-      if (type != x.type) return false;
-      return (type == STRING) ? value_string == x.value_string
-                              : value_uint32 == x.value_uint32;
-    }
-
-    enum Type { UINT32, STRING } type;
-    uint32_t value_uint32;
-    std::string value_string;
-  };
-
-  typedef std::map<std::string, ItemAttribute> attribute_map;
-  typedef std::vector<std::pair<std::string, ItemAttribute> > attribute_query;
-
-  bool Matches(const attribute_query& query) const {
-    // The real GNOME Keyring doesn't match empty queries.
-    if (query.empty()) return false;
-    for (size_t i = 0; i < query.size(); ++i) {
-      auto match = attributes.find(query[i].first);
-      if (match == attributes.end()) return false;
-      if (!match->second.Equals(query[i].second)) return false;
-    }
-    return true;
-  }
-
-  std::string keyring;
-  std::string display_name;
-  std::string password;
-
-  attribute_map attributes;
-};
-
-// The list of all keyring items we have stored.
-std::vector<MockKeyringItem> mock_keyring_items;
-bool mock_keyring_reject_local_ids = false;
-
-bool IsStringAttribute(const GnomeKeyringPasswordSchema* schema,
-                       const std::string& name) {
-  for (size_t i = 0; schema->attributes[i].name; ++i)
-    if (name == schema->attributes[i].name)
-      return schema->attributes[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
-  NOTREACHED() << "Requested type of nonexistent attribute";
-  return false;
-}
-
-gboolean mock_gnome_keyring_is_available() {
-  return true;
-}
-
-gpointer mock_gnome_keyring_store_password(
-    const GnomeKeyringPasswordSchema* schema,
-    const gchar* keyring,
-    const gchar* display_name,
-    const gchar* password,
-    GnomeKeyringOperationDoneCallback callback,
-    gpointer data,
-    GDestroyNotify destroy_data,
-    ...) {
-  mock_keyring_items.push_back(
-      MockKeyringItem(keyring, display_name, password));
-  MockKeyringItem* item = &mock_keyring_items.back();
-  const std::string keyring_desc =
-      keyring ? base::StringPrintf("keyring %s", keyring)
-              : std::string("default keyring");
-  VLOG(1) << "Adding item with origin " << display_name
-          << " to " << keyring_desc;
-  va_list ap;
-  va_start(ap, destroy_data);
-  char* name;
-  while ((name = va_arg(ap, gchar*))) {
-    if (IsStringAttribute(schema, name)) {
-      item->attributes[name] =
-          MockKeyringItem::ItemAttribute(va_arg(ap, gchar*));
-      VLOG(1) << "Adding item attribute " << name
-              << ", value '" << item->attributes[name].value_string << "'";
-    } else {
-      item->attributes[name] =
-          MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t));
-      VLOG(1) << "Adding item attribute " << name
-              << ", value " << item->attributes[name].value_uint32;
-    }
-  }
-  va_end(ap);
-  // As a hack to ease testing migration, make it possible to reject the new
-  // format for the app string. This way we can add them easily to migrate.
-  if (mock_keyring_reject_local_ids) {
-    auto it = item->attributes.find("application");
-    if (it != item->attributes.end() &&
-        it->second.type == MockKeyringItem::ItemAttribute::STRING &&
-        base::StringPiece(it->second.value_string).starts_with("chrome-")) {
-      mock_keyring_items.pop_back();
-      // GnomeKeyringResult, data
-      callback(GNOME_KEYRING_RESULT_IO_ERROR, data);
-      return nullptr;
-    }
-  }
-  // GnomeKeyringResult, data
-  callback(GNOME_KEYRING_RESULT_OK, data);
-  return nullptr;
-}
-
-gpointer mock_gnome_keyring_delete_password(
-    const GnomeKeyringPasswordSchema* schema,
-    GnomeKeyringOperationDoneCallback callback,
-    gpointer data,
-    GDestroyNotify destroy_data,
-    ...) {
-  MockKeyringItem::attribute_query query;
-  va_list ap;
-  va_start(ap, destroy_data);
-  char* name;
-  while ((name = va_arg(ap, gchar*))) {
-    if (IsStringAttribute(schema, name)) {
-      query.push_back(make_pair(std::string(name),
-          MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
-      VLOG(1) << "Querying with item attribute " << name
-              << ", value '" << query.back().second.value_string << "'";
-    } else {
-      query.push_back(make_pair(std::string(name),
-          MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
-      VLOG(1) << "Querying with item attribute " << name
-              << ", value " << query.back().second.value_uint32;
-    }
-  }
-  va_end(ap);
-  bool deleted = false;
-  for (size_t i = mock_keyring_items.size(); i > 0; --i) {
-    const MockKeyringItem* item = &mock_keyring_items[i - 1];
-    if (item->Matches(query)) {
-      VLOG(1) << "Deleting item with origin " <<  item->display_name;
-      mock_keyring_items.erase(mock_keyring_items.begin() + (i - 1));
-      deleted = true;
-    }
-  }
-  // GnomeKeyringResult, data
-  callback(deleted ? GNOME_KEYRING_RESULT_OK
-                   : GNOME_KEYRING_RESULT_NO_MATCH, data);
-  return nullptr;
-}
-
-gpointer mock_gnome_keyring_find_items(
-    GnomeKeyringItemType type,
-    GnomeKeyringAttributeList* attributes,
-    GnomeKeyringOperationGetListCallback callback,
-    gpointer data,
-    GDestroyNotify destroy_data) {
-  MockKeyringItem::attribute_query query;
-  for (size_t i = 0; i < attributes->len; ++i) {
-    GnomeKeyringAttribute attribute =
-        g_array_index(attributes, GnomeKeyringAttribute, i);
-    if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
-      query.push_back(
-          make_pair(std::string(attribute.name),
-                    MockKeyringItem::ItemAttribute(attribute.value.string)));
-      VLOG(1) << "Querying with item attribute " << attribute.name
-              << ", value '" << query.back().second.value_string << "'";
-    } else {
-      query.push_back(
-          make_pair(std::string(attribute.name),
-                    MockKeyringItem::ItemAttribute(attribute.value.integer)));
-      VLOG(1) << "Querying with item attribute " << attribute.name << ", value "
-              << query.back().second.value_uint32;
-    }
-  }
-  // Find matches and add them to a list of results.
-  GList* results = nullptr;
-  for (size_t i = 0; i < mock_keyring_items.size(); ++i) {
-    const MockKeyringItem* item = &mock_keyring_items[i];
-    if (item->Matches(query)) {
-      GnomeKeyringFound* found = new GnomeKeyringFound;
-      found->keyring = strdup(item->keyring.c_str());
-      found->item_id = i;
-      found->attributes = gnome_keyring_attribute_list_new();
-      for (auto it = item->attributes.begin(); it != item->attributes.end();
-           ++it) {
-        if (it->second.type == MockKeyringItem::ItemAttribute::STRING) {
-          gnome_keyring_attribute_list_append_string(
-              found->attributes, it->first.c_str(),
-              it->second.value_string.c_str());
-        } else {
-          gnome_keyring_attribute_list_append_uint32(
-              found->attributes, it->first.c_str(),
-              it->second.value_uint32);
-        }
-      }
-      found->secret = strdup(item->password.c_str());
-      results = g_list_prepend(results, found);
-    }
-  }
-  // GnomeKeyringResult, GList*, data
-  callback(results ? GNOME_KEYRING_RESULT_OK
-                   : GNOME_KEYRING_RESULT_NO_MATCH, results, data);
-  // Now free the list of results.
-  GList* element = g_list_first(results);
-  while (element) {
-    GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data);
-    free(found->keyring);
-    gnome_keyring_attribute_list_free(found->attributes);
-    free(found->secret);
-    delete found;
-    element = g_list_next(element);
-  }
-  g_list_free(results);
-  return nullptr;
-}
-
-const gchar* mock_gnome_keyring_result_to_message(GnomeKeyringResult res) {
-  return "mock keyring simulating failure";
-}
-
-// Inherit to get access to protected fields.
-class MockGnomeKeyringLoader : public GnomeKeyringLoader {
- public:
-  static bool LoadMockGnomeKeyring() {
-    // Mocked methods
-    gnome_keyring_is_available_ptr = &mock_gnome_keyring_is_available;
-    gnome_keyring_store_password_ptr = &mock_gnome_keyring_store_password;
-    gnome_keyring_delete_password_ptr = &mock_gnome_keyring_delete_password;
-    gnome_keyring_find_items_ptr = &mock_gnome_keyring_find_items;
-    gnome_keyring_result_to_message_ptr = &mock_gnome_keyring_result_to_message;
-    // Non-mocked methods
-    gnome_keyring_attribute_list_free_ptr =
-        &::gnome_keyring_attribute_list_free;
-    gnome_keyring_attribute_list_new_ptr = &::gnome_keyring_attribute_list_new;
-    gnome_keyring_attribute_list_append_string_ptr =
-        &::gnome_keyring_attribute_list_append_string;
-    gnome_keyring_attribute_list_append_uint32_ptr =
-        &::gnome_keyring_attribute_list_append_uint32;
-
-    keyring_loaded = true;
-    // Reset the state of the mock library.
-    mock_keyring_items.clear();
-    mock_keyring_reject_local_ids = false;
-    return true;
-  }
-};
-
-void CheckPasswordChanges(const PasswordStoreChangeList& expected_list,
-                          const PasswordStoreChangeList& actual_list) {
-  ASSERT_EQ(expected_list.size(), actual_list.size());
-  for (size_t i = 0; i < expected_list.size(); ++i) {
-    EXPECT_EQ(expected_list[i].type(), actual_list[i].type());
-    const PasswordForm& expected = expected_list[i].form();
-    const PasswordForm& actual = actual_list[i].form();
-
-    EXPECT_EQ(expected.origin, actual.origin);
-    EXPECT_EQ(expected.password_value, actual.password_value);
-    EXPECT_EQ(expected.action, actual.action);
-    EXPECT_EQ(expected.username_element, actual.username_element);
-    EXPECT_EQ(expected.username_value, actual.username_value);
-    EXPECT_EQ(expected.password_element, actual.password_element);
-    EXPECT_EQ(expected.submit_element, actual.submit_element);
-    EXPECT_EQ(expected.signon_realm, actual.signon_realm);
-    EXPECT_EQ(expected.preferred, actual.preferred);
-    EXPECT_EQ(expected.date_created, actual.date_created);
-    EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
-    EXPECT_EQ(expected.type, actual.type);
-    EXPECT_EQ(expected.times_used, actual.times_used);
-    EXPECT_EQ(expected.scheme, actual.scheme);
-    EXPECT_EQ(expected.date_synced, actual.date_synced);
-    EXPECT_EQ(expected.display_name, actual.display_name);
-    EXPECT_EQ(expected.icon_url, actual.icon_url);
-    EXPECT_EQ(expected.federation_origin.Serialize(),
-              actual.federation_origin.Serialize());
-    EXPECT_EQ(expected.skip_zero_click, actual.skip_zero_click);
-    EXPECT_EQ(expected.generation_upload_status,
-              actual.generation_upload_status);
-  }
-}
-
-void CheckPasswordChangesWithResult(const PasswordStoreChangeList* expected,
-                                    const PasswordStoreChangeList* actual,
-                                    bool result) {
-  EXPECT_TRUE(result);
-  CheckPasswordChanges(*expected, *actual);
-}
-
-void CheckTrue(bool result) {
-  EXPECT_TRUE(result);
-}
-
-}  // anonymous namespace
-
-class NativeBackendGnomeTest : public testing::Test {
- protected:
-  enum UpdateType {  // Used in CheckPSLUpdate().
-    UPDATE_BY_UPDATELOGIN,
-    UPDATE_BY_ADDLOGIN,
-  };
-
-  NativeBackendGnomeTest() {}
-
-  void SetUp() override {
-    ASSERT_TRUE(MockGnomeKeyringLoader::LoadMockGnomeKeyring());
-
-    form_google_.origin = GURL("http://www.google.com/");
-    form_google_.action = GURL("http://www.google.com/login");
-    form_google_.username_element = UTF8ToUTF16("user");
-    form_google_.username_value = UTF8ToUTF16("joeschmoe");
-    form_google_.password_element = UTF8ToUTF16("pass");
-    form_google_.password_value = UTF8ToUTF16("seekrit");
-    form_google_.submit_element = UTF8ToUTF16("submit");
-    form_google_.signon_realm = "http://www.google.com/";
-    form_google_.type = PasswordForm::Type::kGenerated;
-    form_google_.date_created = base::Time::Now();
-    form_google_.date_synced = base::Time::Now();
-    form_google_.display_name = UTF8ToUTF16("Joe Schmoe");
-    form_google_.icon_url = GURL("http://www.google.com/icon");
-    form_google_.federation_origin =
-        url::Origin::Create(GURL("http://www.google.com/"));
-    form_google_.skip_zero_click = true;
-    form_google_.generation_upload_status =
-        PasswordForm::GenerationUploadStatus::kPositiveSignalSent;
-    form_google_.form_data.name = UTF8ToUTF16("form_name");
-
-    form_facebook_.origin = GURL("http://www.facebook.com/");
-    form_facebook_.action = GURL("http://www.facebook.com/login");
-    form_facebook_.username_element = UTF8ToUTF16("user");
-    form_facebook_.username_value = UTF8ToUTF16("a");
-    form_facebook_.password_element = UTF8ToUTF16("password");
-    form_facebook_.password_value = UTF8ToUTF16("b");
-    form_facebook_.submit_element = UTF8ToUTF16("submit");
-    form_facebook_.signon_realm = "http://www.facebook.com/";
-    form_facebook_.date_created = base::Time::Now();
-    form_facebook_.date_synced = base::Time::Now();
-    form_facebook_.display_name = UTF8ToUTF16("Joe Schmoe");
-    form_facebook_.icon_url = GURL("http://www.facebook.com/icon");
-    form_facebook_.federation_origin =
-        url::Origin::Create(GURL("http://www.facebook.com/"));
-    form_facebook_.skip_zero_click = true;
-    form_facebook_.generation_upload_status =
-        PasswordForm::GenerationUploadStatus::kNoSignalSent;
-
-    form_isc_.origin = GURL("http://www.isc.org/");
-    form_isc_.action = GURL("http://www.isc.org/auth");
-    form_isc_.username_element = UTF8ToUTF16("id");
-    form_isc_.username_value = UTF8ToUTF16("janedoe");
-    form_isc_.password_element = UTF8ToUTF16("passwd");
-    form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
-    form_isc_.submit_element = UTF8ToUTF16("login");
-    form_isc_.signon_realm = "http://www.isc.org/";
-    form_isc_.date_created = base::Time::Now();
-    form_isc_.date_synced = base::Time::Now();
-
-    other_auth_.origin = GURL("http://www.example.com/");
-    other_auth_.username_value = UTF8ToUTF16("username");
-    other_auth_.password_value = UTF8ToUTF16("pass");
-    other_auth_.signon_realm = "http://www.example.com/Realm";
-    other_auth_.date_created = base::Time::Now();
-    other_auth_.date_synced = base::Time::Now();
-  }
-
-  void CheckUint32Attribute(const MockKeyringItem* item,
-                            const std::string& attribute,
-                            uint32_t value) {
-    auto it = item->attributes.find(attribute);
-    EXPECT_NE(item->attributes.end(), it);
-    if (it != item->attributes.end()) {
-      EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32, it->second.type);
-      EXPECT_EQ(value, it->second.value_uint32);
-    }
-  }
-
-  void CheckStringAttribute(const MockKeyringItem* item,
-                            const std::string& attribute,
-                            const std::string& value) {
-    auto it = item->attributes.find(attribute);
-    EXPECT_NE(item->attributes.end(), it);
-    if (it != item->attributes.end()) {
-      EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING, it->second.type);
-      EXPECT_EQ(value, it->second.value_string);
-    }
-  }
-
-  void CheckMockKeyringItem(const MockKeyringItem* item,
-                            const PasswordForm& form,
-                            const std::string& app_string) {
-    // We always add items to the login keyring.
-    EXPECT_EQ("login", item->keyring);
-    EXPECT_EQ(form.origin.spec(), item->display_name);
-    EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password);
-    EXPECT_EQ(21u, item->attributes.size());
-    CheckStringAttribute(item, "origin_url", form.origin.spec());
-    CheckStringAttribute(item, "action_url", form.action.spec());
-    CheckStringAttribute(item, "username_element",
-                         UTF16ToUTF8(form.username_element));
-    CheckStringAttribute(item, "username_value",
-                         UTF16ToUTF8(form.username_value));
-    CheckStringAttribute(item, "password_element",
-                         UTF16ToUTF8(form.password_element));
-    CheckStringAttribute(item, "submit_element",
-                         UTF16ToUTF8(form.submit_element));
-    CheckStringAttribute(item, "signon_realm", form.signon_realm);
-    CheckUint32Attribute(item, "preferred", form.preferred);
-    // We don't check the date created. It varies.
-    CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
-    CheckUint32Attribute(item, "type", static_cast<uint32_t>(form.type));
-    CheckUint32Attribute(item, "times_used", form.times_used);
-    CheckUint32Attribute(item, "scheme", static_cast<uint32_t>(form.scheme));
-    CheckStringAttribute(
-        item, "date_synced",
-        base::NumberToString(form.date_synced.ToInternalValue()));
-    CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
-    CheckStringAttribute(item, "avatar_url", form.icon_url.spec());
-    // We serialize unique origins as "", in order to make other systems that
-    // read from the login database happy. https://crbug.com/591310
-    CheckStringAttribute(item, "federation_url",
-                         form.federation_origin.opaque()
-                             ? ""
-                             : form.federation_origin.Serialize());
-    CheckUint32Attribute(item, "should_skip_zero_click", form.skip_zero_click);
-    CheckUint32Attribute(item, "generation_upload_status",
-                         static_cast<uint32_t>(form.generation_upload_status));
-    CheckStringAttribute(item, "application", app_string);
-    autofill::FormData actual;
-    DeserializeFormDataFromBase64String(
-        item->attributes.at("form_data").value_string, &actual);
-    EXPECT_TRUE(form.form_data.SameFormAs(actual));
-  }
-
-  // Saves |credentials| and then gets logins matching |url| and |scheme|.
-  // Returns true when something is found, and in such case copies the result to
-  // |result| when |result| is not NULL. (Note that there can be max. 1 result,
-  // derived from |credentials|.)
-  bool CheckCredentialAvailability(const PasswordForm& credentials,
-                                   const GURL& url,
-                                   const PasswordForm::Scheme& scheme,
-                                   PasswordForm* result) {
-    NativeBackendGnome backend(321);
-    backend.Init();
-
-    backend.GetBackgroundTaskRunner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                       base::Unretained(&backend), credentials));
-
-    PasswordStore::FormDigest target_form = {scheme, url.spec(), url};
-    if (scheme != PasswordForm::Scheme::kHtml) {
-      // For non-HTML forms, the realm used for authentication
-      // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
-      // signon_realm. Just use a default value for now.
-      target_form.signon_realm.append("Realm");
-    }
-    std::vector<std::unique_ptr<PasswordForm>> form_list;
-    base::PostTaskAndReplyWithResult(
-        backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-        base::Bind(&NativeBackendGnome::GetLogins, base::Unretained(&backend),
-                   target_form, &form_list),
-        base::Bind(&CheckTrue));
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    EXPECT_EQ(1u, mock_keyring_items.size());
-    if (mock_keyring_items.size() > 0)
-      CheckMockKeyringItem(&mock_keyring_items[0], credentials, "chrome-321");
-    mock_keyring_items.clear();
-
-    if (form_list.empty())
-      return false;
-    EXPECT_EQ(1u, form_list.size());
-    if (result)
-      *result = *form_list[0];
-    return true;
-  }
-
-  // Test that updating does not use PSL matching: Add a www.facebook.com
-  // password, then use PSL matching to get a copy of it for m.facebook.com, and
-  // add that copy as well. Now update the www.facebook.com password -- the
-  // m.facebook.com password should not get updated. Depending on the argument,
-  // the credential update is done via UpdateLogin or AddLogin.
-  void CheckPSLUpdate(UpdateType update_type) {
-    NativeBackendGnome backend(321);
-    backend.Init();
-
-    // Add |form_facebook_| to saved logins.
-    base::PostTaskAndReplyWithResult(
-        backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-        base::Bind(&NativeBackendGnome::AddLogin, base::Unretained(&backend),
-                   form_facebook_),
-        base::Bind(&CheckPasswordChanges,
-                   PasswordStoreChangeList(
-                       1, PasswordStoreChange(PasswordStoreChange::ADD,
-                                              form_facebook_))));
-
-    // Get the PSL-matched copy of the saved login for m.facebook.
-    const GURL kMobileURL("http://m.facebook.com/");
-    PasswordStore::FormDigest m_facebook_lookup = {
-        PasswordForm::Scheme::kHtml, kMobileURL.spec(), kMobileURL};
-    std::vector<std::unique_ptr<PasswordForm>> form_list;
-    base::PostTaskAndReplyWithResult(
-        backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-        base::Bind(&NativeBackendGnome::GetLogins, base::Unretained(&backend),
-                   m_facebook_lookup, &form_list),
-        base::Bind(&CheckTrue));
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    EXPECT_EQ(1u, mock_keyring_items.size());
-    EXPECT_EQ(1u, form_list.size());
-    PasswordForm m_facebook = *form_list[0];
-    form_list.clear();
-    m_facebook.origin = kMobileURL;
-    m_facebook.signon_realm = kMobileURL.spec();
-
-    // Add the PSL-matched copy to saved logins.
-    backend.GetBackgroundTaskRunner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                       base::Unretained(&backend), m_facebook));
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    EXPECT_EQ(2u, mock_keyring_items.size());
-
-    // Update www.facebook.com login.
-    PasswordForm new_facebook(form_facebook_);
-    const base::string16 kOldPassword(form_facebook_.password_value);
-    const base::string16 kNewPassword(UTF8ToUTF16("new_b"));
-    EXPECT_NE(kOldPassword, kNewPassword);
-    new_facebook.password_value = kNewPassword;
-    PasswordStoreChangeList changes;
-    PasswordStoreChangeList expected_changes;
-    switch (update_type) {
-      case UPDATE_BY_UPDATELOGIN:
-        expected_changes.push_back(
-            PasswordStoreChange(PasswordStoreChange::UPDATE, new_facebook));
-        base::PostTaskAndReplyWithResult(
-            backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-            base::Bind(&NativeBackendGnome::UpdateLogin,
-                       base::Unretained(&backend), new_facebook, &changes),
-            base::Bind(&CheckPasswordChangesWithResult, &expected_changes,
-                       &changes));
-        break;
-      case UPDATE_BY_ADDLOGIN:
-        expected_changes.push_back(
-            PasswordStoreChange(PasswordStoreChange::REMOVE, form_facebook_));
-        expected_changes.push_back(
-            PasswordStoreChange(PasswordStoreChange::ADD, new_facebook));
-        base::PostTaskAndReplyWithResult(
-            backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-            base::Bind(&NativeBackendGnome::AddLogin,
-                       base::Unretained(&backend), new_facebook),
-            base::Bind(&CheckPasswordChanges, expected_changes));
-        break;
-    }
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    EXPECT_EQ(2u, mock_keyring_items.size());
-
-    // Check that m.facebook.com login was not modified by the update.
-    base::PostTaskAndReplyWithResult(
-        backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-        base::Bind(&NativeBackendGnome::GetLogins, base::Unretained(&backend),
-                   m_facebook_lookup, &form_list),
-        base::Bind(&CheckTrue));
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    // There should be two results -- the exact one, and the PSL-matched one.
-    EXPECT_EQ(2u, form_list.size());
-    size_t index_non_psl = 0;
-    if (form_list[index_non_psl]->is_public_suffix_match)
-      index_non_psl = 1;
-    EXPECT_EQ(kMobileURL, form_list[index_non_psl]->origin);
-    EXPECT_EQ(kMobileURL.spec(), form_list[index_non_psl]->signon_realm);
-    EXPECT_EQ(kOldPassword, form_list[index_non_psl]->password_value);
-    form_list.clear();
-
-    // Check that www.facebook.com login was modified by the update.
-    base::PostTaskAndReplyWithResult(
-        backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-        base::Bind(&NativeBackendGnome::GetLogins, base::Unretained(&backend),
-                   PasswordStore::FormDigest(form_facebook_), &form_list),
-        base::Bind(&CheckTrue));
-
-    test_browser_thread_bundle_.RunUntilIdle();
-
-    // There should be two results -- the exact one, and the PSL-matched one.
-    EXPECT_EQ(2u, form_list.size());
-    index_non_psl = 0;
-    if (form_list[index_non_psl]->is_public_suffix_match)
-      index_non_psl = 1;
-    EXPECT_EQ(form_facebook_.origin, form_list[index_non_psl]->origin);
-    EXPECT_EQ(form_facebook_.signon_realm,
-              form_list[index_non_psl]->signon_realm);
-    EXPECT_EQ(kNewPassword, form_list[index_non_psl]->password_value);
-  }
-
-  void CheckMatchingWithScheme(const PasswordForm::Scheme& scheme) {
-    other_auth_.scheme = scheme;
-
-    // Don't match a non-HTML form with an HTML form.
-    EXPECT_FALSE(
-        CheckCredentialAvailability(other_auth_, GURL("http://www.example.com"),
-                                    PasswordForm::Scheme::kHtml, nullptr));
-    // Don't match an HTML form with non-HTML auth form.
-    EXPECT_FALSE(CheckCredentialAvailability(
-        form_google_, GURL("http://www.google.com/"), scheme, nullptr));
-    // Don't match two different non-HTML auth forms with different origin.
-    EXPECT_FALSE(CheckCredentialAvailability(
-        other_auth_, GURL("http://first.example.com"), scheme, nullptr));
-    // Do match non-HTML forms from the same origin.
-    EXPECT_TRUE(CheckCredentialAvailability(
-        other_auth_, GURL("http://www.example.com/"), scheme, nullptr));
-  }
-
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
-
-  // Provide some test forms to avoid having to set them up in each test.
-  PasswordForm form_google_;
-  PasswordForm form_facebook_;
-  PasswordForm form_isc_;
-  PasswordForm other_auth_;
-};
-
-TEST_F(NativeBackendGnomeTest, BasicAddLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::AddLogin, base::Unretained(&backend),
-                 form_google_),
-      base::Bind(
-          &CheckPasswordChanges,
-          PasswordStoreChangeList(
-              1, PasswordStoreChange(PasswordStoreChange::ADD, form_google_))));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, BasicListLogins) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  // Quick check that we got something back.
-  EXPECT_EQ(1u, form_list.size());
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-// Save a password for www.facebook.com and see it suggested for m.facebook.com.
-TEST_F(NativeBackendGnomeTest, PSLMatchingPositive) {
-  PasswordForm result;
-  const GURL kMobileURL("http://m.facebook.com/");
-  EXPECT_TRUE(CheckCredentialAvailability(
-      form_facebook_, kMobileURL, PasswordForm::Scheme::kHtml, &result));
-  EXPECT_EQ(form_facebook_.origin, result.origin);
-  EXPECT_EQ(form_facebook_.signon_realm, result.signon_realm);
-}
-
-// Save a password for www.facebook.com and see it not suggested for
-// m-facebook.com.
-TEST_F(NativeBackendGnomeTest, PSLMatchingNegativeDomainMismatch) {
-  EXPECT_FALSE(CheckCredentialAvailability(
-      form_facebook_, GURL("http://m-facebook.com/"),
-      PasswordForm::Scheme::kHtml, nullptr));
-}
-
-// Test PSL matching is off for domains excluded from it.
-TEST_F(NativeBackendGnomeTest, PSLMatchingDisabledDomains) {
-  EXPECT_FALSE(
-      CheckCredentialAvailability(form_google_, GURL("http://one.google.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-// Make sure PSL matches aren't available for non-HTML forms.
-TEST_F(NativeBackendGnomeTest, PSLMatchingDisabledForNonHTMLForms) {
-  CheckMatchingWithScheme(PasswordForm::Scheme::kBasic);
-  CheckMatchingWithScheme(PasswordForm::Scheme::kDigest);
-  CheckMatchingWithScheme(PasswordForm::Scheme::kOther);
-}
-
-TEST_F(NativeBackendGnomeTest, PSLUpdatingStrictUpdateLogin) {
-  CheckPSLUpdate(UPDATE_BY_UPDATELOGIN);
-}
-
-TEST_F(NativeBackendGnomeTest, PSLUpdatingStrictAddLogin) {
-  CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
-}
-
-TEST_F(NativeBackendGnomeTest, FetchFederatedCredentialOnHTTPS) {
-  other_auth_.signon_realm = "federation://www.example.com/google.com";
-  other_auth_.origin = GURL("https://www.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("https://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendGnomeTest, FetchFederatedCredentialOnLocalhost) {
-  other_auth_.signon_realm = "federation://localhost/google.com";
-  other_auth_.origin = GURL("http://localhost:8080/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("http://localhost:8080/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendGnomeTest, DontFetchFederatedCredentialOnHTTP) {
-  other_auth_.signon_realm = "federation://www.example.com/google.com";
-  other_auth_.origin = GURL("https://www.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_FALSE(
-      CheckCredentialAvailability(other_auth_, GURL("http://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendGnomeTest, FetchPSLMatchedFederatedCredentialOnHTTPS) {
-  other_auth_.signon_realm = "federation://www.sub.example.com/google.com";
-  other_auth_.origin = GURL("https://www.sub.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("https://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendGnomeTest, DontFetchPSLMatchedFederatedCredentialOnHTTP) {
-  other_auth_.signon_realm = "federation://www.sub.example.com/google.com";
-  other_auth_.origin = GURL("https://www.sub.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_FALSE(
-      CheckCredentialAvailability(other_auth_, GURL("http://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendGnomeTest, BasicUpdateLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  // First add google login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  PasswordForm new_form_google(form_google_);
-  new_form_google.times_used = 1;
-  new_form_google.action = GURL("http://www.google.com/different/login");
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  // Update login
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes(
-      1, PasswordStoreChange(PasswordStoreChange::UPDATE, new_form_google));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::UpdateLogin, base::Unretained(&backend),
-                 new_form_google, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], new_form_google, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, BasicRemoveLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes(
-      1, PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::RemoveLogin, base::Unretained(&backend),
-                 form_google_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(0u, mock_keyring_items.size());
-}
-
-// Verify fix for http://crbug.com/408783.
-TEST_F(NativeBackendGnomeTest, RemoveLoginActionMismatch) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  // Action url match not required for removal.
-  form_google_.action = GURL("https://some.other.url.com/path");
-
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes(
-      1, PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::RemoveLogin, base::Unretained(&backend),
-                 form_google_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(0u, mock_keyring_items.size());
-}
-
-TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  // First add an unrelated login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  // Attempt to remove a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::RemoveLogin, base::Unretained(&backend),
-                 form_isc_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult,
-                 base::Owned(new PasswordStoreChangeList), &changes));
-
-  // Make sure we can still get the first form back.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  // Quick check that we got something back.
-  EXPECT_EQ(1u, form_list.size());
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, UpdateNonexistentLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  // First add an unrelated login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  // Attempt to update a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::UpdateLogin, base::Unretained(&backend),
-                 form_isc_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult,
-                 base::Owned(new PasswordStoreChangeList), &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, UpdateSameLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  // First add an unrelated login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-
-  // Attempt to update the same login without changing anything.
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::UpdateLogin, base::Unretained(&backend),
-                 form_google_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, AddDuplicateLogin) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  PasswordStoreChangeList changes;
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
-                                        form_google_));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::AddLogin, base::Unretained(&backend),
-                 form_google_),
-      base::Bind(&CheckPasswordChanges, changes));
-
-  changes.clear();
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
-                                        form_google_));
-  form_google_.times_used++;
-  form_google_.submit_element = UTF8ToUTF16("submit2");
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
-                                        form_google_));
-
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::AddLogin, base::Unretained(&backend),
-                 form_google_),
-      base::Bind(&CheckPasswordChanges, changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, AndroidCredentials) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  PasswordForm saved_android_form;
-  saved_android_form.scheme = PasswordForm::Scheme::kHtml;
-  saved_android_form.signon_realm =
-      "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
-  saved_android_form.username_value = base::UTF8ToUTF16("randomusername");
-  saved_android_form.password_value = base::UTF8ToUTF16("password");
-  saved_android_form.date_created = base::Time::Now();
-
-  PasswordStore::FormDigest observed_android_form(saved_android_form);
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::AddLogin, base::Unretained(&backend),
-                 saved_android_form),
-      base::Bind(&CheckPasswordChanges,
-                 PasswordStoreChangeList(
-                     1, PasswordStoreChange(PasswordStoreChange::ADD,
-                                            saved_android_form))));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::GetLogins, base::Unretained(&backend),
-                 observed_android_form, &form_list),
-      base::Bind(&CheckTrue));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(saved_android_form, *form_list[0]);
-}
-
-TEST_F(NativeBackendGnomeTest, RemoveLoginsCreatedBetween) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  base::Time now = base::Time::Now();
-  base::Time next_day = now + base::TimeDelta::FromDays(1);
-  form_google_.date_created = now;
-  form_isc_.date_created = next_day;
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_isc_));
-
-  PasswordStoreChangeList expected_changes;
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_google_);
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::BindOnce(&NativeBackendGnome::RemoveLoginsCreatedBetween,
-                     base::Unretained(&backend), base::Time(), next_day,
-                     &changes),
-      base::BindOnce(&CheckPasswordChangesWithResult, &expected_changes,
-                     &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  ASSERT_EQ(1u, mock_keyring_items.size());
-  CheckMockKeyringItem(&mock_keyring_items[0], form_isc_, "chrome-42");
-
-  // Remove form_isc_.
-  expected_changes.clear();
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_isc_);
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::BindOnce(&NativeBackendGnome::RemoveLoginsCreatedBetween,
-                     base::Unretained(&backend), next_day, base::Time(),
-                     &changes),
-      base::BindOnce(&CheckPasswordChangesWithResult, &expected_changes,
-                     &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_TRUE(mock_keyring_items.empty());
-}
-
-TEST_F(NativeBackendGnomeTest, DisableAutoSignInForOrigins) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-  form_google_.skip_zero_click = false;
-  form_facebook_.skip_zero_click = false;
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_facebook_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(2u, mock_keyring_items.size());
-  for (const auto& item : mock_keyring_items)
-    CheckUint32Attribute(&item, "should_skip_zero_click", 0);
-
-  // Set the canonical forms to the updated value for the following comparison.
-  form_google_.skip_zero_click = true;
-  form_facebook_.skip_zero_click = true;
-  PasswordStoreChangeList expected_changes;
-  expected_changes.push_back(
-      PasswordStoreChange(PasswordStoreChange::UPDATE, form_facebook_));
-
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(
-          &NativeBackendGnome::DisableAutoSignInForOrigins,
-          base::Unretained(&backend),
-          base::Bind(
-              static_cast<bool (*)(const GURL&, const GURL&)>(operator==),
-              form_facebook_.origin),
-          &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(2u, mock_keyring_items.size());
-  CheckStringAttribute(
-      &mock_keyring_items[0], "origin_url", form_google_.origin.spec());
-  CheckUint32Attribute(&mock_keyring_items[0], "should_skip_zero_click", 0);
-  CheckStringAttribute(
-      &mock_keyring_items[1], "origin_url", form_facebook_.origin.spec());
-  CheckUint32Attribute(&mock_keyring_items[1], "should_skip_zero_click", 1);
-}
-
-TEST_F(NativeBackendGnomeTest, ReadDuplicateForms) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  // Add 2 slightly different password forms.
-  const char unique_string[] = "unique_unique_string";
-  const char unique_string_replacement[] = "uniKue_unique_string";
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string);
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string_replacement);
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  // Read the raw value back. Change the |unique_string| to
-  // |unique_string_replacement| so the forms become unique.
-  ASSERT_EQ(2u, mock_keyring_items.size());
-  auto it = mock_keyring_items[0].attributes.find("origin_url");
-  ASSERT_NE(mock_keyring_items[0].attributes.end(), it);
-  size_t position = it->second.value_string.find(unique_string);
-  ASSERT_NE(std::string::npos, position) << it->second.value_string;
-  it->second.value_string.replace(
-      position, std::string(unique_string_replacement).length(),
-      unique_string_replacement);
-
-  // Now test that GetAutofillableLogins returns only one form.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(form_google_, *form_list[0]);
-
-  EXPECT_EQ(1u, mock_keyring_items.size());
-  if (mock_keyring_items.size() > 0)
-    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
-}
-
-TEST_F(NativeBackendGnomeTest, GetAllLogins) {
-  NativeBackendGnome backend(42);
-  backend.Init();
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendGnome::AddLogin),
-                     base::Unretained(&backend), form_facebook_));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendGnome::GetAllLogins, base::Unretained(&backend),
-                 &form_list),
-      base::Bind(&CheckTrue));
-
-  test_browser_thread_bundle_.RunUntilIdle();
-
-  EXPECT_EQ(2u, form_list.size());
-  EXPECT_THAT(form_list, UnorderedElementsAre(Pointee(form_google_),
-                                              Pointee(form_facebook_)));
-}
-
-// TODO(mdm): add more basic tests here at some point.
diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc
deleted file mode 100644
index ed1c42c..0000000
--- a/chrome/browser/password_manager/native_backend_kwallet_x.cc
+++ /dev/null
@@ -1,826 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <iterator>
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/pickle.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task/post_task.h"
-#include "base/threading/thread_restrictions.h"
-#include "chrome/grit/chromium_strings.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/dbus/thread_linux/dbus_thread_linux.h"
-#include "components/password_manager/core/browser/password_manager_util.h"
-#include "dbus/bus.h"
-#include "dbus/message.h"
-#include "dbus/object_path.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "url/origin.h"
-
-using autofill::PasswordForm;
-
-namespace {
-
-// In case the fields in the pickle ever change, version them so we can try to
-// read old pickles. (Note: do not eat old pickles past the expiration date.)
-const int kPickleVersion = 9;
-
-// We could localize this string, but then changing your locale would cause
-// you to lose access to all your stored passwords. Maybe best not to do that.
-// Name of the folder to store passwords in.
-const char kKWalletFolder[] = "Chrome Form Data";
-
-// Checks a serialized list of PasswordForms for sanity. Returns true if OK.
-// Note that |realm| is only used for generating a useful warning message.
-bool CheckSerializedValue(const uint8_t* byte_array,
-                          size_t length,
-                          const std::string& realm) {
-  const base::Pickle::Header* header =
-      reinterpret_cast<const base::Pickle::Header*>(byte_array);
-  if (length < sizeof(*header) ||
-      header->payload_size > length - sizeof(*header)) {
-    LOG(WARNING) << "Invalid KWallet entry detected (realm: " << realm << ")";
-    return false;
-  }
-  return true;
-}
-
-// Convenience function to read a GURL from a Pickle. Assumes the URL has
-// been written as a UTF-8 string. Returns true on success.
-bool ReadGURL(base::PickleIterator* iter, bool warn_only, GURL* url) {
-  std::string url_string;
-  if (!iter->ReadString(&url_string)) {
-    if (!warn_only)
-      LOG(ERROR) << "Failed to deserialize URL.";
-    *url = GURL();
-    return false;
-  }
-  *url = GURL(url_string);
-  return true;
-}
-
-// Convenience function to read a url::Origin from a Pickle. Assumes the origin
-// has been written as a UTF-8 string. Returns true on success.
-bool ReadOrigin(base::PickleIterator* iter,
-                bool warn_only,
-                url::Origin* origin) {
-  std::string origin_string;
-  if (!iter->ReadString(&origin_string)) {
-    if (!warn_only)
-      LOG(ERROR) << "Failed to deserialize Origin.";
-    *origin = url::Origin();
-    return false;
-  }
-  *origin = url::Origin::Create(GURL(origin_string));
-  return true;
-}
-
-void LogDeserializationWarning(int version,
-                               std::string signon_realm,
-                               bool warn_only) {
-  if (warn_only) {
-    LOG(WARNING) << "Failed to deserialize version " << version
-                 << " KWallet entry (realm: " << signon_realm
-                 << ") with native architecture size; will try alternate "
-                 << "size.";
-  } else {
-    LOG(ERROR) << "Failed to deserialize version " << version
-               << " KWallet entry (realm: " << signon_realm << ")";
-  }
-}
-
-// Deserializes a list of credentials from the wallet to |forms| (replacing
-// the contents of |forms|). |size_32| controls reading the size field within
-// the pickle as 32 bits. We used to use Pickle::WriteSize() to write the number
-// of password forms, but that has a different size on 32- and 64-bit systems.
-// So, now we always write a 64-bit quantity, but we support trying to read it
-// as either size when reading old pickles that fail to deserialize using the
-// native size. Returns true on success.
-bool DeserializeValueSize(const std::string& signon_realm,
-                          const base::PickleIterator& init_iter,
-                          int version,
-                          bool size_32,
-                          bool warn_only,
-                          std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  base::PickleIterator iter = init_iter;
-
-  size_t count = 0;
-  if (size_32) {
-    uint32_t count_32 = 0;
-    if (!iter.ReadUInt32(&count_32)) {
-      LOG(ERROR) << "Failed to deserialize KWallet entry "
-                 << "(realm: " << signon_realm << ")";
-      return false;
-    }
-    count = count_32;
-  } else {
-    uint64_t count_64 = 0;
-    if (!iter.ReadUInt64(&count_64)) {
-      LOG(ERROR) << "Failed to deserialize KWallet entry "
-                 << "(realm: " << signon_realm << ")";
-      return false;
-    }
-    count = static_cast<size_t>(count_64);
-  }
-
-  if (count > 0xFFFF) {
-    // Trying to pin down the cause of http://crbug.com/80728 (or fix it).
-    // This is a very large number of passwords to be saved for a single realm.
-    // It is almost certainly a corrupt pickle and not real data. Ignore it.
-    // This very well might actually be http://crbug.com/107701, so if we're
-    // reading an old pickle, we don't even log this the first time we try to
-    // read it. (That is, when we're reading the native architecture size.)
-    if (!warn_only) {
-      LOG(ERROR) << "Suspiciously large number of entries in KWallet entry "
-                 << "(" << count << "; realm: " << signon_realm << ")";
-    }
-    return false;
-  }
-
-  // We'll swap |converted_forms| with |*forms| on success, to make sure we
-  // don't return partial results on failure.
-  std::vector<std::unique_ptr<PasswordForm>> converted_forms;
-  converted_forms.reserve(count);
-  for (size_t i = 0; i < count; ++i) {
-    std::unique_ptr<PasswordForm> form(new PasswordForm());
-    form->signon_realm.assign(signon_realm);
-
-    int scheme = 0;
-    int64_t date_created = 0;
-    int type = 0;
-    int generation_upload_status = 0;
-    // Note that these will be read back in the order listed due to
-    // short-circuit evaluation. This is important.
-    if (!iter.ReadInt(&scheme) || !ReadGURL(&iter, warn_only, &form->origin) ||
-        !ReadGURL(&iter, warn_only, &form->action) ||
-        !iter.ReadString16(&form->username_element) ||
-        !iter.ReadString16(&form->username_value) ||
-        !iter.ReadString16(&form->password_element) ||
-        !iter.ReadString16(&form->password_value) ||
-        !iter.ReadString16(&form->submit_element)) {
-      LogDeserializationWarning(version, signon_realm, warn_only);
-      return false;
-    }
-    if (version <= 8) {
-      bool dummy_unused_flag = false;
-      if (!iter.ReadBool(&dummy_unused_flag)) {
-        LogDeserializationWarning(version, signon_realm, warn_only);
-        return false;
-      }
-    }
-    if (!iter.ReadBool(&form->preferred) ||
-        !iter.ReadBool(&form->blacklisted_by_user) ||
-        !iter.ReadInt64(&date_created)) {
-      LogDeserializationWarning(version, signon_realm, warn_only);
-      return false;
-    }
-    form->scheme = static_cast<PasswordForm::Scheme>(scheme);
-
-    if (version > 1) {
-      if (!iter.ReadInt(&type) ||
-          !iter.ReadInt(&form->times_used) ||
-          !autofill::DeserializeFormData(&iter, &form->form_data)) {
-        LogDeserializationWarning(version, signon_realm, false);
-        return false;
-      }
-      form->type = static_cast<PasswordForm::Type>(type);
-    }
-
-    if (version > 2) {
-      int64_t date_synced = 0;
-      if (!iter.ReadInt64(&date_synced)) {
-        LogDeserializationWarning(version, signon_realm, false);
-        return false;
-      }
-      form->date_synced = base::Time::FromInternalValue(date_synced);
-    }
-
-    if (version > 3) {
-      if (!iter.ReadString16(&form->display_name) ||
-          !ReadGURL(&iter, warn_only, &form->icon_url) ||
-          !ReadOrigin(&iter, warn_only, &form->federation_origin) ||
-          !iter.ReadBool(&form->skip_zero_click)) {
-        LogDeserializationWarning(version, signon_realm, false);
-        return false;
-      }
-      if (version <= 7)
-        form->skip_zero_click = true;
-    }
-
-    if (version > 4) {
-      form->date_created = base::Time::FromInternalValue(date_created);
-    } else {
-      form->date_created = base::Time::FromTimeT(date_created);
-    }
-
-    if (version > 5) {
-      bool read_success = iter.ReadInt(&generation_upload_status);
-      if (!read_success && version > 6) {
-        // Valid version 6 pickles might still lack the
-        // generation_upload_status, see http://crbug.com/494229#c11.
-        LogDeserializationWarning(version, signon_realm, false);
-        return false;
-      }
-      if (read_success) {
-        form->generation_upload_status =
-            static_cast<PasswordForm::GenerationUploadStatus>(
-                generation_upload_status);
-      }
-    }
-
-    converted_forms.push_back(std::move(form));
-  }
-
-  forms->swap(converted_forms);
-  return true;
-}
-
-// Serializes a list of PasswordForms to be stored in the wallet.
-void SerializeValue(const std::vector<std::unique_ptr<PasswordForm>>& forms,
-                    base::Pickle* pickle) {
-  pickle->WriteInt(kPickleVersion);
-  pickle->WriteUInt64(forms.size());
-  for (const auto& form : forms) {
-    pickle->WriteInt(static_cast<int>(form->scheme));
-    pickle->WriteString(form->origin.spec());
-    pickle->WriteString(form->action.spec());
-    pickle->WriteString16(form->username_element);
-    pickle->WriteString16(form->username_value);
-    pickle->WriteString16(form->password_element);
-    pickle->WriteString16(form->password_value);
-    pickle->WriteString16(form->submit_element);
-    pickle->WriteBool(form->preferred);
-    pickle->WriteBool(form->blacklisted_by_user);
-    pickle->WriteInt64(form->date_created.ToInternalValue());
-    pickle->WriteInt(static_cast<int>(form->type));
-    pickle->WriteInt(form->times_used);
-    autofill::SerializeFormData(form->form_data, pickle);
-    pickle->WriteInt64(form->date_synced.ToInternalValue());
-    pickle->WriteString16(form->display_name);
-    pickle->WriteString(form->icon_url.spec());
-    // We serialize unique origins as "", in order to make other systems that
-    // read from the login database happy. https://crbug.com/591310
-    pickle->WriteString(form->federation_origin.opaque()
-                            ? std::string()
-                            : form->federation_origin.Serialize());
-    pickle->WriteBool(form->skip_zero_click);
-    pickle->WriteInt(static_cast<int>(form->generation_upload_status));
-  }
-}
-
-}  // namespace
-
-// Using USER_VISIBLE priority, because the passwords obtained through tasks on
-// the background runner influence what the user sees.
-NativeBackendKWallet::NativeBackendKWallet(
-    LocalProfileId id,
-    base::nix::DesktopEnvironment desktop_env)
-    : profile_id_(id),
-      kwallet_dbus_(desktop_env),
-      app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) {
-  folder_name_ = GetProfileSpecificFolderName();
-}
-
-NativeBackendKWallet::~NativeBackendKWallet() {
-  // This destructor is called on the thread that is destroying the Profile
-  // containing the PasswordStore that owns this NativeBackend. Generally that
-  // won't be run by the background task runner; it will be on the main one. So
-  // we post a message to shut it down on the background task runner, and it
-  // will be destroyed afterward when the scoped_refptr<dbus::Bus> goes out of
-  // scope. The NativeBackend will be destroyed before that occurs, but that's
-  // OK.
-  if (kwallet_dbus_.GetSessionBus()) {
-    dbus_thread_linux::GetTaskRunner()->PostTask(
-        FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock,
-                                  kwallet_dbus_.GetSessionBus()));
-  }
-}
-
-bool NativeBackendKWallet::Init() {
-  // Without the |optional_bus| parameter, a real bus will be instantiated.
-  return InitWithBus(scoped_refptr<dbus::Bus>());
-}
-
-bool NativeBackendKWallet::InitWithBus(scoped_refptr<dbus::Bus> optional_bus) {
-  // We must synchronously do a few DBus calls to figure out if initialization
-  // succeeds, but later, we'll want to do most of the work on the background
-  // task runner. So we have to do the initialization on the background task
-  // runner here too, and wait for it.
-  bool success = false;
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  // NativeBackendKWallet isn't reference counted, but we wait for InitWithBus
-  // to finish, so we can safely use base::Unretained here.
-  dbus_thread_linux::GetTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&NativeBackendKWallet::InitOnBackgroundTaskRunner,
-                     base::Unretained(this), optional_bus, &event, &success));
-
-  // This ScopedAllowBaseSyncPrimitives should not be here. However, the whole
-  // backend is so close to deprecation that it does not make sense to refactor
-  // it. More info on https://crbug.com/739897.
-  base::ScopedAllowBaseSyncPrimitives allow_wait;
-  event.Wait();
-  return success;
-}
-
-void NativeBackendKWallet::InitOnBackgroundTaskRunner(
-    scoped_refptr<dbus::Bus> optional_bus,
-    base::WaitableEvent* event,
-    bool* success) {
-  DCHECK(dbus_thread_linux::GetTaskRunner()->RunsTasksInCurrentSequence());
-  DCHECK(!kwallet_dbus_.GetSessionBus());
-  if (optional_bus.get()) {
-    // The optional_bus parameter is given when this method is called in tests.
-    kwallet_dbus_.SetSessionBus(optional_bus);
-  } else {
-    // Get a (real) connection to the session bus.
-    dbus::Bus::Options options;
-    options.bus_type = dbus::Bus::SESSION;
-    options.connection_type = dbus::Bus::PRIVATE;
-    kwallet_dbus_.SetSessionBus(new dbus::Bus(options));
-  }
-  // kwalletd may not be running. If we get a temporary failure initializing it,
-  // try to start it and then try again. (Note the short-circuit evaluation.)
-  const InitResult result = InitWallet();
-  *success = (result == INIT_SUCCESS ||
-              (result == TEMPORARY_FAIL && kwallet_dbus_.StartKWalletd() &&
-               InitWallet() == INIT_SUCCESS));
-  event->Signal();
-}
-
-NativeBackendKWallet::InitResult NativeBackendKWallet::InitWallet() {
-  DCHECK(dbus_thread_linux::GetTaskRunner()->RunsTasksInCurrentSequence());
-
-  // Check that KWallet is enabled.
-  bool enabled = false;
-  KWalletDBus::Error error = kwallet_dbus_.IsEnabled(&enabled);
-  switch (error) {
-    case KWalletDBus::Error::CANNOT_CONTACT:
-      return TEMPORARY_FAIL;
-    case KWalletDBus::Error::CANNOT_READ:
-      return PERMANENT_FAIL;
-    case KWalletDBus::Error::SUCCESS:
-      break;
-  }
-  if (!enabled)
-    return PERMANENT_FAIL;
-
-  // Get the wallet name.
-  error = kwallet_dbus_.NetworkWallet(&wallet_name_);
-  switch (error) {
-    case KWalletDBus::Error::CANNOT_CONTACT:
-      return TEMPORARY_FAIL;
-    case KWalletDBus::Error::CANNOT_READ:
-      return PERMANENT_FAIL;
-    case KWalletDBus::Error::SUCCESS:
-      return INIT_SUCCESS;
-  }
-
-  NOTREACHED();
-  return PERMANENT_FAIL;
-}
-
-password_manager::PasswordStoreChangeList NativeBackendKWallet::AddLogin(
-    const PasswordForm& form) {
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return password_manager::PasswordStoreChangeList();
-
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!GetLoginsList(form.signon_realm, wallet_handle, &forms))
-    return password_manager::PasswordStoreChangeList();
-
-  auto it = std::partition(
-      forms.begin(), forms.end(),
-      [&form](const std::unique_ptr<PasswordForm>& current_form) {
-        return !ArePasswordFormUniqueKeysEqual(form, *current_form);
-      });
-  password_manager::PasswordStoreChangeList changes;
-  if (it != forms.end()) {
-    // It's an update.
-    changes.push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::REMOVE, **it));
-    forms.erase(it, forms.end());
-  }
-
-  forms.push_back(std::make_unique<PasswordForm>(form));
-  changes.push_back(password_manager::PasswordStoreChange(
-      password_manager::PasswordStoreChange::ADD, form));
-
-  bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
-  if (!ok)
-    changes.clear();
-
-  return changes;
-}
-
-bool NativeBackendKWallet::UpdateLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(changes);
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!GetLoginsList(form.signon_realm, wallet_handle, &forms))
-    return false;
-
-  auto it = std::partition(
-      forms.begin(), forms.end(),
-      [&form](const std::unique_ptr<PasswordForm>& current_form) {
-        return !ArePasswordFormUniqueKeysEqual(form, *current_form);
-      });
-
-  if (it == forms.end())
-    return true;
-
-  forms.erase(it, forms.end());
-  forms.push_back(std::make_unique<PasswordForm>(form));
-  if (SetLoginsList(forms, form.signon_realm, wallet_handle)) {
-    changes->push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::UPDATE, form));
-    return true;
-  }
-
-  return false;
-}
-
-bool NativeBackendKWallet::RemoveLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(changes);
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetLoginsList(form.signon_realm, wallet_handle, &all_forms))
-    return false;
-
-  std::vector<std::unique_ptr<PasswordForm>> kept_forms;
-  kept_forms.reserve(all_forms.size());
-  for (std::unique_ptr<PasswordForm>& saved_form : all_forms) {
-    if (!ArePasswordFormUniqueKeysEqual(form, *saved_form)) {
-      kept_forms.push_back(std::move(saved_form));
-    }
-  }
-
-  if (kept_forms.size() != all_forms.size()) {
-    changes->push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::REMOVE, form));
-    return SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
-  }
-
-  return true;
-}
-
-bool NativeBackendKWallet::RemoveLoginsCreatedBetween(
-    base::Time delete_begin,
-    base::Time delete_end,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(changes);
-  changes->clear();
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-
-  // We could probably also use readEntryList here.
-  std::vector<std::string> realm_list;
-  KWalletDBus::Error error = kwallet_dbus_.EntryList(
-      wallet_handle, folder_name_, app_name_, &realm_list);
-  if (error)
-    return false;
-
-  bool ok = true;
-  for (const auto& signon_realm : realm_list) {
-    std::vector<uint8_t> bytes;
-    KWalletDBus::Error error = kwallet_dbus_.ReadEntry(
-        wallet_handle, folder_name_, signon_realm, app_name_, &bytes);
-    if (error)
-      continue;
-    if (bytes.empty() ||
-        !CheckSerializedValue(bytes.data(), bytes.size(), signon_realm)) {
-      continue;
-    }
-
-    // Can't we all just agree on whether bytes are signed or not? Please?
-    base::Pickle pickle(reinterpret_cast<const char*>(bytes.data()),
-                        bytes.size());
-    std::vector<std::unique_ptr<PasswordForm>> all_forms =
-        DeserializeValue(signon_realm, pickle);
-
-    std::vector<std::unique_ptr<PasswordForm>> kept_forms;
-    kept_forms.reserve(all_forms.size());
-    for (auto& saved_form : all_forms) {
-      if (delete_begin <= saved_form->date_created &&
-          (delete_end.is_null() || saved_form->date_created < delete_end)) {
-        changes->emplace_back(password_manager::PasswordStoreChange::REMOVE,
-                              *saved_form);
-      } else {
-        kept_forms.push_back(std::move(saved_form));
-      }
-    }
-
-    if (!SetLoginsList(kept_forms, signon_realm, wallet_handle)) {
-      ok = false;
-      changes->clear();
-    }
-  }
-  return ok;
-}
-
-bool NativeBackendKWallet::DisableAutoSignInForOrigins(
-    const base::Callback<bool(const GURL&)>& origin_filter,
-    password_manager::PasswordStoreChangeList* changes) {
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetAllLogins(&all_forms))
-    return false;
-
-  for (const std::unique_ptr<PasswordForm>& form : all_forms) {
-    if (origin_filter.Run(form->origin) && !form->skip_zero_click) {
-      form->skip_zero_click = true;
-      if (!UpdateLogin(*form, changes))
-        return false;
-    }
-  }
-  return true;
-}
-
-bool NativeBackendKWallet::GetLogins(
-    const password_manager::PasswordStore::FormDigest& form,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-  return GetLoginsList(form.signon_realm, wallet_handle, forms);
-}
-
-bool NativeBackendKWallet::GetAutofillableLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-  return GetLoginsList(BlacklistOptions::AUTOFILLABLE, wallet_handle, forms);
-}
-
-bool NativeBackendKWallet::GetBlacklistLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-  return GetLoginsList(BlacklistOptions::BLACKLISTED, wallet_handle, forms);
-}
-
-bool NativeBackendKWallet::GetAllLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-  return GetAllLoginsInternal(wallet_handle, forms);
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-NativeBackendKWallet::GetBackgroundTaskRunner() {
-  return dbus_thread_linux::GetTaskRunner();
-}
-
-bool NativeBackendKWallet::GetLoginsList(
-    const std::string& signon_realm,
-    int wallet_handle,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  forms->clear();
-  // Is there an entry in the wallet?
-  bool has_entry = false;
-  KWalletDBus::Error error = kwallet_dbus_.HasEntry(
-      wallet_handle, folder_name_, signon_realm, app_name_, &has_entry);
-  if (error)
-    return false;
-  if (!has_entry)
-    return true;
-
-  std::vector<uint8_t> bytes;
-  error = kwallet_dbus_.ReadEntry(wallet_handle, folder_name_, signon_realm,
-                                  app_name_, &bytes);
-  if (error)
-    return false;
-  if (!bytes.empty() &&
-      !CheckSerializedValue(bytes.data(), bytes.size(), signon_realm)) {
-    // This is weird, but we choose not to call it an error. There is an
-    // invalid entry somehow, but by just ignoring it, we make it easier to
-    // repair without having to delete it using kwalletmanager (that is, by
-    // just saving a new password within this realm to overwrite it).
-    return true;
-  }
-
-  // Can't we all just agree on whether bytes are signed or not? Please?
-  base::Pickle pickle(reinterpret_cast<const char*>(bytes.data()),
-                      bytes.size());
-  *forms = DeserializeValue(signon_realm, pickle);
-
-  return true;
-}
-
-bool NativeBackendKWallet::GetLoginsList(
-    BlacklistOptions options,
-    int wallet_handle,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  forms->clear();
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetAllLoginsInternal(wallet_handle, &all_forms))
-    return false;
-
-  // Remove the duplicate sync tags.
-  std::vector<std::unique_ptr<PasswordForm>> duplicates;
-  password_manager_util::FindDuplicates(&all_forms, &duplicates, nullptr);
-  if (!duplicates.empty()) {
-    // Fill the signon realms to be updated.
-    std::map<std::string, std::vector<std::unique_ptr<PasswordForm>>>
-        update_forms;
-    for (const auto& form : duplicates) {
-      update_forms.insert(std::make_pair(
-          form->signon_realm, std::vector<std::unique_ptr<PasswordForm>>()));
-    }
-
-    // Fill the actual forms to be saved.
-    for (const auto& form : all_forms) {
-      auto it = update_forms.find(form->signon_realm);
-      if (it != update_forms.end())
-        it->second.push_back(std::make_unique<PasswordForm>(*form));
-    }
-
-    // Update the backend.
-    for (const auto& update_forms_for_realm : update_forms) {
-      if (!SetLoginsList(update_forms_for_realm.second,
-                         update_forms_for_realm.first, wallet_handle)) {
-        return false;
-      }
-    }
-  }
-  // We have to read all the entries, and then filter them here.
-  forms->reserve(all_forms.size());
-  for (std::unique_ptr<PasswordForm>& saved_form : all_forms) {
-    if (saved_form->blacklisted_by_user ==
-        (options == BlacklistOptions::BLACKLISTED)) {
-      forms->push_back(std::move(saved_form));
-    }
-  }
-
-  return true;
-}
-
-bool NativeBackendKWallet::GetAllLoginsInternal(
-    int wallet_handle,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  // We could probably also use readEntryList here.
-  std::vector<std::string> realm_list;
-  KWalletDBus::Error error = kwallet_dbus_.EntryList(
-      wallet_handle, folder_name_, app_name_, &realm_list);
-  if (error)
-    return false;
-
-  forms->clear();
-  for (const std::string& signon_realm : realm_list) {
-    std::vector<uint8_t> bytes;
-    KWalletDBus::Error error = kwallet_dbus_.ReadEntry(
-        wallet_handle, folder_name_, signon_realm, app_name_, &bytes);
-    if (error)
-      return false;
-    if (bytes.empty() ||
-        !CheckSerializedValue(bytes.data(), bytes.size(), signon_realm))
-      continue;
-
-    // Can't we all just agree on whether bytes are signed or not? Please?
-    base::Pickle pickle(reinterpret_cast<const char*>(bytes.data()),
-                        bytes.size());
-    std::vector<std::unique_ptr<PasswordForm>> from_pickle =
-        DeserializeValue(signon_realm, pickle);
-    forms->reserve(forms->size() + from_pickle.size());
-    std::move(from_pickle.begin(), from_pickle.end(),
-              std::back_inserter(*forms));
-  }
-  return true;
-}
-
-bool NativeBackendKWallet::SetLoginsList(
-    const std::vector<std::unique_ptr<PasswordForm>>& forms,
-    const std::string& signon_realm,
-    int wallet_handle) {
-  if (forms.empty()) {
-    int ret = 0;
-    KWalletDBus::Error error = kwallet_dbus_.RemoveEntry(
-        wallet_handle, folder_name_, signon_realm, app_name_, &ret);
-    if (error)
-      return false;
-    if (ret != 0)
-      LOG(ERROR) << "Bad return code " << ret << " from KWallet removeEntry";
-    return ret == 0;
-  }
-
-  base::Pickle value;
-  SerializeValue(forms, &value);
-
-  int ret = 0;
-  KWalletDBus::Error error = kwallet_dbus_.WriteEntry(
-      wallet_handle, folder_name_, signon_realm, app_name_,
-      static_cast<const uint8_t*>(value.data()), value.size(), &ret);
-  if (error)
-    return false;
-  if (ret != 0)
-    LOG(ERROR) << "Bad return code " << ret << " from KWallet writeEntry";
-  return ret == 0;
-}
-
-// static
-std::vector<std::unique_ptr<PasswordForm>>
-NativeBackendKWallet::DeserializeValue(const std::string& signon_realm,
-                                       const base::Pickle& pickle) {
-  base::PickleIterator iter(pickle);
-
-  int version = -1;
-  if (!iter.ReadInt(&version) ||
-      version < 0 || version > kPickleVersion) {
-    LOG(ERROR) << "Failed to deserialize KWallet entry "
-               << "(realm: " << signon_realm << ")";
-    return std::vector<std::unique_ptr<PasswordForm>>();
-  }
-
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  bool success = true;
-  if (version > 0) {
-    // In current pickles, we expect 64-bit sizes. Failure is an error.
-    success =
-        DeserializeValueSize(signon_realm, iter, version, false, false, &forms);
-    return forms;
-  }
-
-  const bool size_32 = sizeof(size_t) == sizeof(uint32_t);
-  if (!DeserializeValueSize(
-          signon_realm, iter, version, size_32, true, &forms)) {
-    // We failed to read the pickle using the native architecture of the system.
-    // Try again with the opposite architecture. Note that we do this even on
-    // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare,
-    // since mostly we expect upgrades, not downgrades, but both are possible.)
-    success = DeserializeValueSize(
-        signon_realm, iter, version, !size_32, false, &forms);
-  }
-  return forms;
-}
-
-int NativeBackendKWallet::WalletHandle() {
-  DCHECK(dbus_thread_linux::GetTaskRunner()->RunsTasksInCurrentSequence());
-
-  // Open the wallet.
-  // TODO(mdm): Are we leaking these handles? Find out.
-  int32_t handle = kInvalidKWalletHandle;
-  KWalletDBus::Error error =
-      kwallet_dbus_.Open(wallet_name_, app_name_, &handle);
-  if (error)
-    return kInvalidKWalletHandle;
-  if (handle == kInvalidKWalletHandle) {
-    LOG(ERROR) << "Error obtaining KWallet handle";
-    return kInvalidKWalletHandle;
-  }
-
-  // Check if our folder exists.
-  bool has_folder = false;
-  error = kwallet_dbus_.HasFolder(handle, folder_name_, app_name_, &has_folder);
-  if (error)
-    return kInvalidKWalletHandle;
-
-  // Create it if it didn't.
-  if (!has_folder) {
-    bool success = false;
-    error =
-        kwallet_dbus_.CreateFolder(handle, folder_name_, app_name_, &success);
-    if (error)
-      return kInvalidKWalletHandle;
-    if (!success) {
-      LOG(ERROR) << "Error creating KWallet folder";
-      return kInvalidKWalletHandle;
-    }
-  }
-
-  return handle;
-}
-
-std::string NativeBackendKWallet::GetProfileSpecificFolderName() const {
-  // Originally, the folder name was always just "Chrome Form Data".
-  // Now we use it to distinguish passwords for different profiles.
-  return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_);
-}
diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.h b/chrome/browser/password_manager/native_backend_kwallet_x.h
deleted file mode 100644
index b26479b..0000000
--- a/chrome/browser/password_manager/native_backend_kwallet_x.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/nix/xdg_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/password_store_factory.h"
-#include "chrome/browser/password_manager/password_store_x.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/os_crypt/kwallet_dbus.h"
-
-namespace autofill {
-struct PasswordForm;
-}
-
-namespace base {
-class Pickle;
-class WaitableEvent;
-}
-
-// NativeBackend implementation using KWallet.
-class NativeBackendKWallet : public PasswordStoreX::NativeBackend {
- public:
-  NativeBackendKWallet(LocalProfileId id,
-                       base::nix::DesktopEnvironment desktop_env);
-
-  ~NativeBackendKWallet() override;
-
-  bool Init() override;
-
-  // Implements NativeBackend interface.
-  password_manager::PasswordStoreChangeList AddLogin(
-      const autofill::PasswordForm& form) override;
-  bool UpdateLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLoginsCreatedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool DisableAutoSignInForOrigins(
-      const base::Callback<bool(const GURL&)>& origin_filter,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool GetLogins(
-      const password_manager::PasswordStore::FormDigest& form,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAutofillableLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetBlacklistLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAllLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override;
-
- protected:
-  // Invalid handle returned by WalletHandle().
-  static const int kInvalidKWalletHandle = -1;
-
-  // Internally used by Init(), but also for testing to provide a mock bus.
-  bool InitWithBus(scoped_refptr<dbus::Bus> optional_bus);
-
-  // Deserializes a list of PasswordForms from the wallet.
-  static std::vector<std::unique_ptr<autofill::PasswordForm>> DeserializeValue(
-      const std::string& signon_realm,
-      const base::Pickle& pickle);
-
- private:
-  enum InitResult {
-    INIT_SUCCESS,    // Init succeeded.
-    TEMPORARY_FAIL,  // Init failed, but might succeed after StartKWalletd().
-    PERMANENT_FAIL   // Init failed, and is not likely to work later either.
-  };
-
-  enum class BlacklistOptions { AUTOFILLABLE, BLACKLISTED };
-
-  // Initialization.
-  InitResult InitWallet();
-  void InitOnBackgroundTaskRunner(scoped_refptr<dbus::Bus> optional_bus,
-                                  base::WaitableEvent* event,
-                                  bool* success);
-
-  // Overwrites |forms| with all credentials matching |signon_realm|. Returns
-  // true on success.
-  bool GetLoginsList(const std::string& signon_realm,
-                     int wallet_handle,
-                     std::vector<std::unique_ptr<autofill::PasswordForm>>*
-                         forms) WARN_UNUSED_RESULT;
-
-  // Overwrites |forms| with all credentials matching |options|. Returns true on
-  // success.
-  bool GetLoginsList(BlacklistOptions options,
-                     int wallet_handle,
-                     std::vector<std::unique_ptr<autofill::PasswordForm>>*
-                         forms) WARN_UNUSED_RESULT;
-
-  // Overwrites |forms| with all stored credentials. Returns true on success.
-  bool GetAllLoginsInternal(
-      int wallet_handle,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms)
-      WARN_UNUSED_RESULT;
-
-  // Writes a list of PasswordForms to the wallet with the given signon_realm.
-  // Overwrites any existing list for this signon_realm. Removes the entry if
-  // |forms| is empty. Returns true on success.
-  bool SetLoginsList(
-      const std::vector<std::unique_ptr<autofill::PasswordForm>>& forms,
-      const std::string& signon_realm,
-      int wallet_handle);
-
-  // Opens the wallet and ensures that the "Chrome Form Data" folder exists.
-  // Returns kInvalidWalletHandle on error.
-  int WalletHandle();
-
-  // Generates a profile-specific folder name based on profile_id_.
-  std::string GetProfileSpecificFolderName() const;
-
-  // The local profile id, used to generate the folder name.
-  const LocalProfileId profile_id_;
-
-  KWalletDBus kwallet_dbus_;
-
-  // The KWallet folder name, possibly based on the local profile id.
-  std::string folder_name_;
-
-  // The name of the wallet we've opened. Set during Init().
-  std::string wallet_name_;
-  // The application name (e.g. "Chromium"), shown in KWallet auth dialogs.
-  const std::string app_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeBackendKWallet);
-};
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
diff --git a/chrome/browser/password_manager/native_backend_kwallet_x_unittest.cc b/chrome/browser/password_manager/native_backend_kwallet_x_unittest.cc
deleted file mode 100644
index e5c01423..0000000
--- a/chrome/browser/password_manager/native_backend_kwallet_x_unittest.cc
+++ /dev/null
@@ -1,1382 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <map>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/location.h"
-#include "base/pickle.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner_util.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "dbus/message.h"
-#include "dbus/mock_bus.h"
-#include "dbus/mock_object_proxy.h"
-#include "dbus/object_path.h"
-#include "dbus/object_proxy.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using autofill::PasswordForm;
-using base::UTF8ToUTF16;
-using password_manager::PasswordStoreChange;
-using password_manager::PasswordStoreChangeList;
-using testing::_;
-using testing::Invoke;
-using testing::Pointee;
-using testing::Return;
-using testing::TestWithParam;
-using testing::UnorderedElementsAre;
-using testing::Values;
-
-namespace {
-
-// This class implements a very simple version of KWallet in memory.
-// We only provide the parts we actually use; the real version has more.
-class TestKWallet {
- public:
-  typedef std::basic_string<uint8_t> Blob;  // std::string is binary-safe.
-
-  TestKWallet() : reject_local_folders_(false) {}
-
-  void set_reject_local_folders(bool value) { reject_local_folders_ = value; }
-
-  // NOTE: The method names here are the same as the corresponding DBus
-  // methods, and therefore have names that don't match our style guide.
-
-  // Check for presence of a given password folder.
-  bool hasFolder(const std::string& folder) const {
-    return data_.find(folder) != data_.end();
-  }
-
-  // Check for presence of a given password in a given password folder.
-  bool hasEntry(const std::string& folder, const std::string& key) const {
-    auto it = data_.find(folder);
-    return it != data_.end() && it->second.find(key) != it->second.end();
-  }
-
-  // Get a list of password keys in a given password folder.
-  bool entryList(const std::string& folder,
-                 std::vector<std::string>* entries) const {
-    auto it = data_.find(folder);
-    if (it == data_.end()) return false;
-    for (auto fit = it->second.begin(); fit != it->second.end(); ++fit)
-      entries->push_back(fit->first);
-    return true;
-  }
-
-  // Read the password data for a given password in a given password folder.
-  bool readEntry(const std::string& folder, const std::string& key,
-                 Blob* value) const {
-    auto it = data_.find(folder);
-    if (it == data_.end()) return false;
-    auto fit = it->second.find(key);
-    if (fit == it->second.end()) return false;
-    *value = fit->second;
-    return true;
-  }
-
-  // Create the given password folder.
-  bool createFolder(const std::string& folder) {
-    if (reject_local_folders_ && folder.find('(') != std::string::npos)
-      return false;
-    return data_.insert(make_pair(folder, Folder())).second;
-  }
-
-  // Remove the given password from the given password folder.
-  bool removeEntry(const std::string& folder, const std::string& key) {
-    auto it = data_.find(folder);
-    if (it == data_.end()) return false;
-    return it->second.erase(key) > 0;
-  }
-
-  // Write the given password data to the given password folder.
-  bool writeEntry(const std::string& folder, const std::string& key,
-                  const Blob& value) {
-    auto it = data_.find(folder);
-    if (it == data_.end()) return false;
-    it->second[key] = value;
-    return true;
-  }
-
- private:
-  typedef std::map<std::string, Blob> Folder;
-  typedef std::map<std::string, Folder> Data;
-
-  Data data_;
-  // "Local" folders are folders containing local profile IDs in their names. We
-  // can reject attempts to create them in order to make it easier to create
-  // legacy shared passwords in these tests, for testing the migration code.
-  bool reject_local_folders_;
-
-  // No need to disallow copy and assign. This class is safe to copy and assign.
-};
-
-// Runs |backend->GetAutofillableLogins(forms)| and expects that the return
-// value is false.
-void CheckGetAutofillableLoginsFails(
-    PasswordStoreX::NativeBackend* backend,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  EXPECT_FALSE(backend->GetAutofillableLogins(forms));
-}
-
-void CheckTrue(bool result) {
-  EXPECT_TRUE(result);
-}
-
-void WriteHTMLAttributes(const PasswordForm& form, base::Pickle* pickle) {
-  pickle->WriteInt(static_cast<int>(form.scheme));
-  pickle->WriteString(form.origin.spec());
-  pickle->WriteString(form.action.spec());
-  pickle->WriteString16(form.username_element);
-  pickle->WriteString16(form.username_value);
-  pickle->WriteString16(form.password_element);
-  pickle->WriteString16(form.password_value);
-  pickle->WriteString16(form.submit_element);
-}
-
-void WritePreferenceMetadata(const PasswordForm& form, base::Pickle* pickle) {
-  pickle->WriteBool(form.preferred);
-  pickle->WriteBool(form.blacklisted_by_user);
-}
-
-}  // anonymous namespace
-
-// Obscure magic: we need to declare storage for this constant because we use it
-// in ways that require its address in this test, but not in the actual code.
-const int NativeBackendKWallet::kInvalidKWalletHandle;
-
-// Subclass NativeBackendKWallet to promote some members to public for testing.
-class NativeBackendKWalletStub : public NativeBackendKWallet {
- public:
-  NativeBackendKWalletStub(LocalProfileId id,
-                           base::nix::DesktopEnvironment desktop_env)
-      : NativeBackendKWallet(id, desktop_env) {}
-  using NativeBackendKWallet::InitWithBus;
-  using NativeBackendKWallet::kInvalidKWalletHandle;
-  using NativeBackendKWallet::DeserializeValue;
-};
-
-// Provide some test forms to avoid having to set them up in each test.
-class NativeBackendKWalletTestBase :
-    public testing::TestWithParam<base::nix::DesktopEnvironment> {
- protected:
-  NativeBackendKWalletTestBase() {
-    old_form_google_.origin = GURL("http://www.google.com/");
-    old_form_google_.action = GURL("http://www.google.com/login");
-    old_form_google_.username_element = UTF8ToUTF16("user");
-    old_form_google_.username_value = UTF8ToUTF16("joeschmoe");
-    old_form_google_.password_element = UTF8ToUTF16("pass");
-    old_form_google_.password_value = UTF8ToUTF16("seekrit");
-    old_form_google_.submit_element = UTF8ToUTF16("submit");
-    old_form_google_.signon_realm = "Google";
-    old_form_google_.date_created = base::Time::Now();
-
-    form_google_ = old_form_google_;
-    form_google_.times_used = 3;
-    form_google_.type = PasswordForm::Type::kGenerated;
-    form_google_.form_data.name = UTF8ToUTF16("form_name");
-    form_google_.date_synced = base::Time::Now();
-    form_google_.date_created = old_form_google_.date_created;
-    form_google_.display_name = UTF8ToUTF16("Joe Schmoe");
-    form_google_.icon_url = GURL("http://www.google.com/icon");
-    form_google_.federation_origin =
-        url::Origin::Create(GURL("http://www.google.com/"));
-    form_google_.skip_zero_click = true;
-    form_google_.generation_upload_status =
-        PasswordForm::GenerationUploadStatus::kNegativeSignalSent;
-
-    form_isc_.origin = GURL("http://www.isc.org/");
-    form_isc_.action = GURL("http://www.isc.org/auth");
-    form_isc_.username_element = UTF8ToUTF16("id");
-    form_isc_.username_value = UTF8ToUTF16("janedoe");
-    form_isc_.password_element = UTF8ToUTF16("passwd");
-    form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
-    form_isc_.submit_element = UTF8ToUTF16("login");
-    form_isc_.signon_realm = "ISC";
-    form_isc_.date_synced = base::Time::Now();
-    form_isc_.date_created = base::Time::Now();
-  }
-
-  static void CheckPasswordForm(const PasswordForm& expected,
-                                const PasswordForm& actual,
-                                bool check_date_created);
-  static void CheckPasswordChanges(const PasswordStoreChangeList& expected,
-                                   const PasswordStoreChangeList& actual);
-  static void CheckPasswordChangesWithResult(
-      const PasswordStoreChangeList* expected,
-      const PasswordStoreChangeList* actual,
-      bool result);
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  PasswordForm old_form_google_;
-  PasswordForm form_google_;
-  PasswordForm form_isc_;
-};
-
-// static
-void NativeBackendKWalletTestBase::CheckPasswordForm(
-    const PasswordForm& expected,
-    const PasswordForm& actual,
-    bool check_date_created) {
-  EXPECT_EQ(expected.origin, actual.origin);
-  EXPECT_EQ(expected.password_value, actual.password_value);
-  EXPECT_EQ(expected.action, actual.action);
-  EXPECT_EQ(expected.username_element, actual.username_element);
-  EXPECT_EQ(expected.username_value, actual.username_value);
-  EXPECT_EQ(expected.password_element, actual.password_element);
-  EXPECT_EQ(expected.submit_element, actual.submit_element);
-  EXPECT_EQ(expected.signon_realm, actual.signon_realm);
-  EXPECT_EQ(expected.preferred, actual.preferred);
-  if (check_date_created) {
-    EXPECT_EQ(expected.date_created, actual.date_created);
-  }
-  EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
-  EXPECT_EQ(expected.type, actual.type);
-  EXPECT_EQ(expected.times_used, actual.times_used);
-  EXPECT_EQ(expected.scheme, actual.scheme);
-  EXPECT_EQ(expected.date_synced, actual.date_synced);
-  EXPECT_EQ(expected.display_name, actual.display_name);
-  EXPECT_EQ(expected.icon_url, actual.icon_url);
-  EXPECT_EQ(expected.federation_origin.Serialize(),
-            actual.federation_origin.Serialize());
-  EXPECT_EQ(expected.skip_zero_click, actual.skip_zero_click);
-  EXPECT_EQ(expected.generation_upload_status, actual.generation_upload_status);
-}
-
-// static
-void NativeBackendKWalletTestBase::CheckPasswordChanges(
-    const PasswordStoreChangeList& expected,
-    const PasswordStoreChangeList& actual) {
-  ASSERT_EQ(expected.size(), actual.size());
-  for (size_t i = 0; i < expected.size(); ++i) {
-    EXPECT_EQ(expected[i].type(), actual[i].type());
-    CheckPasswordForm(expected[i].form(), actual[i].form(), true);
-  }
-}
-
-// static
-void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult(
-    const PasswordStoreChangeList* expected,
-    const PasswordStoreChangeList* actual,
-    bool result) {
-  EXPECT_TRUE(result);
-  CheckPasswordChanges(*expected, *actual);
-}
-
-class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
- protected:
-  NativeBackendKWalletTest()
-      : klauncher_ret_(0),
-        klauncher_contacted_(false),
-        kwallet_runnable_(true),
-        kwallet_running_(true),
-        kwallet_enabled_(true),
-        desktop_env_(GetParam()) {}
-
-  void SetUp() override;
-
-  // Utilities to help verify sets of expectations.
-  typedef std::vector<
-              std::pair<std::string,
-                        std::vector<const PasswordForm*> > > ExpectationArray;
-  void CheckPasswordForms(const std::string& folder,
-                          const ExpectationArray& sorted_expected);
-
-  scoped_refptr<dbus::MockBus> mock_session_bus_;
-  scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_;
-  scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_;
-
-  int klauncher_ret_;
-  std::string klauncher_error_;
-  bool klauncher_contacted_;
-
-  bool kwallet_runnable_;
-  bool kwallet_running_;
-  bool kwallet_enabled_;
-
-  // Used for switching between kwalletd and kwalletd5
-  base::nix::DesktopEnvironment desktop_env_;
-
-  TestKWallet wallet_;
-
-  // For all method names contained in |failing_methods_|, the mocked KWallet
-  // will return a null response.
-  std::set<std::string> failing_methods_;
-
- private:
-  std::unique_ptr<dbus::Response> KLauncherMethodCall(
-      dbus::MethodCall* method_call, testing::Unused);
-
-  std::unique_ptr<dbus::Response> KWalletMethodCall(
-      dbus::MethodCall* method_call, testing::Unused);
-};
-
-void NativeBackendKWalletTest::SetUp() {
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SESSION;
-  mock_session_bus_ = new dbus::MockBus(options);
-
-  mock_klauncher_proxy_ =
-      new dbus::MockObjectProxy(mock_session_bus_.get(),
-                                "org.kde.klauncher",
-                                dbus::ObjectPath("/KLauncher"));
-  EXPECT_CALL(*mock_klauncher_proxy_.get(), CallMethodAndBlock(_, _))
-      .WillRepeatedly(
-           Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
-
-  if (desktop_env_ == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
-    mock_kwallet_proxy_ =
-        new dbus::MockObjectProxy(mock_session_bus_.get(),
-                                  "org.kde.kwalletd5",
-                                  dbus::ObjectPath("/modules/kwalletd5"));
-  } else {
-    mock_kwallet_proxy_ =
-        new dbus::MockObjectProxy(mock_session_bus_.get(),
-                                  "org.kde.kwalletd",
-                                  dbus::ObjectPath("/modules/kwalletd"));
-  }
-  EXPECT_CALL(*mock_kwallet_proxy_.get(), CallMethodAndBlock(_, _))
-      .WillRepeatedly(
-           Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
-
-  EXPECT_CALL(
-      *mock_session_bus_.get(),
-      GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
-      .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
-  if (desktop_env_ == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
-    EXPECT_CALL(
-        *mock_session_bus_.get(),
-        GetObjectProxy("org.kde.kwalletd5",
-                       dbus::ObjectPath("/modules/kwalletd5")))
-        .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
-  } else {
-    EXPECT_CALL(
-        *mock_session_bus_.get(),
-        GetObjectProxy("org.kde.kwalletd",
-                       dbus::ObjectPath("/modules/kwalletd")))
-        .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
-  }
-
-  EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return())
-      .WillRepeatedly(Return());
-}
-
-std::unique_ptr<dbus::Response> NativeBackendKWalletTest::KLauncherMethodCall(
-    dbus::MethodCall* method_call, testing::Unused) {
-  EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface());
-  EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember());
-
-  klauncher_contacted_ = true;
-
-  dbus::MessageReader reader(method_call);
-  std::string service_name;
-  std::vector<std::string> urls;
-  std::vector<std::string> envs;
-  std::string startup_id;
-  bool blind = false;
-
-  EXPECT_TRUE(reader.PopString(&service_name));
-  EXPECT_TRUE(reader.PopArrayOfStrings(&urls));
-  EXPECT_TRUE(reader.PopArrayOfStrings(&envs));
-  EXPECT_TRUE(reader.PopString(&startup_id));
-  EXPECT_TRUE(reader.PopBool(&blind));
-
-  if (desktop_env_ == base::nix::DESKTOP_ENVIRONMENT_KDE5)
-    EXPECT_EQ("kwalletd5", service_name);
-  else
-    EXPECT_EQ("kwalletd", service_name);
-  EXPECT_TRUE(urls.empty());
-  EXPECT_TRUE(envs.empty());
-  EXPECT_TRUE(startup_id.empty());
-  EXPECT_FALSE(blind);
-
-  if (kwallet_runnable_)
-    kwallet_running_ = true;
-
-  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
-  dbus::MessageWriter writer(response.get());
-  writer.AppendInt32(klauncher_ret_);
-  writer.AppendString(std::string());  // dbus_name
-  writer.AppendString(klauncher_error_);
-  writer.AppendInt32(1234);  // pid
-  return response;
-}
-
-std::unique_ptr<dbus::Response> NativeBackendKWalletTest::KWalletMethodCall(
-    dbus::MethodCall* method_call, testing::Unused) {
-  if (!kwallet_running_)
-    return nullptr;
-  EXPECT_EQ("org.kde.KWallet", method_call->GetInterface());
-
-  if (base::Contains(failing_methods_, method_call->GetMember()))
-    return nullptr;
-  std::unique_ptr<dbus::Response> response;
-  if (method_call->GetMember() == "isEnabled") {
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    writer.AppendBool(kwallet_enabled_);
-  } else if (method_call->GetMember() == "networkWallet") {
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    writer.AppendString("test_wallet");  // Should match |open| below.
-  } else if (method_call->GetMember() == "open") {
-    dbus::MessageReader reader(method_call);
-    std::string wallet_name;
-    int64_t wallet_id;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopString(&wallet_name));
-    EXPECT_TRUE(reader.PopInt64(&wallet_id));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_EQ("test_wallet", wallet_name);  // Should match |networkWallet|.
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    writer.AppendInt32(1);  // Can be anything but kInvalidKWalletHandle.
-  } else if (method_call->GetMember() == "hasFolder" ||
-             method_call->GetMember() == "createFolder") {
-    dbus::MessageReader reader(method_call);
-    int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
-    std::string folder_name;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopInt32(&handle));
-    EXPECT_TRUE(reader.PopString(&folder_name));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    if (method_call->GetMember() == "hasFolder")
-      writer.AppendBool(wallet_.hasFolder(folder_name));
-    else
-      writer.AppendBool(wallet_.createFolder(folder_name));
-  } else if (method_call->GetMember() == "hasEntry" ||
-             method_call->GetMember() == "removeEntry") {
-    dbus::MessageReader reader(method_call);
-    int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
-    std::string folder_name;
-    std::string key;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopInt32(&handle));
-    EXPECT_TRUE(reader.PopString(&folder_name));
-    EXPECT_TRUE(reader.PopString(&key));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    if (method_call->GetMember() == "hasEntry")
-      writer.AppendBool(wallet_.hasEntry(folder_name, key));
-    else
-      writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1);
-  } else if (method_call->GetMember() == "entryList") {
-    dbus::MessageReader reader(method_call);
-    int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
-    std::string folder_name;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopInt32(&handle));
-    EXPECT_TRUE(reader.PopString(&folder_name));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
-    std::vector<std::string> entries;
-    if (wallet_.entryList(folder_name, &entries)) {
-      response = dbus::Response::CreateEmpty();
-      dbus::MessageWriter writer(response.get());
-      writer.AppendArrayOfStrings(entries);
-    }
-  } else if (method_call->GetMember() == "readEntry") {
-    dbus::MessageReader reader(method_call);
-    int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
-    std::string folder_name;
-    std::string key;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopInt32(&handle));
-    EXPECT_TRUE(reader.PopString(&folder_name));
-    EXPECT_TRUE(reader.PopString(&key));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
-    TestKWallet::Blob value;
-    if (wallet_.readEntry(folder_name, key, &value)) {
-      response = dbus::Response::CreateEmpty();
-      dbus::MessageWriter writer(response.get());
-      writer.AppendArrayOfBytes(value.data(), value.size());
-    }
-  } else if (method_call->GetMember() == "writeEntry") {
-    dbus::MessageReader reader(method_call);
-    int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
-    std::string folder_name;
-    std::string key;
-    const uint8_t* bytes = nullptr;
-    size_t length = 0;
-    std::string app_name;
-    EXPECT_TRUE(reader.PopInt32(&handle));
-    EXPECT_TRUE(reader.PopString(&folder_name));
-    EXPECT_TRUE(reader.PopString(&key));
-    EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
-    EXPECT_TRUE(reader.PopString(&app_name));
-    EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
-    response = dbus::Response::CreateEmpty();
-    dbus::MessageWriter writer(response.get());
-    writer.AppendInt32(
-        wallet_.writeEntry(folder_name, key,
-                           TestKWallet::Blob(bytes, length)) ? 0 : 1);
-  }
-
-  EXPECT_TRUE(response);
-  return response;
-}
-
-void NativeBackendKWalletTest::CheckPasswordForms(
-    const std::string& folder, const ExpectationArray& sorted_expected) {
-  EXPECT_TRUE(wallet_.hasFolder(folder));
-  std::vector<std::string> entries;
-  EXPECT_TRUE(wallet_.entryList(folder, &entries));
-  EXPECT_EQ(sorted_expected.size(), entries.size());
-  std::sort(entries.begin(), entries.end());
-  for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) {
-    EXPECT_EQ(sorted_expected[i].first, entries[i]);
-    TestKWallet::Blob value;
-    EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value));
-    base::Pickle pickle(reinterpret_cast<const char*>(value.data()),
-                        value.size());
-    std::vector<std::unique_ptr<PasswordForm>> forms =
-        NativeBackendKWalletStub::DeserializeValue(entries[i], pickle);
-    const std::vector<const PasswordForm*>& expect = sorted_expected[i].second;
-    EXPECT_EQ(expect.size(), forms.size());
-    for (size_t j = 0; j < forms.size() && j < expect.size(); ++j)
-      CheckPasswordForm(*expect[j], *forms[j], true);
-  }
-}
-
-TEST_P(NativeBackendKWalletTest, NotEnabled) {
-  NativeBackendKWalletStub kwallet(42, desktop_env_);
-  kwallet_enabled_ = false;
-  EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
-  EXPECT_FALSE(klauncher_contacted_);
-}
-
-TEST_P(NativeBackendKWalletTest, NotRunnable) {
-  NativeBackendKWalletStub kwallet(42, desktop_env_);
-  kwallet_runnable_ = false;
-  kwallet_running_ = false;
-  EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
-  EXPECT_TRUE(klauncher_contacted_);
-}
-
-TEST_P(NativeBackendKWalletTest, NotRunningOrEnabled) {
-  NativeBackendKWalletStub kwallet(42, desktop_env_);
-  kwallet_running_ = false;
-  kwallet_enabled_ = false;
-  EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
-  EXPECT_TRUE(klauncher_contacted_);
-}
-
-TEST_P(NativeBackendKWalletTest, NotRunning) {
-  NativeBackendKWalletStub kwallet(42, desktop_env_);
-  kwallet_running_ = false;
-  EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
-  EXPECT_TRUE(klauncher_contacted_);
-}
-
-TEST_P(NativeBackendKWalletTest, BasicStartup) {
-  NativeBackendKWalletStub kwallet(42, desktop_env_);
-  EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
-  EXPECT_FALSE(klauncher_contacted_);
-}
-
-TEST_P(NativeBackendKWalletTest, BasicAddLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::AddLogin,
-                 base::Unretained(&backend), form_google_),
-      base::Bind(
-          &CheckPasswordChanges,
-          PasswordStoreChangeList(
-              1, PasswordStoreChange(PasswordStoreChange::ADD, form_google_))));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, BasicUpdateLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  PasswordForm new_form_google(form_google_);
-  new_form_google.times_used = 10;
-  new_form_google.action = GURL("http://www.google.com/different/login");
-
-  // Update login
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes(
-      1, PasswordStoreChange(PasswordStoreChange::UPDATE, new_form_google));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::UpdateLogin,
-                 base::Unretained(&backend), new_form_google, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  ASSERT_EQ(1u, changes.size());
-  EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
-  EXPECT_EQ(new_form_google, changes.front().form());
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&new_form_google);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, BasicListLogins) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  // Quick check that we got something back.
-  EXPECT_EQ(1u, form_list.size());
-
-  EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, BasicRemoveLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-
-  PasswordStoreChangeList changes;
-  PasswordStoreChangeList expected_changes(
-      1, PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::RemoveLogin,
-                 base::Unretained(&backend), form_google_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  expected.clear();
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, UpdateNonexistentLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  // First add an unrelated login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-
-  // Attempt to update a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::UpdateLogin,
-                 base::Unretained(&backend), form_isc_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult,
-                 base::Owned(new PasswordStoreChangeList), &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_EQ(PasswordStoreChangeList(), changes);
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, RemoveNonexistentLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  // First add an unrelated login.
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-
-  // Attempt to remove a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::RemoveLogin,
-                 base::Unretained(&backend), form_isc_, &changes),
-      base::Bind(&CheckPasswordChangesWithResult,
-                 base::Owned(new PasswordStoreChangeList), &changes));
-
-  // Make sure we can still get the first form back.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  // Quick check that we got something back.
-  EXPECT_EQ(1u, form_list.size());
-
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, AddDuplicateLogin) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  PasswordStoreChangeList changes;
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
-                                        form_google_));
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::AddLogin,
-                 base::Unretained(&backend), form_google_),
-      base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges, changes));
-
-  changes.clear();
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
-                                        form_google_));
-  form_google_.times_used++;
-  form_google_.submit_element = UTF8ToUTF16("submit2");
-  changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
-                                        form_google_));
-
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::AddLogin,
-                 base::Unretained(&backend), form_google_),
-      base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges, changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, AndroidCredentials) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  PasswordForm saved_android_form;
-  saved_android_form.scheme = PasswordForm::Scheme::kHtml;
-  saved_android_form.signon_realm =
-      "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
-  saved_android_form.username_value = base::UTF8ToUTF16("randomusername");
-  saved_android_form.password_value = base::UTF8ToUTF16("password");
-
-  password_manager::PasswordStore::FormDigest observed_android_form(
-      saved_android_form);
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::AddLogin,
-                 base::Unretained(&backend), saved_android_form),
-      base::Bind(&CheckPasswordChanges,
-                 PasswordStoreChangeList(
-                     1, PasswordStoreChange(PasswordStoreChange::ADD,
-                                            saved_android_form))));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::GetLogins,
-                 base::Unretained(&backend), observed_android_form, &form_list),
-      base::Bind(&CheckTrue));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_EQ(1u, form_list.size());
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&saved_android_form);
-  ExpectationArray expected;
-  expected.push_back(
-      make_pair(std::string(saved_android_form.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, RemoveLoginsCreatedBetween) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  form_google_.date_synced = base::Time();
-  form_isc_.date_synced = base::Time();
-  form_google_.date_created = base::Time();
-  form_isc_.date_created = base::Time();
-  base::Time now = base::Time::Now();
-  base::Time next_day = now + base::TimeDelta::FromDays(1);
-  form_google_.date_created = now;
-  form_isc_.date_created = next_day;
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_isc_));
-
-  PasswordStoreChangeList expected_changes;
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_google_);
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::BindOnce(&NativeBackendKWalletStub::RemoveLoginsCreatedBetween,
-                     base::Unretained(&backend), base::Time(), next_day,
-                     &changes),
-      base::BindOnce(&CheckPasswordChangesWithResult, &expected_changes,
-                     &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  ExpectationArray expected = {{form_isc_.signon_realm, {&form_isc_}}};
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-
-  // Remove form_isc_.
-  expected_changes.clear();
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_isc_);
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::BindOnce(&NativeBackendKWalletStub::RemoveLoginsCreatedBetween,
-                     base::Unretained(&backend), next_day, base::Time(),
-                     &changes),
-      base::BindOnce(&CheckPasswordChangesWithResult, &expected_changes,
-                     &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  CheckPasswordForms("Chrome Form Data (42)", ExpectationArray());
-}
-
-TEST_P(NativeBackendKWalletTest, DisableAutoSignInForOrigins) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  form_isc_.skip_zero_click = false;
-  form_google_.skip_zero_click = false;
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWallet::AddLogin),
-                     base::Unretained(&backend), form_isc_));
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWallet::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  // Set the canonical forms to the updated value for the following comparison.
-  form_google_.skip_zero_click = true;
-  PasswordStoreChangeList expected_changes;
-  expected_changes.push_back(
-      PasswordStoreChange(PasswordStoreChange::UPDATE, form_google_));
-
-  PasswordStoreChangeList changes;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(
-          &NativeBackendKWallet::DisableAutoSignInForOrigins,
-          base::Unretained(&backend),
-          base::Bind(
-              static_cast<bool (*)(const GURL&, const GURL&)>(operator==),
-              form_google_.origin),
-          &changes),
-      base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  forms.clear();
-  forms.push_back(&form_isc_);
-  expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-TEST_P(NativeBackendKWalletTest, ReadDuplicateForms) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  // Add 2 slightly different password forms.
-  const char unique_string[] = "unique_unique_string";
-  const char unique_string_replacement[] = "uniKue_unique_string";
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string);
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string_replacement);
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  // Read the raw value back. Change the |unique_string| to
-  // |unique_string_replacement| so the forms become unique.
-  TestKWallet::Blob value;
-  ASSERT_TRUE(wallet_.readEntry("Chrome Form Data (42)",
-                                form_google_.signon_realm, &value));
-  TestKWallet::Blob sample(reinterpret_cast<const uint8_t*>(unique_string));
-  size_t position = value.find(sample);
-  ASSERT_NE(TestKWallet::Blob::npos, position);
-  value.replace(position, sample.length(),
-                reinterpret_cast<const uint8_t*>(unique_string_replacement));
-  wallet_.writeEntry("Chrome Form Data (42)", form_google_.signon_realm, value);
-
-  // Now test that GetAutofillableLogins returns only one form.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::GetAutofillableLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(form_google_, *form_list[0]);
-
-  std::vector<const PasswordForm*> forms;
-  forms.push_back(&form_google_);
-  ExpectationArray expected;
-  expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
-  CheckPasswordForms("Chrome Form Data (42)", expected);
-}
-
-// Check that if KWallet fails to respond, the backend propagates the error.
-TEST_P(NativeBackendKWalletTest, GetAllLoginsErrorHandling) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-  // Make KWallet fail on calling readEntry.
-  failing_methods_.insert("readEntry");
-
-  // Store some non-blacklisted logins to be potentially returned.
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::AddLogin,
-                 base::Unretained(&backend), form_google_),
-      base::Bind(
-          &CheckPasswordChanges,
-          PasswordStoreChangeList(
-              1, PasswordStoreChange(PasswordStoreChange::ADD, form_google_))));
-
-  // Verify that nothing is in fact returned, because KWallet fails to respond.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&CheckGetAutofillableLoginsFails,
-                                base::Unretained(&backend), &form_list));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_EQ(0u, form_list.size());
-}
-
-TEST_P(NativeBackendKWalletTest, GetAllLogins) {
-  NativeBackendKWalletStub backend(42, desktop_env_);
-  EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
-
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_google_));
-  backend.GetBackgroundTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
-                     base::Unretained(&backend), form_isc_));
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  base::PostTaskAndReplyWithResult(
-      backend.GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&NativeBackendKWalletStub::GetAllLogins,
-                 base::Unretained(&backend), &form_list),
-      base::Bind(&CheckTrue));
-
-  scoped_task_environment_.RunUntilIdle();
-
-  EXPECT_EQ(2u, form_list.size());
-  EXPECT_THAT(form_list,
-              UnorderedElementsAre(Pointee(form_google_), Pointee(form_isc_)));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    NativeBackendKWalletTest,
-    ::testing::Values(base::nix::DESKTOP_ENVIRONMENT_KDE4,
-                      base::nix::DESKTOP_ENVIRONMENT_KDE5));
-
-// TODO(mdm): add more basic tests here at some point.
-// (For example tests for storing >1 password per realm pickle.)
-
-class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase {
- protected:
-  // Based on |form|, fills |pickle| with data conforming to
-  // |effective_version|, but marking the pickle version as |stored_version|. In
-  // most cases the two versions should be the same.
-  void CreateVersion1PlusPickle(const PasswordForm& form,
-                                base::Pickle* pickle,
-                                int stored_version,
-                                int effective_version);
-  // If |size_32| is true, stores the number of forms in the pickle as a 32bit
-  // uint, otherwise as 64 bit size_t.
-  void CreateVersion0Pickle(bool size_32,
-                            const PasswordForm& form,
-                            base::Pickle* pickle);
-  void CheckVersion9Pickle();
-  void CheckVersion8Pickle();
-  void CheckVersion7Pickle();
-  // As explained in http://crbug.com/494229#c11, version 6 added a new optional
-  // field to version 5. This field became required in version 7. Depending on
-  // |with_optional_field|, this method checks deserialization with or without
-  // the optional field.
-  void CheckVersion6Pickle(bool with_optional_field);
-  void CheckVersion5Pickle();
-  void CheckVersion3Pickle();
-  void CheckVersion2Pickle();
-  void CheckVersion1Pickle();
-  void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
-};
-
-void NativeBackendKWalletPickleTest::CreateVersion1PlusPickle(
-    const PasswordForm& form,
-    base::Pickle* pickle,
-    int stored_version,
-    int effective_version) {
-  pickle->WriteInt(stored_version);
-  pickle->WriteUInt64(1);  // Number of forms in the pickle.
-  WriteHTMLAttributes(form, pickle);
-  if (effective_version < 9)
-    pickle->WriteBool(true);  // Unused flag.
-  WritePreferenceMetadata(form, pickle);
-  pickle->WriteInt64(form.date_created.ToInternalValue());
-  if (effective_version < 2)
-    return;
-  pickle->WriteInt(static_cast<int>(form.type));
-  pickle->WriteInt(form.times_used);
-  autofill::SerializeFormData(form.form_data, pickle);
-  if (effective_version < 3)
-    return;
-  pickle->WriteInt64(form.date_synced.ToInternalValue());
-  if (effective_version < 4)
-    return;
-  pickle->WriteString16(form.display_name);
-  pickle->WriteString(form.icon_url.spec());
-  pickle->WriteString(form.federation_origin.Serialize());
-  pickle->WriteBool(form.skip_zero_click);
-  if (effective_version < 7)
-    return;
-  pickle->WriteInt(static_cast<int>(form.generation_upload_status));
-}
-
-void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
-    bool size_32,
-    const PasswordForm& form,
-    base::Pickle* pickle) {
-  pickle->WriteInt(0);
-  // Write the number of forms in the pickle in the appopriate bit size.
-  if (size_32)
-    pickle->WriteUInt32(1);
-  else
-    pickle->WriteUInt64(1);
-  WriteHTMLAttributes(form, pickle);
-  pickle->WriteBool(true);  // Unused flag.
-  WritePreferenceMetadata(form, pickle);
-  // Old way to store the date.
-  pickle->WriteInt64(form.date_created.ToTimeT());
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion9Pickle() {
-  // Pickle 9+ dropped an old flag in the middle of PasswordForm. This test
-  // makes sure that the attributes after the dropped flag are deserialised
-  // correctly.
-  base::Pickle pickle;
-  PasswordForm default_values;
-  PasswordForm form = form_google_;
-
-  CreateVersion1PlusPickle(form, &pickle, 9, 9);
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], true);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion8Pickle() {
-  base::Pickle pickle;
-  PasswordForm default_values;
-  PasswordForm form = form_google_;
-
-  // Version 8 pickles deserialize with their own 'skip_zero_click' value.
-  form.skip_zero_click = false;
-  CreateVersion1PlusPickle(form, &pickle, 8, 8);
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], true);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion7Pickle() {
-  base::Pickle pickle;
-  PasswordForm default_values;
-  PasswordForm form = form_google_;
-
-  // Version 7 pickles always deserialize with 'skip_zero_click' of 'true'.
-  form.skip_zero_click = false;
-  CreateVersion1PlusPickle(form, &pickle, 7, 7);
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-  EXPECT_EQ(1u, form_list.size());
-  form.skip_zero_click = true;
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], true);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion6Pickle(
-    bool with_optional_field) {
-  base::Pickle pickle;
-  PasswordForm form = form_google_;
-  if (!with_optional_field) {
-    PasswordForm default_values;
-    form.generation_upload_status = default_values.generation_upload_status;
-  }
-  CreateVersion1PlusPickle(form, &pickle, 6, with_optional_field ? 7 : 5);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], true);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion5Pickle() {
-  base::Pickle pickle;
-  PasswordForm default_values;
-  PasswordForm form = form_google_;
-  // Remove the field which was not present in version #5.
-  form.generation_upload_status = default_values.generation_upload_status;
-  CreateVersion1PlusPickle(form, &pickle, 6, 6);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], true);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion3Pickle() {
-  base::Pickle pickle;
-  PasswordForm default_values;
-  PasswordForm form = form_google_;
-  // Remove the fields which were not present in version #3.
-  form.display_name = default_values.display_name;
-  form.icon_url = default_values.icon_url;
-  form.federation_origin = default_values.federation_origin;
-  form.skip_zero_click = default_values.skip_zero_click;
-  form.generation_upload_status = default_values.generation_upload_status;
-  CreateVersion1PlusPickle(form, &pickle, 3, 3);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], false);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion2Pickle() {
-  base::Pickle pickle;
-  PasswordForm form = old_form_google_;
-  form.times_used = form_google_.times_used;
-  form.type = form_google_.type;
-  form.form_data = form_google_.form_data;
-  CreateVersion1PlusPickle(form, &pickle, 2, 2);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], false);
-}
-
-// Make sure that we can still read version 1 pickles.
-void NativeBackendKWalletPickleTest::CheckVersion1Pickle() {
-  base::Pickle pickle;
-  PasswordForm form = form_google_;
-  CreateVersion1PlusPickle(form, &pickle, 1, 1);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-
-  // This will match |old_form_google_| because not all the fields present in
-  // |form_google_| will be deserialized.
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(old_form_google_, *form_list[0], false);
-}
-
-void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
-    bool size_32, PasswordForm::Scheme scheme) {
-  base::Pickle pickle;
-  PasswordForm form = old_form_google_;
-  form.scheme = scheme;
-  CreateVersion0Pickle(size_32, form, &pickle);
-  std::vector<std::unique_ptr<PasswordForm>> form_list =
-      NativeBackendKWalletStub::DeserializeValue(form.signon_realm, pickle);
-  EXPECT_EQ(1u, form_list.size());
-  if (form_list.size() > 0)
-    CheckPasswordForm(form, *form_list[0], false);
-}
-
-// We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
-// after the size in the pickle, so it's what gets read as part of the count
-// when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
-// detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
-// try both 32-bit and 64-bit pickles since only one will be the "other" size
-// for whatever architecture we're running on, but we want to make sure we can
-// read all combinations in any event.
-
-TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) {
-  CheckVersion0Pickle(true, PasswordForm::Scheme::kHtml);
-}
-
-TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) {
-  CheckVersion0Pickle(true, PasswordForm::Scheme::kBasic);
-}
-
-TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) {
-  CheckVersion0Pickle(false, PasswordForm::Scheme::kHtml);
-}
-
-TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) {
-  CheckVersion0Pickle(false, PasswordForm::Scheme::kBasic);
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion1Pickle) {
-  CheckVersion1Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion2Pickle) {
-  CheckVersion2Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion3Pickle) {
-  CheckVersion3Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion5Pickle) {
-  CheckVersion5Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion6Pickle) {
-  CheckVersion6Pickle(false);
-  CheckVersion6Pickle(true);
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion7Pickle) {
-  CheckVersion7Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion8Pickle) {
-  CheckVersion8Pickle();
-}
-
-TEST_F(NativeBackendKWalletPickleTest, CheckVersion9Pickle) {
-  CheckVersion9Pickle();
-}
diff --git a/chrome/browser/password_manager/native_backend_libsecret.cc b/chrome/browser/password_manager/native_backend_libsecret.cc
deleted file mode 100644
index 8122ce5..0000000
--- a/chrome/browser/password_manager/native_backend_libsecret.cc
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/password_manager/native_backend_libsecret.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <libsecret/secret.h>
-
-#include <list>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/password_manager/password_manager_util_linux.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/password_manager_util.h"
-#include "url/origin.h"
-
-using autofill::PasswordForm;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-using password_manager::MatchResult;
-using password_manager::PasswordStore;
-
-namespace {
-
-// Schema is analagous to the fields in PasswordForm.
-const SecretSchema kLibsecretSchema = {
-    "chrome_libsecret_password_schema",
-    // We have to use SECRET_SCHEMA_DONT_MATCH_NAME in order to get old
-    // passwords stored with gnome_keyring.
-    SECRET_SCHEMA_DONT_MATCH_NAME,
-    {{"origin_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"action_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"username_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"username_value", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"password_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"submit_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"signon_realm", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"preferred", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"date_created", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"blacklisted_by_user", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"scheme", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"type", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"times_used", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"date_synced", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"display_name", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"avatar_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"federation_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {"should_skip_zero_click", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"generation_upload_status", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
-     {"form_data", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     // This field is always "chrome-profile_id" so that we can search for it.
-     {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
-     {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
-
-const char* GetStringFromAttributes(GHashTable* attrs, const char* keyname) {
-  gpointer value = g_hash_table_lookup(attrs, keyname);
-  return value ? static_cast<char*>(value) : "";
-}
-
-uint32_t GetUintFromAttributes(GHashTable* attrs, const char* keyname) {
-  gpointer value = g_hash_table_lookup(attrs, keyname);
-  if (!value)
-    return uint32_t();
-  uint32_t result;
-  bool value_ok = base::StringToUint(static_cast<char*>(value), &result);
-  DCHECK(value_ok);
-  return result;
-}
-
-// Convert the attributes into a new PasswordForm.
-// Note: does *not* get the actual password, as that is not a key attribute!
-// Returns nullptr if the attributes are for the wrong application.
-std::unique_ptr<PasswordForm> FormOutOfAttributes(GHashTable* attrs) {
-  base::StringPiece app_value = GetStringFromAttributes(attrs, "application");
-  if (!app_value.starts_with(kLibsecretAndGnomeAppString))
-    return std::unique_ptr<PasswordForm>();
-
-  std::unique_ptr<PasswordForm> form(new PasswordForm());
-  form->origin = GURL(GetStringFromAttributes(attrs, "origin_url"));
-  form->action = GURL(GetStringFromAttributes(attrs, "action_url"));
-  form->username_element =
-      UTF8ToUTF16(GetStringFromAttributes(attrs, "username_element"));
-  form->username_value =
-      UTF8ToUTF16(GetStringFromAttributes(attrs, "username_value"));
-  form->password_element =
-      UTF8ToUTF16(GetStringFromAttributes(attrs, "password_element"));
-  form->submit_element =
-      UTF8ToUTF16(GetStringFromAttributes(attrs, "submit_element"));
-  form->signon_realm = GetStringFromAttributes(attrs, "signon_realm");
-  form->preferred = GetUintFromAttributes(attrs, "preferred");
-  int64_t date_created = 0;
-  bool date_ok = base::StringToInt64(
-      GetStringFromAttributes(attrs, "date_created"), &date_created);
-  DCHECK(date_ok);
-  // In the past |date_created| was stored as time_t. Currently is stored as
-  // base::Time's internal value. We need to distinguish, which format the
-  // number in |date_created| was stored in. We use the fact that
-  // kMaxPossibleTimeTValue interpreted as the internal value corresponds to an
-  // unlikely date back in 17th century, and anything above
-  // kMaxPossibleTimeTValue clearly must be in the internal value format.
-  form->date_created = date_created < kMaxPossibleTimeTValue
-                           ? base::Time::FromTimeT(date_created)
-                           : base::Time::FromInternalValue(date_created);
-  form->blacklisted_by_user =
-      GetUintFromAttributes(attrs, "blacklisted_by_user");
-  form->type =
-      static_cast<PasswordForm::Type>(GetUintFromAttributes(attrs, "type"));
-  form->times_used = GetUintFromAttributes(attrs, "times_used");
-  form->scheme =
-      static_cast<PasswordForm::Scheme>(GetUintFromAttributes(attrs, "scheme"));
-  int64_t date_synced = 0;
-  base::StringToInt64(GetStringFromAttributes(attrs, "date_synced"),
-                      &date_synced);
-  form->date_synced = base::Time::FromInternalValue(date_synced);
-  form->display_name =
-      UTF8ToUTF16(GetStringFromAttributes(attrs, "display_name"));
-  form->icon_url = GURL(GetStringFromAttributes(attrs, "avatar_url"));
-  form->federation_origin = url::Origin::Create(
-      GURL(GetStringFromAttributes(attrs, "federation_url")));
-  form->skip_zero_click =
-      g_hash_table_lookup(attrs, "should_skip_zero_click")
-          ? GetUintFromAttributes(attrs, "should_skip_zero_click")
-          : true;
-  form->generation_upload_status =
-      static_cast<PasswordForm::GenerationUploadStatus>(
-          GetUintFromAttributes(attrs, "generation_upload_status"));
-  base::StringPiece encoded_form_data =
-      GetStringFromAttributes(attrs, "form_data");
-  if (!encoded_form_data.empty()) {
-    bool success = DeserializeFormDataFromBase64String(encoded_form_data,
-                                                       &form->form_data);
-    password_manager::metrics_util::FormDeserializationStatus status =
-        success ? password_manager::metrics_util::GNOME_SUCCESS
-                : password_manager::metrics_util::GNOME_FAILURE;
-    LogFormDataDeserializationStatus(status);
-  }
-  return form;
-}
-
-}  // namespace
-
-NativeBackendLibsecret::NativeBackendLibsecret(LocalProfileId id)
-    : app_string_(GetProfileSpecificAppString(id)),
-      ensured_keyring_unlocked_(false) {}
-
-NativeBackendLibsecret::~NativeBackendLibsecret() {
-}
-
-bool NativeBackendLibsecret::Init() {
-  return LibsecretLoader::EnsureLibsecretLoaded();
-}
-
-password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin(
-    const PasswordForm& form) {
-  // Based on LoginDatabase::AddLogin(), we search for an existing match based
-  // on origin_url, username_element, username_value, password_element and
-  // signon_realm first, remove that, and then add the new entry.
-  password_manager::PasswordStoreChangeList changes;
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!AddUpdateLoginSearch(form, &forms))
-    return changes;
-
-  if (forms.size() > 0) {
-    password_manager::PasswordStoreChangeList temp_changes;
-    if (forms.size() > 1) {
-      LOG(WARNING) << "Adding login when there are " << forms.size()
-                   << " matching logins already!";
-    }
-    for (const auto& old_form : forms) {
-      if (!RemoveLogin(*old_form, &temp_changes))
-        return changes;
-    }
-    changes.push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::REMOVE, *forms[0]));
-  }
-  if (RawAddLogin(form)) {
-    changes.push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::ADD, form));
-  }
-  return changes;
-}
-
-bool NativeBackendLibsecret::UpdateLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
-  // origin_url, username_element, username_value, password_element, and
-  // signon_realm. We then compare the result to the updated form. If they
-  // differ in any of the mutable fields, then we remove the original, and
-  // then add the new entry. We'd add the new one first, and then delete the
-  // original, but then the delete might actually delete the newly-added entry!
-  DCHECK(changes);
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!AddUpdateLoginSearch(form, &forms))
-    return false;
-  if (forms.empty())
-    return true;
-  if (forms.size() == 1 && *forms.front() == form)
-    return true;
-
-  password_manager::PasswordStoreChangeList temp_changes;
-  for (const auto& keychain_form : forms) {
-    // Remove all the obsolete forms. Note that RemoveLogin can remove any form
-    // matching the unique key. Thus, it's important to call it the right number
-    // of times.
-    if (!RemoveLogin(*keychain_form, &temp_changes))
-      return false;
-  }
-
-  if (RawAddLogin(form)) {
-    password_manager::PasswordStoreChange change(
-        password_manager::PasswordStoreChange::UPDATE, form);
-    changes->push_back(change);
-    return true;
-  }
-  return false;
-}
-
-bool NativeBackendLibsecret::RemoveLogin(
-    const PasswordForm& form,
-    password_manager::PasswordStoreChangeList* changes) {
-  DCHECK(changes);
-  GError* error = nullptr;
-  if (LibsecretLoader::secret_password_clear_sync(
-          &kLibsecretSchema, nullptr, &error, "origin_url",
-          form.origin.spec().c_str(), "username_element",
-          UTF16ToUTF8(form.username_element).c_str(), "username_value",
-          UTF16ToUTF8(form.username_value).c_str(), "password_element",
-          UTF16ToUTF8(form.password_element).c_str(), "signon_realm",
-          form.signon_realm.c_str(), "application", app_string_.c_str(),
-          nullptr)) {
-    changes->push_back(password_manager::PasswordStoreChange(
-        password_manager::PasswordStoreChange::REMOVE, form));
-  }
-
-  if (error) {
-    LOG(ERROR) << "Libsecret delete failed: " << error->message;
-    g_error_free(error);
-    return false;
-  }
-  return true;
-}
-
-bool NativeBackendLibsecret::RemoveLoginsCreatedBetween(
-    base::Time delete_begin,
-    base::Time delete_end,
-    password_manager::PasswordStoreChangeList* changes) {
-  changes->clear();
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetLoginsList(nullptr, ALL_LOGINS, &all_forms))
-    return false;
-
-  for (const auto& saved_form : all_forms) {
-    if (delete_begin <= saved_form->date_created &&
-        (delete_end.is_null() || saved_form->date_created < delete_end) &&
-        !RemoveLogin(*saved_form, changes)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool NativeBackendLibsecret::DisableAutoSignInForOrigins(
-    const base::Callback<bool(const GURL&)>& origin_filter,
-    password_manager::PasswordStoreChangeList* changes) {
-  std::vector<std::unique_ptr<PasswordForm>> all_forms;
-  if (!GetLoginsList(nullptr, ALL_LOGINS, &all_forms))
-    return false;
-
-  for (const std::unique_ptr<PasswordForm>& form : all_forms) {
-    if (origin_filter.Run(form->origin) && !form->skip_zero_click) {
-      form->skip_zero_click = true;
-      if (!UpdateLogin(*form, changes))
-        return false;
-    }
-  }
-
-  return true;
-}
-
-bool NativeBackendLibsecret::GetLogins(
-    const PasswordStore::FormDigest& form,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(&form, ALL_LOGINS, forms);
-}
-
-bool NativeBackendLibsecret::AddUpdateLoginSearch(
-    const PasswordForm& lookup_form,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  if (!ensured_keyring_unlocked_) {
-    LibsecretLoader::EnsureKeyringUnlocked();
-    ensured_keyring_unlocked_ = true;
-  }
-
-  LibsecretAttributesBuilder attrs;
-  attrs.Append("origin_url", lookup_form.origin.spec());
-  attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element));
-  attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value));
-  attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element));
-  attrs.Append("signon_realm", lookup_form.signon_realm);
-  attrs.Append("application", app_string_);
-
-  LibsecretLoader::SearchHelper helper;
-  helper.Search(&kLibsecretSchema, attrs.Get(),
-                SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK);
-  if (!helper.success())
-    return false;
-
-  PasswordStore::FormDigest form(lookup_form);
-  *forms = ConvertFormList(helper.results(), &form);
-  return true;
-}
-
-bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) {
-  int64_t date_created = form.date_created.ToInternalValue();
-  // If we are asked to save a password with 0 date, use the current time.
-  // We don't want to actually save passwords as though on January 1, 1601.
-  if (!date_created)
-    date_created = base::Time::Now().ToInternalValue();
-  int64_t date_synced = form.date_synced.ToInternalValue();
-  std::string form_data;
-  SerializeFormDataToBase64String(form.form_data, &form_data);
-  GError* error = nullptr;
-  // clang-format off
-  LibsecretLoader::secret_password_store_sync(
-      &kLibsecretSchema,
-      nullptr,                     // Default collection.
-      form.origin.spec().c_str(),  // Display name.
-      UTF16ToUTF8(form.password_value).c_str(),
-      nullptr,  // no cancellable ojbect
-      &error,
-      "origin_url", form.origin.spec().c_str(),
-      "action_url", form.action.spec().c_str(),
-      "username_element", UTF16ToUTF8(form.username_element).c_str(),
-      "username_value", UTF16ToUTF8(form.username_value).c_str(),
-      "password_element", UTF16ToUTF8(form.password_element).c_str(),
-      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
-      "signon_realm", form.signon_realm.c_str(),
-      "preferred", form.preferred,
-      "date_created", base::NumberToString(date_created).c_str(),
-      "blacklisted_by_user", form.blacklisted_by_user,
-      "type", form.type,
-      "times_used", form.times_used,
-      "scheme", form.scheme,
-      "date_synced", base::NumberToString(date_synced).c_str(),
-      "display_name", UTF16ToUTF8(form.display_name).c_str(),
-      "avatar_url", form.icon_url.spec().c_str(),
-      // We serialize unique origins as "", in order to make other systems that
-      // read from the login database happy. https://crbug.com/591310
-      "federation_url", form.federation_origin.opaque()
-          ? ""
-          : form.federation_origin.Serialize().c_str(),
-      "should_skip_zero_click", form.skip_zero_click,
-      "generation_upload_status", form.generation_upload_status,
-      "form_data", form_data.c_str(),
-      "application", app_string_.c_str(),
-      nullptr);
-  // clang-format on
-
-  if (error) {
-    LOG(ERROR) << "Libsecret add raw login failed: " << error->message;
-    g_error_free(error);
-    return false;
-  }
-  return true;
-}
-
-bool NativeBackendLibsecret::GetAutofillableLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(nullptr, AUTOFILLABLE_LOGINS, forms);
-}
-
-bool NativeBackendLibsecret::GetBlacklistLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(nullptr, BLACKLISTED_LOGINS, forms);
-}
-
-bool NativeBackendLibsecret::GetAllLogins(
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  return GetLoginsList(nullptr, ALL_LOGINS, forms);
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-NativeBackendLibsecret::GetBackgroundTaskRunner() {
-  return nullptr;
-}
-
-bool NativeBackendLibsecret::GetLoginsList(
-    const PasswordStore::FormDigest* lookup_form,
-    GetLoginsListOptions options,
-    std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  if (!ensured_keyring_unlocked_) {
-    LibsecretLoader::EnsureKeyringUnlocked();
-    ensured_keyring_unlocked_ = true;
-  }
-
-  LibsecretAttributesBuilder attrs;
-  attrs.Append("application", app_string_);
-  if (options != ALL_LOGINS)
-    attrs.Append("blacklisted_by_user", options == BLACKLISTED_LOGINS);
-  if (lookup_form &&
-      !password_manager::ShouldPSLDomainMatchingApply(
-          password_manager::GetRegistryControlledDomain(
-              GURL(lookup_form->signon_realm))) &&
-      lookup_form->scheme != PasswordForm::Scheme::kHtml)
-    attrs.Append("signon_realm", lookup_form->signon_realm);
-
-  LibsecretLoader::SearchHelper helper;
-  helper.Search(&kLibsecretSchema, attrs.Get(),
-                SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK);
-  if (!helper.success())
-    return false;
-
-  *forms = ConvertFormList(helper.results(), lookup_form);
-  if (lookup_form)
-    return true;
-
-  // Get rid of the forms with the same sync tags.
-  std::vector<std::unique_ptr<PasswordForm>> duplicates;
-  std::vector<std::vector<PasswordForm*>> tag_groups;
-  password_manager_util::FindDuplicates(forms, &duplicates, &tag_groups);
-  if (duplicates.empty())
-    return true;
-  for (const auto& group : tag_groups) {
-    if (group.size() > 1) {
-      // There are duplicates. Readd the first form. AddLogin() is smart enough
-      // to clean the previous ones.
-      password_manager::PasswordStoreChangeList changes = AddLogin(*group[0]);
-      if (changes.empty() ||
-          changes.back().type() != password_manager::PasswordStoreChange::ADD)
-        return false;
-    }
-  }
-  return true;
-}
-
-std::vector<std::unique_ptr<PasswordForm>>
-NativeBackendLibsecret::ConvertFormList(
-    GList* found,
-    const PasswordStore::FormDigest* lookup_form) {
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  password_manager::PSLDomainMatchMetric psl_domain_match_metric =
-      password_manager::PSL_DOMAIN_MATCH_NONE;
-  GError* error = nullptr;
-  for (GList* element = g_list_first(found); element != nullptr;
-       element = g_list_next(element)) {
-    SecretItem* secretItem = static_cast<SecretItem*>(element->data);
-    GHashTable* attrs = LibsecretLoader::secret_item_get_attributes(secretItem);
-    std::unique_ptr<PasswordForm> form(FormOutOfAttributes(attrs));
-    g_hash_table_unref(attrs);
-    if (!form) {
-      VLOG(1) << "Could not initialize PasswordForm from attributes!";
-      continue;
-    }
-
-    if (lookup_form) {
-      switch (GetMatchResult(*form, *lookup_form)) {
-        case MatchResult::NO_MATCH:
-          continue;
-        case MatchResult::EXACT_MATCH:
-          break;
-        case MatchResult::PSL_MATCH:
-          psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
-          form->is_public_suffix_match = true;
-          break;
-        case MatchResult::FEDERATED_MATCH:
-          break;
-        case MatchResult::FEDERATED_PSL_MATCH:
-          psl_domain_match_metric =
-              password_manager::PSL_DOMAIN_MATCH_FOUND_FEDERATED;
-          form->is_public_suffix_match = true;
-          break;
-      }
-    }
-
-    LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error);
-    if (error) {
-      LOG(ERROR) << "Unable to load secret item" << error->message;
-      g_error_free(error);
-      error = nullptr;
-      continue;
-    }
-
-    SecretValue* secretValue =
-        LibsecretLoader::secret_item_get_secret(secretItem);
-    if (secretValue) {
-      form->password_value =
-          UTF8ToUTF16(LibsecretLoader::secret_value_get_text(secretValue));
-      LibsecretLoader::secret_value_unref(secretValue);
-    } else {
-      LOG(WARNING) << "Unable to access password from list element!";
-    }
-    forms.push_back(std::move(form));
-  }
-
-  if (lookup_form) {
-    const bool allow_psl_match = password_manager::ShouldPSLDomainMatchingApply(
-        password_manager::GetRegistryControlledDomain(
-            GURL(lookup_form->signon_realm)));
-    UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
-                              allow_psl_match
-                                  ? psl_domain_match_metric
-                                  : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
-                              password_manager::PSL_DOMAIN_MATCH_COUNT);
-  }
-  return forms;
-}
diff --git a/chrome/browser/password_manager/native_backend_libsecret.h b/chrome/browser/password_manager/native_backend_libsecret.h
deleted file mode 100644
index 5cb318c1f..0000000
--- a/chrome/browser/password_manager/native_backend_libsecret.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_LIBSECRET_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_LIBSECRET_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/password_store_factory.h"
-#include "chrome/browser/password_manager/password_store_x.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/os_crypt/libsecret_util_linux.h"
-
-namespace autofill {
-struct PasswordForm;
-}
-
-class NativeBackendLibsecret : public PasswordStoreX::NativeBackend {
- public:
-  explicit NativeBackendLibsecret(LocalProfileId id);
-
-  ~NativeBackendLibsecret() override;
-
-  bool Init() override;
-
-  // Implements NativeBackend interface.
-  password_manager::PasswordStoreChangeList AddLogin(
-      const autofill::PasswordForm& form) override;
-  bool UpdateLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLogin(const autofill::PasswordForm& form,
-                   password_manager::PasswordStoreChangeList* changes) override;
-  bool RemoveLoginsCreatedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool DisableAutoSignInForOrigins(
-      const base::Callback<bool(const GURL&)>& origin_filter,
-      password_manager::PasswordStoreChangeList* changes) override;
-  bool GetLogins(
-      const password_manager::PasswordStore::FormDigest& form,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAutofillableLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetBlacklistLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  bool GetAllLogins(
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
-  scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override;
-
- private:
-  // Returns credentials matching |lookup_form| via |forms|.
-  bool AddUpdateLoginSearch(
-      const autofill::PasswordForm& lookup_form,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms);
-
-  // Adds a login form without checking for one to replace first.
-  bool RawAddLogin(const autofill::PasswordForm& form);
-
-  enum GetLoginsListOptions {
-    ALL_LOGINS,
-    AUTOFILLABLE_LOGINS,
-    BLACKLISTED_LOGINS,
-  };
-
-  // Retrieves credentials matching |options| from the keyring into |forms|,
-  // overwriting the original contents of |forms|. If |lookup_form| is not NULL,
-  // only retrieves credentials PSL-matching it. Returns true on success.
-  bool GetLoginsList(
-      const password_manager::PasswordStore::FormDigest* lookup_form,
-      GetLoginsListOptions options,
-      std::vector<std::unique_ptr<autofill::PasswordForm>>* forms)
-      WARN_UNUSED_RESULT;
-
-  // Convert data get from Libsecret to Passwordform. Uses |lookup_form| for
-  // additional (PSL) matching, if present.
-  std::vector<std::unique_ptr<autofill::PasswordForm>> ConvertFormList(
-      GList* found,
-      const password_manager::PasswordStore::FormDigest* lookup_form);
-
-  // The app string, possibly based on the local profile id.
-  std::string app_string_;
-
-  // True if we're already ensured that the default keyring has been unlocked
-  // once.
-  bool ensured_keyring_unlocked_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeBackendLibsecret);
-};
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_LIBSECRET_H_
diff --git a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
deleted file mode 100644
index 21372cf7..0000000
--- a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
+++ /dev/null
@@ -1,1018 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/time/time.h"
-#include "chrome/browser/password_manager/native_backend_libsecret.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/psl_matching_helper.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using autofill::PasswordForm;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-using password_manager::PasswordStore;
-using password_manager::PasswordStoreChange;
-using password_manager::PasswordStoreChangeList;
-using testing::Pointee;
-using testing::UnorderedElementsAre;
-
-namespace {
-
-// What follows is a very simple implementation of the subset of the Libsecret
-// API that we actually use. It gets substituted for the real one by
-// MockLibsecretLoader, which hooks into the facility normally used to load
-// the libsecret library at runtime to avoid a static dependency on it.
-
-struct MockSecretValue {
-  gchar* password;
-  explicit MockSecretValue(gchar* password) : password(password) {}
-  ~MockSecretValue() { g_free(password); }
-};
-
-struct MockSecretItem {
-  std::unique_ptr<MockSecretValue> value;
-  GHashTable* attributes;
-
-  MockSecretItem(std::unique_ptr<MockSecretValue> value, GHashTable* attributes)
-      : value(std::move(value)), attributes(attributes) {}
-  ~MockSecretItem() {
-    g_hash_table_destroy(attributes);
-  }
-
-  void RemoveAttribute(const char* keyname) {
-    g_hash_table_remove(attributes, keyname);
-  }
-};
-
-bool Matches(MockSecretItem* item, GHashTable* query) {
-  GHashTable* attributes = item->attributes;
-  GHashTableIter iter;
-  gchar* name;
-  gchar* query_value;
-  g_hash_table_iter_init(&iter, query);
-
-  while (g_hash_table_iter_next(&iter, reinterpret_cast<gpointer*>(&name),
-                                reinterpret_cast<gpointer*>(&query_value))) {
-    gchar* value = static_cast<gchar*>(g_hash_table_lookup(attributes, name));
-    if (value == nullptr || strcmp(value, query_value) != 0)
-      return false;
-  }
-  return true;
-}
-
-bool IsStringAttribute(const SecretSchema* schema, const std::string& name) {
-  for (size_t i = 0; schema->attributes[i].name; ++i)
-    if (name == schema->attributes[i].name)
-      return schema->attributes[i].type == SECRET_SCHEMA_ATTRIBUTE_STRING;
-  NOTREACHED() << "Requested type of nonexistent attribute";
-  return false;
-}
-
-// The list of all libsecret items we have stored.
-std::vector<std::unique_ptr<MockSecretItem>>* global_mock_libsecret_items;
-std::unordered_map<GObject*, MockSecretItem*>* global_map_object_to_secret_item;
-bool global_mock_libsecret_reject_local_ids = false;
-
-GObject* MakeNewObject(MockSecretItem* item) {
-  // Create an object with a ref-count of 2. The caller is expected to release
-  // one reference, and the second reference is released during test tear down
-  // along with checks that the ref count is correct.
-  GObject* o = static_cast<GObject*>(g_object_new(G_TYPE_OBJECT, nullptr));
-  g_object_ref(o);
-  (*global_map_object_to_secret_item)[o] = item;
-  return o;
-}
-
-gboolean mock_secret_password_store_sync(const SecretSchema* schema,
-                                         const gchar* collection,
-                                         const gchar* label,
-                                         const gchar* password,
-                                         GCancellable* cancellable,
-                                         GError** error,
-                                         ...) {
-  // TODO(crbug.com/660005) We don't read the dummy we store to unlock keyring.
-  if (strcmp("_chrome_dummy_schema_for_unlocking", schema->name) == 0)
-    return true;
-
-  GHashTable* attributes =
-      g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-  va_list ap;
-  va_start(ap, error);
-  char* name;
-  while ((name = va_arg(ap, gchar*))) {
-    char* value;
-    if (IsStringAttribute(schema, name)) {
-      value = g_strdup(va_arg(ap, gchar*));
-      VLOG(1) << "Adding item attribute " << name << ", value '" << value
-              << "'";
-    } else {
-      uint32_t intvalue = va_arg(ap, uint32_t);
-      VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
-      value = g_strdup_printf("%u", intvalue);
-    }
-    g_hash_table_insert(attributes, g_strdup(name), value);
-  }
-  va_end(ap);
-  global_mock_libsecret_items->push_back(std::make_unique<MockSecretItem>(
-      std::make_unique<MockSecretValue>(g_strdup(password)), attributes));
-  return true;
-}
-
-GList* mock_secret_service_search_sync(SecretService* service,
-                                       const SecretSchema* schema,
-                                       GHashTable* attributes,
-                                       SecretSearchFlags flags,
-                                       GCancellable* cancellable,
-                                       GError** error) {
-  EXPECT_TRUE(flags & SECRET_SEARCH_UNLOCK);
-  GList* result = nullptr;
-  for (std::unique_ptr<MockSecretItem>& item : *global_mock_libsecret_items) {
-    if (Matches(item.get(), attributes)) {
-      result = g_list_append(result, MakeNewObject(item.get()));
-    }
-  }
-  return result;
-}
-
-gboolean mock_secret_password_clear_sync(const SecretSchema* schema,
-                                         GCancellable* cancellable,
-                                         GError** error,
-                                         ...) {
-  GHashTable* attributes =
-      g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-  va_list ap;
-  va_start(ap, error);
-  char* name;
-  while ((name = va_arg(ap, gchar*))) {
-    char* value;
-    if (IsStringAttribute(schema, name)) {
-      value = g_strdup(va_arg(ap, gchar*));
-      VLOG(1) << "Adding item attribute " << name << ", value '" << value
-              << "'";
-    } else {
-      uint32_t intvalue = va_arg(ap, uint32_t);
-      VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
-      value = g_strdup_printf("%u", intvalue);
-    }
-    g_hash_table_insert(attributes, g_strdup(name), value);
-  }
-  va_end(ap);
-
-  std::vector<std::unique_ptr<MockSecretItem>> kept_mock_libsecret_items;
-  kept_mock_libsecret_items.reserve(global_mock_libsecret_items->size());
-  for (std::unique_ptr<MockSecretItem>& item : *global_mock_libsecret_items) {
-    if (!Matches(item.get(), attributes)) {
-      kept_mock_libsecret_items.push_back(std::move(item));
-    }
-  }
-  global_mock_libsecret_items->swap(kept_mock_libsecret_items);
-
-  g_hash_table_unref(attributes);
-  return
-      global_mock_libsecret_items->size() != kept_mock_libsecret_items.size();
-}
-
-SecretValue* mock_secret_item_get_secret(SecretItem* self) {
-  GObject* o = reinterpret_cast<GObject*>(self);
-  MockSecretValue* mock_value =
-      (*global_map_object_to_secret_item)[o]->value.get();
-  return reinterpret_cast<SecretValue*>(mock_value);
-}
-
-const gchar* mock_secret_value_get_text(SecretValue* value) {
-  return reinterpret_cast<MockSecretValue*>(value)->password;
-}
-
-GHashTable* mock_secret_item_get_attributes(SecretItem* self) {
-  // Libsecret backend will make unreference of received attributes, so in
-  // order to save them we need to increase their reference number.
-  GObject* o = reinterpret_cast<GObject*>(self);
-  MockSecretItem* mock_ptr = (*global_map_object_to_secret_item)[o];
-  g_hash_table_ref(mock_ptr->attributes);
-  return mock_ptr->attributes;
-}
-
-gboolean mock_secret_item_load_secret_sync(SecretItem* self,
-                                           GCancellable* cancellable,
-                                           GError** error) {
-  return true;
-}
-
-void mock_secret_value_unref(gpointer value) {
-}
-
-// Inherit to get access to protected fields.
-class MockLibsecretLoader : public LibsecretLoader {
- public:
-  static bool LoadMockLibsecret() {
-    secret_password_store_sync = &mock_secret_password_store_sync;
-    secret_service_search_sync = &mock_secret_service_search_sync;
-    secret_password_clear_sync = &mock_secret_password_clear_sync;
-    secret_item_get_secret = &mock_secret_item_get_secret;
-    secret_value_get_text = &mock_secret_value_get_text;
-    secret_item_get_attributes = &mock_secret_item_get_attributes;
-    secret_item_load_secret_sync = &mock_secret_item_load_secret_sync;
-    secret_value_unref = &mock_secret_value_unref;
-    libsecret_loaded_ = true;
-    // Reset the state of the mock library.
-    global_mock_libsecret_items->clear();
-    global_map_object_to_secret_item->clear();
-    global_mock_libsecret_reject_local_ids = false;
-    return true;
-  }
-};
-
-void CheckPasswordChanges(const PasswordStoreChangeList& expected_list,
-                          const PasswordStoreChangeList& actual_list) {
-  ASSERT_EQ(expected_list.size(), actual_list.size());
-  for (size_t i = 0; i < expected_list.size(); ++i) {
-    EXPECT_EQ(expected_list[i].type(), actual_list[i].type());
-    EXPECT_EQ(expected_list[i].form(), actual_list[i].form());
-  }
-}
-
-void VerifiedAdd(NativeBackendLibsecret* backend, const PasswordForm& form) {
-  SCOPED_TRACE("VerifiedAdd");
-  PasswordStoreChangeList changes = backend->AddLogin(form);
-  PasswordStoreChangeList expected(1,
-                                   PasswordStoreChange(PasswordStoreChange::ADD,
-                                                       form));
-  CheckPasswordChanges(expected, changes);
-}
-
-void VerifiedUpdate(NativeBackendLibsecret* backend, const PasswordForm& form) {
-  SCOPED_TRACE("VerifiedUpdate");
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend->UpdateLogin(form, &changes));
-  PasswordStoreChangeList expected(1, PasswordStoreChange(
-      PasswordStoreChange::UPDATE, form));
-  CheckPasswordChanges(expected, changes);
-}
-
-void VerifiedRemove(NativeBackendLibsecret* backend, const PasswordForm& form) {
-  SCOPED_TRACE("VerifiedRemove");
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend->RemoveLogin(form, &changes));
-  CheckPasswordChanges(PasswordStoreChangeList(1, PasswordStoreChange(
-      PasswordStoreChange::REMOVE, form)), changes);
-}
-
-}  // anonymous namespace
-
-class NativeBackendLibsecretTest : public testing::Test {
- protected:
-  enum UpdateType {  // Used in CheckPSLUpdate().
-    UPDATE_BY_UPDATELOGIN,
-    UPDATE_BY_ADDLOGIN,
-  };
-
-  NativeBackendLibsecretTest()
-      : scoped_task_environment_(
-            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
-
-  void SetUp() override {
-    ASSERT_FALSE(global_mock_libsecret_items);
-    ASSERT_FALSE(global_map_object_to_secret_item);
-    global_mock_libsecret_items = &mock_libsecret_items_;
-    global_map_object_to_secret_item = &mock_map_object_to_secret_item_;
-
-    ASSERT_TRUE(MockLibsecretLoader::LoadMockLibsecret());
-
-    form_google_.origin = GURL("http://www.google.com/");
-    form_google_.action = GURL("http://www.google.com/login");
-    form_google_.username_element = UTF8ToUTF16("user");
-    form_google_.username_value = UTF8ToUTF16("joeschmoe");
-    form_google_.password_element = UTF8ToUTF16("pass");
-    form_google_.password_value = UTF8ToUTF16("seekrit");
-    form_google_.submit_element = UTF8ToUTF16("submit");
-    form_google_.signon_realm = "http://www.google.com/";
-    form_google_.type = PasswordForm::Type::kGenerated;
-    form_google_.date_created = base::Time::Now();
-    form_google_.date_synced = base::Time::Now();
-    form_google_.display_name = UTF8ToUTF16("Joe Schmoe");
-    form_google_.icon_url = GURL("http://www.google.com/icon");
-    form_google_.federation_origin =
-        url::Origin::Create(GURL("http://www.google.com/"));
-    form_google_.skip_zero_click = true;
-    form_google_.generation_upload_status =
-        PasswordForm::GenerationUploadStatus::kPositiveSignalSent;
-    form_google_.form_data.name = UTF8ToUTF16("form_name");
-
-    form_facebook_.origin = GURL("http://www.facebook.com/");
-    form_facebook_.action = GURL("http://www.facebook.com/login");
-    form_facebook_.username_element = UTF8ToUTF16("user");
-    form_facebook_.username_value = UTF8ToUTF16("a");
-    form_facebook_.password_element = UTF8ToUTF16("password");
-    form_facebook_.password_value = UTF8ToUTF16("b");
-    form_facebook_.submit_element = UTF8ToUTF16("submit");
-    form_facebook_.signon_realm = "http://www.facebook.com/";
-    form_facebook_.date_created = base::Time::Now();
-    form_facebook_.date_synced = base::Time::Now();
-    form_facebook_.display_name = UTF8ToUTF16("Joe Schmoe");
-    form_facebook_.icon_url = GURL("http://www.facebook.com/icon");
-    form_facebook_.federation_origin =
-        url::Origin::Create(GURL("http://www.facebook.com/"));
-    form_facebook_.skip_zero_click = true;
-    form_facebook_.generation_upload_status =
-        PasswordForm::GenerationUploadStatus::kNoSignalSent;
-
-    form_isc_.origin = GURL("http://www.isc.org/");
-    form_isc_.action = GURL("http://www.isc.org/auth");
-    form_isc_.username_element = UTF8ToUTF16("id");
-    form_isc_.username_value = UTF8ToUTF16("janedoe");
-    form_isc_.password_element = UTF8ToUTF16("passwd");
-    form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
-    form_isc_.submit_element = UTF8ToUTF16("login");
-    form_isc_.signon_realm = "http://www.isc.org/";
-    form_isc_.date_created = base::Time::Now();
-    form_isc_.date_synced = base::Time::Now();
-
-    other_auth_.origin = GURL("http://www.example.com/");
-    other_auth_.username_value = UTF8ToUTF16("username");
-    other_auth_.password_value = UTF8ToUTF16("pass");
-    other_auth_.signon_realm = "http://www.example.com/Realm";
-    other_auth_.date_created = base::Time::Now();
-    other_auth_.date_synced = base::Time::Now();
-  }
-
-  void TearDown() override {
-    scoped_task_environment_.RunUntilIdle();
-    ASSERT_TRUE(global_mock_libsecret_items);
-    global_mock_libsecret_items = nullptr;
-    for (auto& pair : *global_map_object_to_secret_item) {
-      ASSERT_EQ(pair.first->ref_count, 1u);
-      g_object_unref(pair.first);
-    }
-    global_map_object_to_secret_item->clear();
-    global_map_object_to_secret_item = nullptr;
-  }
-
-  void CheckUint32Attribute(const MockSecretItem* item,
-                            const std::string& attribute,
-                            uint32_t value) {
-    gpointer item_value =
-        g_hash_table_lookup(item->attributes, attribute.c_str());
-    EXPECT_TRUE(item_value) << " in attribute " << attribute;
-    if (item_value) {
-      uint32_t int_value;
-      bool conversion_ok =
-          base::StringToUint(static_cast<char*>(item_value), &int_value);
-      EXPECT_TRUE(conversion_ok);
-      EXPECT_EQ(value, int_value);
-    }
-  }
-
-  void CheckStringAttribute(const MockSecretItem* item,
-                            const std::string& attribute,
-                            const std::string& value) {
-    gpointer item_value =
-        g_hash_table_lookup(item->attributes, attribute.c_str());
-    EXPECT_TRUE(item_value) << " in attribute " << attribute;
-    if (item_value) {
-      EXPECT_EQ(value, static_cast<char*>(item_value));
-    }
-  }
-
-  void CheckMockSecretItem(const MockSecretItem* item,
-                           const PasswordForm& form,
-                           const std::string& app_string) {
-    EXPECT_EQ(UTF16ToUTF8(form.password_value), item->value->password);
-    EXPECT_EQ(21u, g_hash_table_size(item->attributes));
-    CheckStringAttribute(item, "origin_url", form.origin.spec());
-    CheckStringAttribute(item, "action_url", form.action.spec());
-    CheckStringAttribute(item, "username_element",
-                         UTF16ToUTF8(form.username_element));
-    CheckStringAttribute(item, "username_value",
-                         UTF16ToUTF8(form.username_value));
-    CheckStringAttribute(item, "password_element",
-                         UTF16ToUTF8(form.password_element));
-    CheckStringAttribute(item, "submit_element",
-                         UTF16ToUTF8(form.submit_element));
-    CheckStringAttribute(item, "signon_realm", form.signon_realm);
-    CheckUint32Attribute(item, "preferred", form.preferred);
-    // We don't check the date created. It varies.
-    CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
-    CheckUint32Attribute(item, "type", static_cast<uint32_t>(form.type));
-    CheckUint32Attribute(item, "times_used", form.times_used);
-    CheckUint32Attribute(item, "scheme", static_cast<uint32_t>(form.scheme));
-    CheckStringAttribute(
-        item, "date_synced",
-        base::NumberToString(form.date_synced.ToInternalValue()));
-    CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
-    CheckStringAttribute(item, "avatar_url", form.icon_url.spec());
-    // We serialize unique origins as "", in order to make other systems that
-    // read from the login database happy. https://crbug.com/591310
-    CheckStringAttribute(item, "federation_url",
-                         form.federation_origin.opaque()
-                             ? ""
-                             : form.federation_origin.Serialize());
-    CheckUint32Attribute(item, "should_skip_zero_click", form.skip_zero_click);
-    CheckUint32Attribute(item, "generation_upload_status",
-                         static_cast<uint32_t>(form.generation_upload_status));
-    CheckStringAttribute(item, "application", app_string);
-    autofill::FormData actual;
-    DeserializeFormDataFromBase64String(
-        static_cast<char*>(g_hash_table_lookup(item->attributes, "form_data")),
-        &actual);
-    EXPECT_TRUE(form.form_data.SameFormAs(actual));
-  }
-
-  // Saves |credentials| and then gets logins matching |url| and |scheme|.
-  // Returns true when something is found, and in such case copies the result to
-  // |result| when |result| is not nullptr. (Note that there can be max. 1
-  // result derived from |credentials|.)
-  bool CheckCredentialAvailability(const PasswordForm& credentials,
-                                   const GURL& url,
-                                   const PasswordForm::Scheme& scheme,
-                                   PasswordForm* result) {
-    NativeBackendLibsecret backend(321);
-
-    VerifiedAdd(&backend, credentials);
-
-    PasswordStore::FormDigest target_form = {PasswordForm::Scheme::kHtml,
-                                             url.spec(), url};
-    if (scheme != PasswordForm::Scheme::kHtml) {
-      // For non-HTML forms, the realm used for authentication
-      // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
-      // signon_realm. Just use a default value for now.
-      target_form.signon_realm.append("Realm");
-    }
-    std::vector<std::unique_ptr<PasswordForm>> form_list;
-    EXPECT_TRUE(backend.GetLogins(target_form, &form_list));
-
-    EXPECT_EQ(1u, global_mock_libsecret_items->size());
-    if (!global_mock_libsecret_items->empty())
-      CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), credentials,
-                          "chrome-321");
-    global_mock_libsecret_items->clear();
-
-    if (form_list.empty())
-      return false;
-    EXPECT_EQ(1u, form_list.size());
-    if (result)
-      *result = *form_list[0];
-    return true;
-  }
-
-  // Test that updating does not use PSL matching: Add a www.facebook.com
-  // password, then use PSL matching to get a copy of it for m.facebook.com, and
-  // add that copy as well. Now update the www.facebook.com password -- the
-  // m.facebook.com password should not get updated. Depending on the argument,
-  // the credential update is done via UpdateLogin or AddLogin.
-  void CheckPSLUpdate(UpdateType update_type) {
-    NativeBackendLibsecret backend(321);
-
-    VerifiedAdd(&backend, form_facebook_);
-
-    // Get the PSL-matched copy of the saved login for m.facebook.
-    const GURL kMobileURL("http://m.facebook.com/");
-    PasswordStore::FormDigest m_facebook_lookup = {
-        PasswordForm::Scheme::kHtml, kMobileURL.spec(), kMobileURL};
-    std::vector<std::unique_ptr<PasswordForm>> form_list;
-    EXPECT_TRUE(backend.GetLogins(m_facebook_lookup, &form_list));
-
-    EXPECT_EQ(1u, global_mock_libsecret_items->size());
-    EXPECT_EQ(1u, form_list.size());
-    PasswordForm m_facebook = *form_list[0];
-    form_list.clear();
-    m_facebook.origin = kMobileURL;
-    m_facebook.signon_realm = kMobileURL.spec();
-
-    // Add the PSL-matched copy to saved logins.
-    VerifiedAdd(&backend, m_facebook);
-    EXPECT_EQ(2u, global_mock_libsecret_items->size());
-
-    // Update www.facebook.com login.
-    PasswordForm new_facebook(form_facebook_);
-    const base::string16 kOldPassword(form_facebook_.password_value);
-    const base::string16 kNewPassword(UTF8ToUTF16("new_b"));
-    EXPECT_NE(kOldPassword, kNewPassword);
-    new_facebook.password_value = kNewPassword;
-    switch (update_type) {
-      case UPDATE_BY_UPDATELOGIN:
-        VerifiedUpdate(&backend, new_facebook);
-        break;
-      case UPDATE_BY_ADDLOGIN:
-        // This is an overwrite call.
-        backend.AddLogin(new_facebook);
-        break;
-    }
-
-    EXPECT_EQ(2u, global_mock_libsecret_items->size());
-
-    // Check that m.facebook.com login was not modified by the update.
-    EXPECT_TRUE(backend.GetLogins(m_facebook_lookup, &form_list));
-
-    // There should be two results -- the exact one, and the PSL-matched one.
-    EXPECT_EQ(2u, form_list.size());
-    size_t index_non_psl = 0;
-    if (form_list[index_non_psl]->is_public_suffix_match)
-      index_non_psl = 1;
-    EXPECT_EQ(kMobileURL, form_list[index_non_psl]->origin);
-    EXPECT_EQ(kMobileURL.spec(), form_list[index_non_psl]->signon_realm);
-    EXPECT_EQ(kOldPassword, form_list[index_non_psl]->password_value);
-    form_list.clear();
-
-    // Check that www.facebook.com login was modified by the update.
-    EXPECT_TRUE(backend.GetLogins(PasswordStore::FormDigest(form_facebook_),
-                                  &form_list));
-    // There should be two results -- the exact one, and the PSL-matched one.
-    EXPECT_EQ(2u, form_list.size());
-    index_non_psl = 0;
-    if (form_list[index_non_psl]->is_public_suffix_match)
-      index_non_psl = 1;
-    EXPECT_EQ(form_facebook_.origin, form_list[index_non_psl]->origin);
-    EXPECT_EQ(form_facebook_.signon_realm,
-              form_list[index_non_psl]->signon_realm);
-    EXPECT_EQ(kNewPassword, form_list[index_non_psl]->password_value);
-    form_list.clear();
-  }
-
-  // Checks various types of matching for forms with a non-HTML |scheme|.
-  void CheckMatchingWithScheme(const PasswordForm::Scheme& scheme) {
-    ASSERT_NE(PasswordForm::Scheme::kHtml, scheme);
-    other_auth_.scheme = scheme;
-
-    // Don't match a non-HTML form with an HTML form.
-    EXPECT_FALSE(
-        CheckCredentialAvailability(other_auth_, GURL("http://www.example.com"),
-                                    PasswordForm::Scheme::kHtml, nullptr));
-    // Don't match an HTML form with non-HTML auth form.
-    EXPECT_FALSE(CheckCredentialAvailability(
-        form_google_, GURL("http://www.google.com/"), scheme, nullptr));
-    // Don't match two different non-HTML auth forms with different origin.
-    EXPECT_FALSE(CheckCredentialAvailability(
-        other_auth_, GURL("http://first.example.com"), scheme, nullptr));
-    // Do match non-HTML forms from the same origin.
-    EXPECT_TRUE(CheckCredentialAvailability(
-        other_auth_, GURL("http://www.example.com/"), scheme, nullptr));
-  }
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  // Provide some test forms to avoid having to set them up in each test.
-  PasswordForm form_google_;
-  PasswordForm form_facebook_;
-  PasswordForm form_isc_;
-  PasswordForm other_auth_;
-
-  std::vector<std::unique_ptr<MockSecretItem>> mock_libsecret_items_;
-  std::unordered_map<GObject*, MockSecretItem*> mock_map_object_to_secret_item_;
-};
-
-TEST_F(NativeBackendLibsecretTest, BasicAddLogin) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, BasicListLogins) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAutofillableLogins(&form_list));
-
-  ASSERT_EQ(1u, form_list.size());
-  EXPECT_EQ(form_google_, *form_list[0]);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, GetAllLogins) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-  VerifiedAdd(&backend, form_facebook_);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAllLogins(&form_list));
-
-  ASSERT_EQ(2u, form_list.size());
-  EXPECT_THAT(form_list, UnorderedElementsAre(Pointee(form_google_),
-                                              Pointee(form_facebook_)));
-}
-
-// Save a password for www.facebook.com and see it suggested for m.facebook.com.
-TEST_F(NativeBackendLibsecretTest, PSLMatchingPositive) {
-  PasswordForm result;
-  const GURL kMobileURL("http://m.facebook.com/");
-  EXPECT_TRUE(CheckCredentialAvailability(
-      form_facebook_, kMobileURL, PasswordForm::Scheme::kHtml, &result));
-  EXPECT_EQ(form_facebook_.origin, result.origin);
-  EXPECT_EQ(form_facebook_.signon_realm, result.signon_realm);
-}
-
-// Save a password for www.facebook.com and see it not suggested for
-// m-facebook.com.
-TEST_F(NativeBackendLibsecretTest, PSLMatchingNegativeDomainMismatch) {
-  EXPECT_FALSE(CheckCredentialAvailability(
-      form_facebook_, GURL("http://m-facebook.com/"),
-      PasswordForm::Scheme::kHtml, nullptr));
-}
-
-// Test PSL matching is off for domains excluded from it.
-TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledDomains) {
-  EXPECT_FALSE(
-      CheckCredentialAvailability(form_google_, GURL("http://one.google.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-// Make sure PSL matches aren't available for non-HTML forms.
-TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledForNonHTMLForms) {
-  CheckMatchingWithScheme(PasswordForm::Scheme::kBasic);
-  CheckMatchingWithScheme(PasswordForm::Scheme::kDigest);
-  CheckMatchingWithScheme(PasswordForm::Scheme::kOther);
-}
-
-TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictUpdateLogin) {
-  CheckPSLUpdate(UPDATE_BY_UPDATELOGIN);
-}
-
-TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictAddLogin) {
-  CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
-}
-
-TEST_F(NativeBackendLibsecretTest, FetchFederatedCredentialOnHTTPS) {
-  other_auth_.signon_realm = "federation://www.example.com/google.com";
-  other_auth_.origin = GURL("https://www.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("https://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendLibsecretTest, FetchFederatedCredentialOnLocalhost) {
-  other_auth_.signon_realm = "federation://localhost/google.com";
-  other_auth_.origin = GURL("http://localhost:8080/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("http://localhost:8080/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendLibsecretTest, DontFetchFederatedCredentialOnHTTP) {
-  other_auth_.signon_realm = "federation://www.example.com/google.com";
-  other_auth_.origin = GURL("https://www.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_FALSE(
-      CheckCredentialAvailability(other_auth_, GURL("http://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendLibsecretTest, FetchPSLMatchedFederatedCredentialOnHTTPS) {
-  other_auth_.signon_realm = "federation://www.sub.example.com/google.com";
-  other_auth_.origin = GURL("https://www.sub.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_TRUE(
-      CheckCredentialAvailability(other_auth_, GURL("https://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendLibsecretTest,
-       DontFetchPSLMatchedFederatedCredentialOnHTTP) {
-  other_auth_.signon_realm = "federation://www.sub.example.com/google.com";
-  other_auth_.origin = GURL("https://www.sub.example.com/");
-  other_auth_.federation_origin =
-      url::Origin::Create(GURL("https://google.com/"));
-  EXPECT_FALSE(
-      CheckCredentialAvailability(other_auth_, GURL("http://www.example.com/"),
-                                  PasswordForm::Scheme::kHtml, nullptr));
-}
-
-TEST_F(NativeBackendLibsecretTest, BasicUpdateLogin) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  PasswordForm new_form_google(form_google_);
-  new_form_google.times_used = 1;
-  new_form_google.action = GURL("http://www.google.com/different/login");
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty()) {
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-  }
-
-  // Update login
-  VerifiedUpdate(&backend, new_form_google);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(),
-                        new_form_google, "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, BasicRemoveLogin) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-
-  VerifiedRemove(&backend, form_google_);
-
-  EXPECT_TRUE(global_mock_libsecret_items->empty());
-}
-
-// Verify fix for http://crbug.com/408783.
-TEST_F(NativeBackendLibsecretTest, RemoveLoginActionMismatch) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-
-  // Action url match not required for removal.
-  form_google_.action = GURL("https://some.other.url.com/path");
-  VerifiedRemove(&backend, form_google_);
-
-  EXPECT_TRUE(global_mock_libsecret_items->empty());
-}
-
-TEST_F(NativeBackendLibsecretTest, RemoveNonexistentLogin) {
-  NativeBackendLibsecret backend(42);
-
-  // First add an unrelated login.
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-
-  // Attempt to remove a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend.RemoveLogin(form_isc_, &changes));
-  CheckPasswordChanges(PasswordStoreChangeList(), changes);
-
-  // Make sure we can still get the first form back.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAutofillableLogins(&form_list));
-
-  // Quick check that we got something back.
-  EXPECT_EQ(1u, form_list.size());
-  form_list.clear();
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, UpdateNonexistentLogin) {
-  NativeBackendLibsecret backend(42);
-
-  // First add an unrelated login.
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty()) {
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-  }
-
-  // Attempt to update a login that doesn't exist.
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend.UpdateLogin(form_isc_, &changes));
-  CheckPasswordChanges(PasswordStoreChangeList(), changes);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, UpdateSameLogin) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty()) {
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-  }
-
-  // Attempt to update the same login without changing anything.
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend.UpdateLogin(form_google_, &changes));
-  CheckPasswordChanges(PasswordStoreChangeList(), changes);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty()) {
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-  }
-}
-
-TEST_F(NativeBackendLibsecretTest, AddDuplicateLogin) {
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  PasswordStoreChangeList expected_changes;
-  expected_changes.push_back(
-      PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
-  form_google_.times_used++;
-  form_google_.submit_element = UTF8ToUTF16("submit2");
-  expected_changes.push_back(
-      PasswordStoreChange(PasswordStoreChange::ADD, form_google_));
-
-  PasswordStoreChangeList actual_changes = backend.AddLogin(form_google_);
-  CheckPasswordChanges(expected_changes, actual_changes);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty())
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-}
-
-TEST_F(NativeBackendLibsecretTest, AndroidCredentials) {
-  NativeBackendLibsecret backend(42);
-  backend.Init();
-
-  PasswordForm observed_android_form;
-  observed_android_form.scheme = PasswordForm::Scheme::kHtml;
-  observed_android_form.signon_realm =
-      "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
-  PasswordForm saved_android_form = observed_android_form;
-  saved_android_form.username_value = base::UTF8ToUTF16("randomusername");
-  saved_android_form.password_value = base::UTF8ToUTF16("password");
-  saved_android_form.date_created = base::Time::Now();
-
-  VerifiedAdd(&backend, saved_android_form);
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAutofillableLogins(&form_list));
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(saved_android_form, *form_list[0]);
-}
-
-TEST_F(NativeBackendLibsecretTest, RemoveLoginsCreatedBetween) {
-  NativeBackendLibsecret backend(42);
-
-  base::Time now = base::Time::Now();
-  base::Time next_day = now + base::TimeDelta::FromDays(1);
-  form_google_.date_created = now;
-  form_isc_.date_created = next_day;
-
-  VerifiedAdd(&backend, form_google_);
-  VerifiedAdd(&backend, form_isc_);
-
-  PasswordStoreChangeList expected_changes;
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_google_);
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(
-      backend.RemoveLoginsCreatedBetween(base::Time(), next_day, &changes));
-  CheckPasswordChanges(expected_changes, changes);
-
-  ASSERT_EQ(1u, global_mock_libsecret_items->size());
-  CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_isc_,
-                      "chrome-42");
-
-  // Remove form_isc_.
-  expected_changes.clear();
-  expected_changes.emplace_back(PasswordStoreChange::REMOVE, form_isc_);
-
-  EXPECT_TRUE(
-      backend.RemoveLoginsCreatedBetween(next_day, base::Time(), &changes));
-  CheckPasswordChanges(expected_changes, changes);
-
-  EXPECT_TRUE(global_mock_libsecret_items->empty());
-}
-
-TEST_F(NativeBackendLibsecretTest, DisableAutoSignInForOrigins) {
-  NativeBackendLibsecret backend(42);
-  backend.Init();
-  form_google_.skip_zero_click = false;
-  form_facebook_.skip_zero_click = false;
-
-  VerifiedAdd(&backend, form_google_);
-  VerifiedAdd(&backend, form_facebook_);
-
-  EXPECT_EQ(2u, global_mock_libsecret_items->size());
-  for (const auto& item : *global_mock_libsecret_items)
-    CheckUint32Attribute(item.get(), "should_skip_zero_click", 0);
-
-  // Set the canonical forms to the updated value for the following comparison.
-  form_google_.skip_zero_click = true;
-  form_facebook_.skip_zero_click = true;
-  PasswordStoreChangeList expected_changes;
-  expected_changes.push_back(
-      PasswordStoreChange(PasswordStoreChange::UPDATE, form_facebook_));
-
-  PasswordStoreChangeList changes;
-  EXPECT_TRUE(backend.DisableAutoSignInForOrigins(
-      base::Bind(static_cast<bool (*)(const GURL&, const GURL&)>(operator==),
-                 form_facebook_.origin),
-      &changes));
-  CheckPasswordChanges(expected_changes, changes);
-
-  EXPECT_EQ(2u, global_mock_libsecret_items->size());
-  CheckStringAttribute((*global_mock_libsecret_items)[0].get(), "origin_url",
-                       form_google_.origin.spec());
-  CheckUint32Attribute((*global_mock_libsecret_items)[0].get(),
-                       "should_skip_zero_click", 0);
-  CheckStringAttribute((*global_mock_libsecret_items)[1].get(), "origin_url",
-                       form_facebook_.origin.spec());
-  CheckUint32Attribute((*global_mock_libsecret_items)[1].get(),
-                       "should_skip_zero_click", 1);
-}
-
-TEST_F(NativeBackendLibsecretTest, SomeKeyringAttributesAreMissing) {
-  // Absent attributes should be filled with default values.
-  NativeBackendLibsecret backend(42);
-
-  VerifiedAdd(&backend, form_google_);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  // Remove a string attribute.
-  (*global_mock_libsecret_items)[0]->RemoveAttribute("avatar_url");
-  // Remove an integer attribute.
-  (*global_mock_libsecret_items)[0]->RemoveAttribute("times_used");
-
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAutofillableLogins(&form_list));
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(GURL(""), form_list[0]->icon_url);
-  EXPECT_EQ(0, form_list[0]->times_used);
-}
-
-TEST_F(NativeBackendLibsecretTest, ReadDuplicateForms) {
-  NativeBackendLibsecret backend(42);
-
-  // Add 2 slightly different password forms.
-  const char unique_string[] = "unique_unique_string";
-  const char unique_string_replacement[] = "uniKue_unique_string";
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string);
-  VerifiedAdd(&backend, form_google_);
-  form_google_.origin =
-      GURL(std::string("http://www.google.com/") + unique_string_replacement);
-  VerifiedAdd(&backend, form_google_);
-
-  // Read the raw value back. Change the |unique_string| to
-  // |unique_string_replacement| so the forms become unique.
-  ASSERT_EQ(2u, global_mock_libsecret_items->size());
-  gpointer item_value = g_hash_table_lookup(
-      global_mock_libsecret_items->front()->attributes, "origin_url");
-  ASSERT_TRUE(item_value);
-  char* substr = strstr(static_cast<char*>(item_value), unique_string);
-  ASSERT_TRUE(substr);
-  ASSERT_EQ(strlen(unique_string), strlen(unique_string_replacement));
-  strncpy(substr, unique_string_replacement, strlen(unique_string));
-
-  // Now test that GetAutofillableLogins returns only one form.
-  std::vector<std::unique_ptr<PasswordForm>> form_list;
-  EXPECT_TRUE(backend.GetAutofillableLogins(&form_list));
-
-  EXPECT_EQ(1u, form_list.size());
-  EXPECT_EQ(form_google_, *form_list[0]);
-
-  EXPECT_EQ(1u, global_mock_libsecret_items->size());
-  if (!global_mock_libsecret_items->empty()) {
-    CheckMockSecretItem((*global_mock_libsecret_items)[0].get(), form_google_,
-                        "chrome-42");
-  }
-}
-
-// TODO(mdm): add more basic tests here at some point.
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc
index bd13e93f..702478e 100644
--- a/chrome/browser/password_manager/password_store_factory.cc
+++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -48,14 +48,6 @@
 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
 // Don't do anything. We're going to use the default store.
 #elif defined(USE_X11)
-#include "components/os_crypt/key_storage_util_linux.h"
-#if defined(USE_GNOME_KEYRING)
-#include "chrome/browser/password_manager/native_backend_gnome_x.h"
-#endif
-#if defined(USE_LIBSECRET)
-#include "chrome/browser/password_manager/native_backend_libsecret.h"
-#endif
-#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
 #include "chrome/browser/password_manager/password_store_x.h"
 #endif
 
@@ -179,96 +171,9 @@
 #if defined(OS_WIN)
   ps = new password_manager::PasswordStoreDefault(std::move(login_db));
 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID) || defined(OS_MACOSX)
-  // For now, we use PasswordStoreDefault. We might want to make a native
-  // backend for PasswordStoreX (see below) in the future though.
   ps = new password_manager::PasswordStoreDefault(std::move(login_db));
 #elif defined(USE_X11)
-  // On POSIX systems, we try to use the "native" password management system of
-  // the desktop environment currently running, allowing GNOME Keyring in XFCE.
-  // (In all cases we fall back on the basic store in case of failure.)
-  base::nix::DesktopEnvironment desktop_env = GetDesktopEnvironment();
-  std::string store_type =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kPasswordStore);
-  LinuxBackendUsed used_backend = PLAINTEXT;
-
-  PrefService* prefs = profile->GetPrefs();
-  LocalProfileId id = GetLocalProfileId(prefs);
-
-  bool use_preference = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableEncryptionSelection);
-  bool use_backend = true;
-  if (use_preference) {
-    base::FilePath user_data_dir;
-    chrome::GetDefaultUserDataDirectory(&user_data_dir);
-    use_backend = os_crypt::GetBackendUse(user_data_dir);
-  }
-
-  os_crypt::SelectedLinuxBackend selected_backend =
-      os_crypt::SelectBackend(store_type, use_backend, desktop_env);
-
-  std::unique_ptr<PasswordStoreX::NativeBackend> backend;
-  if (selected_backend == os_crypt::SelectedLinuxBackend::KWALLET ||
-      selected_backend == os_crypt::SelectedLinuxBackend::KWALLET5) {
-    VLOG(1) << "Trying KWallet for password storage.";
-    base::nix::DesktopEnvironment used_desktop_env =
-        selected_backend == os_crypt::SelectedLinuxBackend::KWALLET
-            ? base::nix::DESKTOP_ENVIRONMENT_KDE4
-            : base::nix::DESKTOP_ENVIRONMENT_KDE5;
-    backend.reset(new NativeBackendKWallet(id, used_desktop_env));
-    if (backend->Init()) {
-      VLOG(1) << "Using KWallet for password storage.";
-      used_backend = KWALLET;
-    } else {
-      backend.reset();
-    }
-  } else if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
-             selected_backend ==
-                 os_crypt::SelectedLinuxBackend::GNOME_KEYRING ||
-             selected_backend ==
-                 os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
-#if defined(USE_LIBSECRET)
-    if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
-        selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
-      VLOG(1) << "Trying libsecret for password storage.";
-      backend.reset(new NativeBackendLibsecret(id));
-      if (backend->Init()) {
-        VLOG(1) << "Using libsecret keyring for password storage.";
-        used_backend = LIBSECRET;
-      } else {
-        backend.reset();
-      }
-    }
-#endif  // defined(USE_LIBSECRET)
-#if defined(USE_GNOME_KEYRING)
-    if (!backend.get() &&
-        (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
-         selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING)) {
-      VLOG(1) << "Trying GNOME keyring for password storage.";
-      backend.reset(new NativeBackendGnome(id));
-      if (backend->Init()) {
-        VLOG(1) << "Using GNOME keyring for password storage.";
-        used_backend = GNOME_KEYRING;
-      } else {
-        backend.reset();
-      }
-    }
-#endif  // defined(USE_GNOME_KEYRING)
-  }
-
-  if (!backend.get()) {
-    LOG(WARNING) << "Using basic (unencrypted) store for password storage. "
-        "See "
-        "https://chromium.googlesource.com/chromium/src/+/master/docs/linux_password_storage.md"
-        " for more information about password storage options.";
-  }
-
-  ps = new PasswordStoreX(
-      std::move(login_db),
-      profile->GetPath().Append(password_manager::kLoginDataFileName),
-      profile->GetPath().Append(password_manager::kSecondLoginDataFileName),
-      std::move(backend), prefs);
-  RecordBackendStatistics(desktop_env, store_type, used_backend);
+  ps = new PasswordStoreX(std::move(login_db), profile->GetPrefs());
 #elif defined(USE_OZONE)
   ps = new password_manager::PasswordStoreDefault(std::move(login_db));
 #else
diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc
index 6b8ea98..1aab9a6 100644
--- a/chrome/browser/password_manager/password_store_x.cc
+++ b/chrome/browser/password_manager/password_store_x.cc
@@ -32,48 +32,6 @@
 using password_manager::PasswordStoreDefault;
 
 namespace {
-
-bool AddLoginToBackend(
-    const std::unique_ptr<PasswordStoreX::NativeBackend>& backend,
-    const PasswordForm& form,
-    PasswordStoreChangeList* changes) {
-  *changes = backend->AddLogin(form);
-  return (!changes->empty() &&
-          changes->back().type() == PasswordStoreChange::ADD);
-}
-
-bool RemoveLoginsByURLAndTimeFromBackend(
-    PasswordStoreX::NativeBackend* backend,
-    const base::Callback<bool(const GURL&)>& url_filter,
-    base::Time delete_begin,
-    base::Time delete_end,
-    PasswordStoreChangeList* changes) {
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!backend->GetAllLogins(&forms))
-    return false;
-
-  for (const auto& form : forms) {
-    if (url_filter.Run(form->origin) && form->date_created >= delete_begin &&
-        (delete_end.is_null() || form->date_created < delete_end) &&
-        !backend->RemoveLogin(*form, changes))
-      return false;
-  }
-
-  return true;
-}
-
-// Disables encryption on |login_db|, if the migration to encryption has not
-// been performed yet.
-std::unique_ptr<password_manager::LoginDatabase> DisableEncryption(
-    std::unique_ptr<password_manager::LoginDatabase> login_db,
-    PrefService* prefs) {
-  if (prefs->GetInteger(password_manager::prefs::kMigrationToLoginDBStep) !=
-      PasswordStoreX::LOGIN_DB_REPLACED) {
-    login_db->disable_encryption();
-  }
-  return login_db;
-}
-
 // Returns the password_manager::metrics_util::LinuxBackendMigrationStatus
 // equivalent for |step|.
 password_manager::metrics_util::LinuxBackendMigrationStatus StepForMetrics(
@@ -109,47 +67,12 @@
   return LinuxBackendMigrationStatus::kNotAttempted;
 }
 
-// Remove |forms| from |backend|. If |forms| is empty, |backend| will be cleared
-// entirely. This must be called on |runner|, which has to be the same as
-// |backend|'s.
-void ClearNativeBackend(scoped_refptr<base::SequencedTaskRunner> runner,
-                        std::unique_ptr<PasswordStoreX::NativeBackend> backend,
-                        std::vector<std::unique_ptr<PasswordForm>> forms) {
-  if (forms.empty()) {
-    if (!backend->GetAllLogins(&forms))
-      return;
-  }
-
-  if (!forms.empty()) {
-    PasswordStoreChangeList changes;
-    backend->RemoveLogin(*forms.back(), &changes);
-    forms.pop_back();
-    if (!forms.empty()) {
-      // We yield on the task runner between deletes, because this is a
-      // background cleanup which has to happen on the native backend's
-      // preferred thread, and in the case of gnome-keyring it's the main
-      // thread.
-      runner->PostTask(FROM_HERE,
-                       base::BindOnce(&ClearNativeBackend, runner,
-                                      std::move(backend), std::move(forms)));
-    }
-  }
-}
-
 }  // namespace
 
 PasswordStoreX::PasswordStoreX(
     std::unique_ptr<password_manager::LoginDatabase> login_db,
-    base::FilePath login_db_file,
-    base::FilePath encrypted_login_db_file,
-    std::unique_ptr<NativeBackend> backend,
     PrefService* prefs)
-    : PasswordStoreDefault(DisableEncryption(std::move(login_db), prefs)),
-      backend_(std::move(backend)),
-      login_db_file_(std::move(login_db_file)),
-      encrypted_login_db_file_(std::move(encrypted_login_db_file)),
-      migration_checked_(false),
-      allow_fallback_(false) {
+    : PasswordStoreDefault(std::move(login_db)), migration_checked_(false) {
   migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
                             prefs);
   migration_to_login_db_step_ =
@@ -158,57 +81,33 @@
   base::UmaHistogramEnumeration(
       "PasswordManager.LinuxBackendMigration.Adoption",
       StepForMetrics(migration_to_login_db_step_));
-
-  // No |backend_| means serving from PasswordStoreDefault.
-  if (migration_to_login_db_step_ == LOGIN_DB_REPLACED)
-    backend_.reset();
 }
 
 PasswordStoreX::~PasswordStoreX() {}
 
 scoped_refptr<base::SequencedTaskRunner>
 PasswordStoreX::CreateBackgroundTaskRunner() const {
-  scoped_refptr<base::SequencedTaskRunner> result =
-      backend_ ? backend_->GetBackgroundTaskRunner() : nullptr;
-  return result ? result : PasswordStoreDefault::CreateBackgroundTaskRunner();
+  return PasswordStoreDefault::CreateBackgroundTaskRunner();
 }
 
 PasswordStoreChangeList PasswordStoreX::AddLoginImpl(
     const PasswordForm& form,
     password_manager::AddLoginError* error) {
   CheckMigration();
-  PasswordStoreChangeList changes;
-  if (use_native_backend() && AddLoginToBackend(backend_, form, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes = PasswordStoreDefault::AddLoginImpl(form, error);
-  }
-  return changes;
+  return PasswordStoreDefault::AddLoginImpl(form, error);
 }
 
 PasswordStoreChangeList PasswordStoreX::UpdateLoginImpl(
     const PasswordForm& form,
     password_manager::UpdateLoginError* error) {
   CheckMigration();
-  PasswordStoreChangeList changes;
-  if (use_native_backend() && backend_->UpdateLogin(form, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes = PasswordStoreDefault::UpdateLoginImpl(form, error);
-  }
-  return changes;
+  return PasswordStoreDefault::UpdateLoginImpl(form, error);
 }
 
 PasswordStoreChangeList PasswordStoreX::RemoveLoginImpl(
     const PasswordForm& form) {
   CheckMigration();
-  PasswordStoreChangeList changes;
-  if (use_native_backend() && backend_->RemoveLogin(form, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes = PasswordStoreDefault::RemoveLoginImpl(form);
-  }
-  return changes;
+  return PasswordStoreDefault::RemoveLoginImpl(form);
 }
 
 PasswordStoreChangeList PasswordStoreX::RemoveLoginsByURLAndTimeImpl(
@@ -216,303 +115,63 @@
     base::Time delete_begin,
     base::Time delete_end) {
   CheckMigration();
-
-  PasswordStoreChangeList changes;
-  if (use_native_backend() &&
-      RemoveLoginsByURLAndTimeFromBackend(backend_.get(), url_filter,
-                                          delete_begin, delete_end, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes = PasswordStoreDefault::RemoveLoginsByURLAndTimeImpl(
-        url_filter, delete_begin, delete_end);
-  }
-
-  return changes;
+  return PasswordStoreDefault::RemoveLoginsByURLAndTimeImpl(
+      url_filter, delete_begin, delete_end);
 }
 
 PasswordStoreChangeList PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
     base::Time delete_begin,
     base::Time delete_end) {
   CheckMigration();
-  PasswordStoreChangeList changes;
-  if (use_native_backend() &&
-      backend_->RemoveLoginsCreatedBetween(
-          delete_begin, delete_end, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes = PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
-                                                                   delete_end);
-  }
-  return changes;
+  return PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
+                                                              delete_end);
 }
 
 PasswordStoreChangeList PasswordStoreX::DisableAutoSignInForOriginsImpl(
     const base::Callback<bool(const GURL&)>& origin_filter) {
   CheckMigration();
-  PasswordStoreChangeList changes;
-  if (use_native_backend() &&
-      backend_->DisableAutoSignInForOrigins(origin_filter, &changes)) {
-    allow_fallback_ = false;
-  } else if (allow_default_store()) {
-    changes =
-        PasswordStoreDefault::DisableAutoSignInForOriginsImpl(origin_filter);
-  }
-  return changes;
+  return PasswordStoreDefault::DisableAutoSignInForOriginsImpl(origin_filter);
 }
 
-namespace {
-
-// Sorts |list| by origin, like the ORDER BY clause in login_database.cc.
-void SortLoginsByOrigin(std::vector<std::unique_ptr<PasswordForm>>* list) {
-  std::sort(list->begin(), list->end(),
-            [](const std::unique_ptr<PasswordForm>& a,
-               const std::unique_ptr<PasswordForm>& b) {
-              return a->origin < b->origin;
-            });
-}
-
-}  // anonymous namespace
-
 std::vector<std::unique_ptr<PasswordForm>> PasswordStoreX::FillMatchingLogins(
     const FormDigest& form) {
   CheckMigration();
-  std::vector<std::unique_ptr<PasswordForm>> matched_forms;
-  if (use_native_backend() && backend_->GetLogins(form, &matched_forms)) {
-    SortLoginsByOrigin(&matched_forms);
-    // The native backend may succeed and return no data even while locked, if
-    // the query did not match anything stored. So we continue to allow fallback
-    // until we perform a write operation, or until a read returns actual data.
-    if (!matched_forms.empty())
-      allow_fallback_ = false;
-    return matched_forms;
-  }
-  if (allow_default_store())
     return PasswordStoreDefault::FillMatchingLogins(form);
-  return std::vector<std::unique_ptr<PasswordForm>>();
 }
 
 bool PasswordStoreX::FillAutofillableLogins(
     std::vector<std::unique_ptr<PasswordForm>>* forms) {
   CheckMigration();
-  if (use_native_backend() && backend_->GetAutofillableLogins(forms)) {
-    SortLoginsByOrigin(forms);
-    // See GetLoginsImpl() for why we disallow fallback conditionally here.
-    if (!forms->empty())
-      allow_fallback_ = false;
-    return true;
-  }
-  if (allow_default_store())
     return PasswordStoreDefault::FillAutofillableLogins(forms);
-  return false;
 }
 
 bool PasswordStoreX::FillBlacklistLogins(
     std::vector<std::unique_ptr<PasswordForm>>* forms) {
   CheckMigration();
-  if (use_native_backend() && backend_->GetBlacklistLogins(forms)) {
-    // See GetLoginsImpl() for why we disallow fallback conditionally here.
-    SortLoginsByOrigin(forms);
-    if (!forms->empty())
-      allow_fallback_ = false;
-    return true;
-  }
-  if (allow_default_store())
     return PasswordStoreDefault::FillBlacklistLogins(forms);
-  return false;
 }
 
 void PasswordStoreX::CheckMigration() {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
 
-  if (migration_checked_ || !backend_.get())
+  if (migration_checked_)
     return;
+
   migration_checked_ = true;
-  DCHECK_NE(migration_to_login_db_step_, LOGIN_DB_REPLACED);
-
-  base::Time migration_to_native_started = base::Time::Now();
-
-  ssize_t migrated = MigrateToNativeBackend();
-  if (migrated > 0) {
-    VLOG(1) << "Migrated " << migrated << " passwords to native store.";
-  } else if (migrated == 0) {
-    // As long as we are able to migrate some passwords, we know the native
-    // store is working. But if there is nothing to migrate, the "migration"
-    // can succeed even when the native store would fail. In this case we
-    // allow a later fallback to the default store. Once any later operation
-    // succeeds on the native store, we will no longer allow fallback.
-    allow_fallback_ = true;
-  } else {
-    LOG(WARNING) << "Native password store migration failed! "
-                 << "Falling back on default (unencrypted) store.";
-    backend_.reset();
-  }
-
-  base::UmaHistogramLongTimes(
-      "PasswordManager.LinuxBackendMigration.TimeIntoNative",
-      base::Time::Now() - migration_to_native_started);
-
-  if (base::FeatureList::IsEnabled(
-          password_manager::features::kMigrateLinuxToLoginDB)) {
-    // Copy passwords from the backend into the login database, using
-    // encryption.
-    if (backend_) {
-      UpdateMigrationToLoginDBStep(STARTED);
-      MigrateToEncryptedLoginDB();
-    } else {
-      UpdateMigrationToLoginDBStep(POSTPONED);
-    }
-
-    base::UmaHistogramEnumeration(
-        "PasswordManager.LinuxBackendMigration.AttemptResult",
-        StepForMetrics(migration_to_login_db_step_));
-  }
-}
-
-bool PasswordStoreX::allow_default_store() {
-  if (allow_fallback_) {
-    LOG(WARNING) << "Native password store failed! " <<
-                 "Falling back on default (unencrypted) store.";
-    backend_.reset();
-    // Don't warn again. We'll use the default store because backend_ is NULL.
-    allow_fallback_ = false;
-  }
-  return !backend_.get();
-}
-
-ssize_t PasswordStoreX::MigrateToNativeBackend() {
-  DCHECK(backend_.get());
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  std::vector<std::unique_ptr<PasswordForm>> blacklist_forms;
-  bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) &&
-            PasswordStoreDefault::FillBlacklistLogins(&blacklist_forms);
-  const size_t autofillable_forms_count = forms.size();
-  forms.resize(autofillable_forms_count + blacklist_forms.size());
-  std::move(blacklist_forms.begin(), blacklist_forms.end(),
-            forms.begin() + autofillable_forms_count);
-  if (ok) {
-    // We add all the passwords (and blacklist entries) to the native backend
-    // before attempting to remove any from the login database, to make sure we
-    // don't somehow end up with some of the passwords in one store and some in
-    // another. We'll always have at least one intact store this way.
-    for (size_t i = 0; i < forms.size(); ++i) {
-      PasswordStoreChangeList changes;
-      if (!AddLoginToBackend(backend_, *forms[i], &changes)) {
-        ok = false;
-        break;
-      }
-    }
-    if (ok) {
-      for (size_t i = 0; i < forms.size(); ++i) {
-        // If even one of these calls to RemoveLoginImpl() succeeds, then we
-        // should prefer the native backend to the now-incomplete login
-        // database. Thus we want to return a success status even in the case
-        // where some fail. The only real problem with this is that we might
-        // leave passwords in the login database and never come back to clean
-        // them out if any of these calls do fail.
-        PasswordStoreDefault::RemoveLoginImpl(*forms[i]);
-      }
-      // Finally, delete the database file itself. We remove the passwords from
-      // it before deleting the file just in case there is some problem deleting
-      // the file (e.g. directory is not writable, but file is), which would
-      // otherwise cause passwords to re-migrate next (or maybe every) time.
-      DeleteAndRecreateDatabaseFile();
-    }
-  }
-  ssize_t result = ok ? forms.size() : -1;
-  return result;
-}
-
-void PasswordStoreX::MigrateToEncryptedLoginDB() {
-  base::Time migration_to_encrypted_started = base::Time::Now();
-
-  // Initialise the temporary database.
-  auto encrypted_login_db = std::make_unique<password_manager::LoginDatabase>(
-      encrypted_login_db_file_);
-  if (!encrypted_login_db->Init()) {
-    VLOG(1) << "Failed to init the encrypted database file. Migration "
-               "aborted.";
-    UpdateMigrationToLoginDBStep(FAILED_INIT_ENCRYPTED);
-    return;  // Serve from the native backend.
-  }
-
-  // Copy everything from the backend to the temporary database.
-  VLOG(1) << "Migrating all passwords to the encrypted database. Last status: "
-          << migration_to_login_db_step_;
-  UpdateMigrationToLoginDBStep(CopyBackendToLoginDB(encrypted_login_db.get()));
-  if (migration_to_login_db_step_ != COPIED_ALL) {
-    VLOG(1) << "Migration to encryption failed.";
-    base::DeleteFile(encrypted_login_db_file_, false);
+  if (migration_to_login_db_step_ == LOGIN_DB_REPLACED) {
     return;
   }
-
-  base::UmaHistogramLongTimes(
-      "PasswordManager.LinuxBackendMigration.TimeIntoEncrypted",
-      base::Time::Now() - migration_to_encrypted_started);
-
-  // Dispose of the databases, so that we release the databases' locks.
-  PasswordStoreDefault::SetLoginDB(nullptr);
-  encrypted_login_db.reset();
-  // Move the new database onto the old.
-  if (!base::Move(encrypted_login_db_file_, login_db_file_)) {
-    LOG(ERROR) << "Could not replace login database.";
-    UpdateMigrationToLoginDBStep(FAILED_REPLACE);
-    base::DeleteFile(encrypted_login_db_file_, false);
-    return;  // Serve from the native backend.
+  // If the db is empty, there are no records to migrate, and we then can call
+  // it a completed migration.
+  if (login_db()->IsEmpty()) {
+    UpdateMigrationToLoginDBStep(LOGIN_DB_REPLACED);
+    return;
   }
-  UpdateMigrationToLoginDBStep(LOGIN_DB_REPLACED);
-
-  auto login_db =
-      std::make_unique<password_manager::LoginDatabase>(login_db_file_);
-  if (login_db->Init()) {
-    PasswordStoreDefault::SetLoginDB(std::move(login_db));
-  } else {
-    // The password manager is disabled because PasswordStoreDefault is left
-    // with no LoginDatabase and |backend_| will be disposed of.
-    LOG(ERROR) << "Could not initialise database after migration. Password "
-                  "Manager is disabled.";
-  }
-
-  // Cleanup the native backend on the background, while we serve from
-  // PasswordStoreDefault. PasswordStoreX will use the PasswordStoreDefault
-  // behaviour, because we move |backend_|.
-  auto task_runner = CreateBackgroundTaskRunner();
-  task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ClearNativeBackend, task_runner, std::move(backend_),
-                     std::vector<std::unique_ptr<autofill::PasswordForm>>()));
-}
-
-PasswordStoreX::MigrationToLoginDBStep PasswordStoreX::CopyBackendToLoginDB(
-    password_manager::LoginDatabase* login_db) {
-  DCHECK(backend_);
-  DCHECK(login_db);
-
-  if (!login_db->DeleteAndRecreateDatabaseFile()) {
-    LOG(ERROR) << "Failed to create the encrypted login database file";
-    return FAILED_RECREATE_ENCRYPTED;
-  }
-
-  std::vector<std::unique_ptr<PasswordForm>> forms;
-  if (!backend_->GetAllLogins(&forms))
-    return FAILED_ACCESS_NATIVE;
-
-  for (auto& form : forms) {
-    PasswordStoreChangeList changes = login_db->AddLogin(*form);
-    if (changes.empty() || changes.back().type() != PasswordStoreChange::ADD) {
-      // AddLogin() would fail if the form has empty |origin|, empty
-      // |signon_realm|, is a duplicate blacklisting or there was an IO error.
-      // All of these cases are not supported and can be dropped.
-      if (form->signon_realm.empty() || form->origin.is_empty() ||
-          form->blacklisted_by_user) {
-        LOG(WARNING) << "Dropped a credential during migration away from the "
-                        "native backend";
-      } else {
-        return FAILED_WRITE_TO_ENCRYPTED;
-      }
-    }
-  }
-
-  return COPIED_ALL;
+  // The migration hasn't completed yes. The records in the database aren't
+  // encrypted, so we must disable the encryption.
+  // TODO(crbug/950267): Handle users who have unencrypted entries in the
+  // database.
+  login_db()->disable_encryption();
 }
 
 void PasswordStoreX::UpdateMigrationToLoginDBStep(MigrationToLoginDBStep step) {
@@ -534,36 +193,18 @@
 
 password_manager::FormRetrievalResult PasswordStoreX::ReadAllLogins(
     password_manager::PrimaryKeyToFormMap* key_to_form_map) {
-  // This method is called from the PasswordSyncBridge which supports only
-  // PasswordStoreDefault. Therefore, on Linux, it should be called only if the
-  // client is using LogainDatabase instead of the NativeBackend's. It's the
-  // responsibility of the caller to guarantee that.
-  if (use_native_backend()) {
-    NOTREACHED();
-  }
+  CheckMigration();
   return PasswordStoreDefault::ReadAllLogins(key_to_form_map);
 }
 
 PasswordStoreChangeList PasswordStoreX::RemoveLoginByPrimaryKeySync(
     int primary_key) {
-  // This method is called from the PasswordSyncBridge which supports only
-  // PasswordStoreDefault. Therefore, on Linux, it should be called only if the
-  // client is using LogainDatabase instead of the NativeBackend's. It's the
-  // responsibility of the caller to guarantee that.
-  if (use_native_backend()) {
-    NOTREACHED();
-  }
+  CheckMigration();
   return PasswordStoreDefault::RemoveLoginByPrimaryKeySync(primary_key);
 }
 
 password_manager::PasswordStoreSync::MetadataStore*
 PasswordStoreX::GetMetadataStore() {
-  // This method is called from the PasswordSyncBridge which supports only
-  // PasswordStoreDefault. Therefore, on Linux, it should be called only if the
-  // client is using LogainDatabase instead of the NativeBackend's. It's the
-  // responsibility of the caller to guarantee that.
-  if (use_native_backend()) {
-    NOTREACHED();
-  }
+  CheckMigration();
   return PasswordStoreDefault::GetMetadataStore();
 }
diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h
index f2e1028..7f59e36 100644
--- a/chrome/browser/password_manager/password_store_x.h
+++ b/chrome/browser/password_manager/password_store_x.h
@@ -21,13 +21,10 @@
 class LoginDatabase;
 }
 
-// PasswordStoreX is used on Linux and other non-Windows, non-Mac OS X
-// operating systems. It uses a "native backend" to actually store the password
-// data when such a backend is available, and otherwise falls back to using the
-// login database like PasswordStoreDefault. It also handles automatically
-// migrating password data to a native backend from the login database.
-//
-// There are currently native backends for GNOME Keyring and KWallet.
+// PasswordStoreX is used on Linux and other non-Windows, non-Mac OS X operating
+// systems. It is used as a proxy for the PasswordStoreDefault that basically
+// takes care of migrating the passwords of the users to login database. Once
+// all users are migrated we should delete this class.
 class PasswordStoreX : public password_manager::PasswordStoreDefault {
  public:
   // The state of the migration from native backends and an unencrypted loginDB
@@ -61,73 +58,7 @@
     FAILED_WRITE_TO_ENCRYPTED,
   };
 
-  // NativeBackends more or less implement the PaswordStore interface, but
-  // with return values rather than implicit consumer notification.
-  class NativeBackend {
-   public:
-    virtual ~NativeBackend() {}
-
-    virtual bool Init() = 0;
-
-    virtual password_manager::PasswordStoreChangeList AddLogin(
-        const autofill::PasswordForm& form) = 0;
-    // Updates |form| and appends the changes to |changes|. |changes| shouldn't
-    // be null. Returns false iff the operation failed due to a system backend
-    // error.
-    virtual bool UpdateLogin(
-        const autofill::PasswordForm& form,
-        password_manager::PasswordStoreChangeList* changes) = 0;
-    // Removes |form| and appends the changes to |changes|. |changes| shouldn't
-    // be null. Returns false iff the operation failed due to a system backend
-    // error.
-    virtual bool RemoveLogin(
-        const autofill::PasswordForm& form,
-        password_manager::PasswordStoreChangeList* changes) = 0;
-
-    // Removes all logins created/synced from |delete_begin| onwards (inclusive)
-    // and before |delete_end|. You may use a null Time value to do an unbounded
-    // delete in either direction.
-    virtual bool RemoveLoginsCreatedBetween(
-        base::Time delete_begin,
-        base::Time delete_end,
-        password_manager::PasswordStoreChangeList* changes) = 0;
-
-    // Sets the 'skip_zero_click' flag to 'true' for all logins in the database
-    // that match |origin_filter|.
-    virtual bool DisableAutoSignInForOrigins(
-        const base::Callback<bool(const GURL&)>& origin_filter,
-        password_manager::PasswordStoreChangeList* changes) = 0;
-
-    // The three methods below overwrite |forms| with all stored credentials
-    // matching |form|, all stored non-blacklisted credentials, and all stored
-    // blacklisted credentials, respectively. On success, they return true.
-    virtual bool GetLogins(const FormDigest& form,
-                           std::vector<std::unique_ptr<autofill::PasswordForm>>*
-                               forms) WARN_UNUSED_RESULT = 0;
-    virtual bool GetAutofillableLogins(
-        std::vector<std::unique_ptr<autofill::PasswordForm>>* forms)
-        WARN_UNUSED_RESULT = 0;
-    virtual bool GetBlacklistLogins(
-        std::vector<std::unique_ptr<autofill::PasswordForm>>* forms)
-        WARN_UNUSED_RESULT = 0;
-    virtual bool GetAllLogins(
-        std::vector<std::unique_ptr<autofill::PasswordForm>>* forms)
-        WARN_UNUSED_RESULT = 0;
-
-    // Returns the background thread in case the backend uses one, or null.
-    virtual scoped_refptr<base::SequencedTaskRunner>
-    GetBackgroundTaskRunner() = 0;
-  };
-
-  // |backend| may be NULL in which case this PasswordStoreX will act the same
-  // as PasswordStoreDefault. |login_db| is the default location and does not
-  // use encryption. |login_db_file| is the location of |login_db|.
-  // |encrypted_login_db_file| is a separate file and is used for the migration
-  // to encryption.
   PasswordStoreX(std::unique_ptr<password_manager::LoginDatabase> login_db,
-                 base::FilePath login_db_file,
-                 base::FilePath encrypted_login_db_file,
-                 std::unique_ptr<NativeBackend> backend,
                  PrefService* prefs);
 
   // RefcountedKeyedService:
@@ -143,8 +74,6 @@
       override;
 
  private:
-  friend class PasswordStoreXTest;
-
   ~PasswordStoreX() override;
 
   // Implements PasswordStore interface.
@@ -174,38 +103,9 @@
   bool FillBlacklistLogins(
       std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
 
-  // Check to see whether migration from the unencrypted loginDB is necessary,
-  // and perform it if so. Additionally, if the migration to encryption is
-  // enabled, then the passwords will also be copied into the encrypted login
-  // database and PasswordStoreX will serve from there. If this migration was
-  // completed in a previous run, CheckMigration will simply enable serving from
-  // the encrypted login database.
+  // Checks whether the login database is encrypted or not.
   void CheckMigration();
 
-  // Return true if we should try using the native backend.
-  bool use_native_backend() { return !!backend_.get(); }
-
-  // Return true if we can fall back on the default store, warning the first
-  // time we call it when falling back is necessary. See |allow_fallback_|.
-  bool allow_default_store();
-
-  // Synchronously migrates all the passwords stored in the login database
-  // (PasswordStoreDefault) to the native backend. If successful, the login
-  // database will be left with no stored passwords, and the number of passwords
-  // migrated will be returned. (This might be 0 if migration was not
-  // necessary.) Returns < 0 on failure.
-  ssize_t MigrateToNativeBackend();
-
-  // Moves the passwords from the backend to a temporary login database, using
-  // encryption, and then moves them over to the standard location. This
-  // operation can take a significant amount of time.
-  void MigrateToEncryptedLoginDB();
-
-  // Synchronously copies everything from the |backend_| to |login_db|. Returns
-  // COPIED_ALL on success and FAILED on error.
-  MigrationToLoginDBStep CopyBackendToLoginDB(
-      password_manager::LoginDatabase* login_db);
-
   // Update |migration_to_login_db_step_| and |migration_step_pref_|.
   void UpdateMigrationToLoginDBStep(MigrationToLoginDBStep step);
 
@@ -213,19 +113,8 @@
   // thread.
   void UpdateMigrationPref(MigrationToLoginDBStep step);
 
-  // The native backend in use, or NULL if none.
-  std::unique_ptr<NativeBackend> backend_;
-  // The location of the PasswordStoreDefault's database.
-  const base::FilePath login_db_file_;
-  // A second login database, which will hold encrypted values during migration.
-  const base::FilePath encrypted_login_db_file_;
   // Whether we have already attempted migration to the native store.
   bool migration_checked_;
-  // Whether we should allow falling back to the default store. If there is
-  // nothing to migrate, then the first attempt to use the native store will
-  // be the first time we try to use it and we should allow falling back. If
-  // we have migrated successfully, then we do not allow falling back.
-  bool allow_fallback_;
   // Tracks the last completed step in the migration from the native backends to
   // LoginDB.
   IntegerPrefMember migration_step_pref_;
diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc
index 2ed7679..3383d9cd 100644
--- a/chrome/browser/password_manager/password_store_x_unittest.cc
+++ b/chrome/browser/password_manager/password_store_x_unittest.cc
@@ -37,8 +37,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using autofill::PasswordForm;
+using password_manager::FormRetrievalResult;
 using password_manager::PasswordStoreChange;
 using password_manager::PasswordStoreChangeList;
+using password_manager::PrimaryKeyToFormMap;
 using password_manager::UnorderedPasswordFormElementsAre;
 using password_manager::metrics_util::LinuxBackendMigrationStatus;
 using testing::ElementsAreArray;
@@ -48,6 +50,9 @@
 
 namespace {
 
+const char kPassword[] = "password_value";
+const char kUsername[] = "username_value";
+const char kAnotherUsername[] = "another_username_value";
 class MockPasswordStoreConsumer
     : public password_manager::PasswordStoreConsumer {
  public:
@@ -61,701 +66,73 @@
   }
 };
 
-class FailingBackend : public PasswordStoreX::NativeBackend {
- public:
-  bool Init() override { return true; }
-
-  PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
-    return PasswordStoreChangeList();
-  }
-  bool UpdateLogin(const PasswordForm& form,
-                   PasswordStoreChangeList* changes) override {
-    return false;
-  }
-  bool RemoveLogin(const PasswordForm& form,
-                   PasswordStoreChangeList* changes) override {
-    return false;
-  }
-
-  bool RemoveLoginsCreatedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
-      password_manager::PasswordStoreChangeList* changes) override {
-    return false;
-  }
-
-  bool DisableAutoSignInForOrigins(
-      const base::Callback<bool(const GURL&)>& origin_filter,
-      password_manager::PasswordStoreChangeList* changes) override {
-    return false;
-  }
-
-  // Use this as a landmine to check whether results of failed Get*Logins calls
-  // get ignored.
-  static std::vector<std::unique_ptr<PasswordForm>> CreateTrashForms() {
-    std::vector<std::unique_ptr<PasswordForm>> forms;
-    PasswordForm trash;
-    trash.username_element = base::ASCIIToUTF16("trash u. element");
-    trash.username_value = base::ASCIIToUTF16("trash u. value");
-    trash.password_element = base::ASCIIToUTF16("trash p. element");
-    trash.password_value = base::ASCIIToUTF16("trash p. value");
-    for (size_t i = 0; i < 3; ++i) {
-      trash.origin = GURL(base::StringPrintf("http://trash%zu.com", i));
-      forms.push_back(std::make_unique<PasswordForm>(trash));
-    }
-    return forms;
-  }
-
-  bool GetLogins(const PasswordStore::FormDigest& form,
-                 std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    *forms = CreateTrashForms();
-    return false;
-  }
-
-  bool GetAutofillableLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    *forms = CreateTrashForms();
-    return false;
-  }
-
-  bool GetBlacklistLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    *forms = CreateTrashForms();
-    return false;
-  }
-
-  bool GetAllLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    *forms = CreateTrashForms();
-    return false;
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override {
-    return nullptr;
-  }
-};
-
-class MockBackend : public PasswordStoreX::NativeBackend {
- public:
-  ~MockBackend() override {
-    if (save_on_destruct_) {
-      *save_on_destruct_ = std::move(all_forms_);
-    }
-  }
-
-  bool Init() override { return true; }
-
-  PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
-    all_forms_.push_back(form);
-    PasswordStoreChange change(PasswordStoreChange::ADD, form);
-    return PasswordStoreChangeList(1, change);
-  }
-
-  bool UpdateLogin(const PasswordForm& form,
-                   PasswordStoreChangeList* changes) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i) {
-      if (ArePasswordFormUniqueKeysEqual(all_forms_[i], form)) {
-        all_forms_[i] = form;
-        changes->push_back(
-            PasswordStoreChange(PasswordStoreChange::UPDATE, form));
-      }
-    }
-    return true;
-  }
-
-  bool RemoveLogin(const PasswordForm& form,
-                   PasswordStoreChangeList* changes) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i) {
-      if (ArePasswordFormUniqueKeysEqual(all_forms_[i], form)) {
-        changes->push_back(
-            PasswordStoreChange(PasswordStoreChange::REMOVE, form));
-        erase(i--);
-      }
-    }
-    return true;
-  }
-
-  bool RemoveLoginsCreatedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
-      password_manager::PasswordStoreChangeList* changes) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i) {
-      if (delete_begin <= all_forms_[i].date_created &&
-          (delete_end.is_null() || all_forms_[i].date_created < delete_end))
-        erase(i--);
-    }
-    return true;
-  }
-
-  bool DisableAutoSignInForOrigins(
-      const base::Callback<bool(const GURL&)>& origin_filter,
-      password_manager::PasswordStoreChangeList* changes) override {
-    return true;
-  }
-
-  bool GetLogins(const PasswordStore::FormDigest& form,
-                 std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i)
-      if (all_forms_[i].signon_realm == form.signon_realm)
-        forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
-    return true;
-  }
-
-  bool GetAutofillableLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i)
-      if (!all_forms_[i].blacklisted_by_user)
-        forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
-    return true;
-  }
-
-  bool GetBlacklistLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i)
-      if (all_forms_[i].blacklisted_by_user)
-        forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
-    return true;
-  }
-
-  bool GetAllLogins(
-      std::vector<std::unique_ptr<PasswordForm>>* forms) override {
-    for (size_t i = 0; i < all_forms_.size(); ++i)
-      forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
-    return true;
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override {
-    return nullptr;
-  }
-
-  void SaveFormsOnDestruct(std::vector<PasswordForm>* forms) {
-    save_on_destruct_ = forms;
-  }
-
- private:
-  void erase(size_t index) {
-    if (index < all_forms_.size() - 1)
-      all_forms_[index] = all_forms_.back();
-    all_forms_.pop_back();
-  }
-
-  std::vector<PasswordForm>* save_on_destruct_ = nullptr;
-  std::vector<PasswordForm> all_forms_;
-};
-
-class MockLoginDatabaseReturn {
- public:
-  MOCK_METHOD1(OnLoginDatabaseQueryDone,
-               void(const std::vector<std::unique_ptr<PasswordForm>>&));
-};
-
-void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
-                                MockLoginDatabaseReturn* mock_return) {
-  password_manager::PrimaryKeyToFormMap key_to_form_map;
-  EXPECT_EQ(password_manager::FormRetrievalResult::kSuccess,
-            login_db->GetAllLogins(&key_to_form_map));
-  std::vector<std::unique_ptr<PasswordForm>> results;
-  results.reserve(key_to_form_map.size());
-  for (auto& key_to_form : key_to_form_map)
-    results.push_back(std::move(key_to_form.second));
-  mock_return->OnLoginDatabaseQueryDone(results);
-}
-
-// Generate |count| expected logins, either auto-fillable or blacklisted.
-void InitExpectedForms(bool autofillable,
-                       size_t count,
-                       std::vector<std::unique_ptr<PasswordForm>>* forms) {
-  const char* domain = autofillable ? "example" : "blacklisted";
-  for (size_t i = 0; i < count; ++i) {
-    std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
-    std::string origin =
-        base::StringPrintf("http://%zu.%s.com/origin", i, domain);
-    std::string action =
-        base::StringPrintf("http://%zu.%s.com/action", i, domain);
-    password_manager::PasswordFormData data = {
-        PasswordForm::Scheme::kHtml,
-        realm.c_str(),
-        origin.c_str(),
-        action.c_str(),
-        L"submit_element",
-        L"username_element",
-        L"password_element",
-        autofillable ? L"username_value" : nullptr,
-        autofillable ? L"password_value" : nullptr,
-        autofillable,
-        static_cast<double>(i + 1)};
-    forms->push_back(FillPasswordFormWithData(data));
-  }
-}
-
-PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
-  return PasswordStoreChangeList(
-      1, PasswordStoreChange(PasswordStoreChange::ADD, form));
-}
-
-enum BackendType { NO_BACKEND, FAILING_BACKEND, WORKING_BACKEND };
-
-std::unique_ptr<PasswordStoreX::NativeBackend> GetBackend(
-    BackendType backend_type) {
-  switch (backend_type) {
-    case FAILING_BACKEND:
-      return std::make_unique<FailingBackend>();
-    case WORKING_BACKEND:
-      return std::make_unique<MockBackend>();
-    default:
-      return std::unique_ptr<PasswordStoreX::NativeBackend>();
-  }
-}
-
-class PasswordStoreXTestDelegate {
- public:
-  PasswordStoreX* store() { return store_.get(); }
-
-  void FinishAsyncProcessing();
-
- protected:
-  explicit PasswordStoreXTestDelegate(BackendType backend_type);
-  ~PasswordStoreXTestDelegate();
-
- private:
-  void SetupTempDir();
-
-  base::FilePath test_login_db_file_path() const;
-  base::FilePath test_encrypted_login_db_file_path() const;
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  base::ScopedTempDir temp_dir_;
-  BackendType backend_type_;
-  scoped_refptr<PasswordStoreX> store_;
-  TestingPrefServiceSimple fake_pref_service;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordStoreXTestDelegate);
-};
-
-PasswordStoreXTestDelegate::PasswordStoreXTestDelegate(BackendType backend_type)
-    : backend_type_(backend_type) {
-  SetupTempDir();
-  auto login_db = std::make_unique<password_manager::LoginDatabase>(
-      test_login_db_file_path());
-  fake_pref_service.registry()->RegisterIntegerPref(
-      password_manager::prefs::kMigrationToLoginDBStep,
-      PasswordStoreX::NOT_ATTEMPTED);
-  store_ = new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                              test_encrypted_login_db_file_path(),
-                              GetBackend(backend_type_), &fake_pref_service);
-  store_->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
-}
-
-PasswordStoreXTestDelegate::~PasswordStoreXTestDelegate() {
-  store_->ShutdownOnUIThread();
-}
-
-void PasswordStoreXTestDelegate::FinishAsyncProcessing() {
-  task_environment_.RunUntilIdle();
-}
-
-void PasswordStoreXTestDelegate::SetupTempDir() {
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-}
-
-base::FilePath PasswordStoreXTestDelegate::test_login_db_file_path() const {
-  return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("login_test"));
-}
-
-base::FilePath PasswordStoreXTestDelegate::test_encrypted_login_db_file_path()
-    const {
-  return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("encrypted_login_test"));
-}
-
-class PasswordStoreXNoBackendTestDelegate : public PasswordStoreXTestDelegate {
- public:
-  PasswordStoreXNoBackendTestDelegate()
-      : PasswordStoreXTestDelegate(NO_BACKEND) {}
-};
-
-class PasswordStoreXWorkingBackendTestDelegate
-    : public PasswordStoreXTestDelegate {
- public:
-  PasswordStoreXWorkingBackendTestDelegate()
-      : PasswordStoreXTestDelegate(WORKING_BACKEND) {
-    // Working backends are switched for LoginDatabase with encryption.
-    OSCryptMocker::SetUp();
-  }
-  ~PasswordStoreXWorkingBackendTestDelegate() { OSCryptMocker::TearDown(); }
-};
-
-std::vector<std::unique_ptr<PasswordForm>> ReadLoginDB(
-    const base::FilePath& path,
-    bool encrypted) {
-  password_manager::LoginDatabase login_db(path);
-  if (!encrypted)
-    login_db.disable_encryption();
-  EXPECT_TRUE(login_db.Init());
-  std::vector<std::unique_ptr<PasswordForm>> stored_forms;
-  EXPECT_TRUE(login_db.GetAutofillableLogins(&stored_forms));
-  return stored_forms;
+PasswordForm MakePasswordForm() {
+  PasswordForm form;
+  form.origin = GURL("http://www.origin.com");
+  form.username_element = base::UTF8ToUTF16("username_element");
+  form.username_value = base::UTF8ToUTF16(kUsername);
+  form.password_element = base::UTF8ToUTF16("password_element");
+  form.username_value = base::UTF8ToUTF16(kPassword);
+  form.signon_realm = form.origin.GetOrigin().spec();
+  return form;
 }
 
 }  // namespace
 
-namespace password_manager {
-
-INSTANTIATE_TYPED_TEST_SUITE_P(XNoBackend,
-                               PasswordStoreOriginTest,
-                               PasswordStoreXNoBackendTestDelegate);
-
-INSTANTIATE_TYPED_TEST_SUITE_P(XWorkingBackend,
-                               PasswordStoreOriginTest,
-                               PasswordStoreXWorkingBackendTestDelegate);
-}  // namespace password_manager
-
-class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
- protected:
-  PasswordStoreXTest() = default;
-
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+class PasswordStoreXTest : public testing::Test {
+ public:
+  PasswordStoreXTest() {
+    ignore_result(temp_dir_.CreateUniqueTempDir());
     fake_pref_service_.registry()->RegisterIntegerPref(
         password_manager::prefs::kMigrationToLoginDBStep,
         PasswordStoreX::NOT_ATTEMPTED);
     OSCryptMocker::SetUp();
   }
 
-  void TearDown() override { OSCryptMocker::TearDown(); }
+  ~PasswordStoreXTest() override { OSCryptMocker::TearDown(); }
+
+  PrefService* fake_pref_service() { return &fake_pref_service_; }
 
   base::FilePath test_login_db_file_path() const {
     return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("login_test"));
   }
 
-  base::FilePath test_encrypted_login_db_file_path() const {
-    return temp_dir_.GetPath().Append(
-        FILE_PATH_LITERAL("encrypted_login_test"));
-  }
-
   void WaitForPasswordStore() { task_environment_.RunUntilIdle(); }
 
- protected:
-  TestingPrefServiceSimple fake_pref_service_;
-  base::HistogramTester histogram_tester_;
-
  private:
+  TestingPrefServiceSimple fake_pref_service_;
   base::test::ScopedTaskEnvironment task_environment_;
   base::ScopedTempDir temp_dir_;
 
   DISALLOW_COPY_AND_ASSIGN(PasswordStoreXTest);
 };
 
-TEST_P(PasswordStoreXTest, Notifications) {
-  std::unique_ptr<password_manager::LoginDatabase> login_db(
-      new password_manager::LoginDatabase(test_login_db_file_path()));
-  scoped_refptr<PasswordStoreX> store(
-      new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                         test_encrypted_login_db_file_path(),
-                         GetBackend(GetParam()), &fake_pref_service_));
-  store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
-
-  password_manager::PasswordFormData form_data = {
-      PasswordForm::Scheme::kHtml,
-      "http://bar.example.com",
-      "http://bar.example.com/origin",
-      "http://bar.example.com/action",
-      L"submit_element",
-      L"username_element",
-      L"password_element",
-      L"username_value",
-      L"password_value",
-      true,
-      1};
-  std::unique_ptr<PasswordForm> form = FillPasswordFormWithData(form_data);
-
-  password_manager::MockPasswordStoreObserver observer;
-  store->AddObserver(&observer);
-
-  const PasswordStoreChange expected_add_changes[] = {
-      PasswordStoreChange(PasswordStoreChange::ADD, *form),
-  };
-
-  EXPECT_CALL(observer,
-              OnLoginsChanged(ElementsAreArray(expected_add_changes)));
-
-  // Adding a login should trigger a notification.
-  store->AddLogin(*form);
-
-  WaitForPasswordStore();
-
-  // Change the password.
-  form->password_value = base::ASCIIToUTF16("a different password");
-
-  const PasswordStoreChange expected_update_changes[] = {
-      PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
-  };
-
-  EXPECT_CALL(observer,
-              OnLoginsChanged(ElementsAreArray(expected_update_changes)));
-
-  // Updating the login with the new password should trigger a notification.
-  store->UpdateLogin(*form);
-
-  WaitForPasswordStore();
-
-  const PasswordStoreChange expected_delete_changes[] = {
-      PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
-  };
-
-  EXPECT_CALL(observer,
-              OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
-
-  // Deleting the login should trigger a notification.
-  store->RemoveLogin(*form);
-
-  WaitForPasswordStore();
-
-  store->RemoveObserver(&observer);
-
-  store->ShutdownOnUIThread();
-}
-
-TEST_P(PasswordStoreXTest, NativeMigration) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndDisableFeature(
-      password_manager::features::kMigrateLinuxToLoginDB);
-
-  std::vector<std::unique_ptr<PasswordForm>> expected_forms;
-  InitExpectedForms(true, 5, &expected_forms);
-  InitExpectedForms(false, 5, &expected_forms);
-
-  const base::FilePath login_db_file = test_login_db_file_path();
-  std::unique_ptr<password_manager::LoginDatabase> login_db(
-      new password_manager::LoginDatabase(login_db_file));
-  login_db->disable_encryption();
-  ASSERT_TRUE(login_db->Init());
-
-  // Get the initial size of the login DB file, before we populate it.
-  // This will be used later to make sure it gets back to this size.
-  base::File::Info db_file_start_info;
-  ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
-
-  // Populate the login DB with logins that should be migrated.
-  for (const auto& form : expected_forms) {
-    EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
-  }
-
-  // Get the new size of the login DB file. We expect it to be larger.
-  base::File::Info db_file_full_info;
-  ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
-  EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
-
-  // Initializing the PasswordStore shouldn't trigger a native migration (yet).
-  login_db.reset(new password_manager::LoginDatabase(login_db_file));
-  scoped_refptr<PasswordStoreX> store(
-      new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                         test_encrypted_login_db_file_path(),
-                         GetBackend(GetParam()), &fake_pref_service_));
-  store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
-
-  MockPasswordStoreConsumer consumer;
-
-  // All forms should have been migrated to the native backend.
-  EXPECT_CALL(consumer, OnGetPasswordStoreResultsConstRef(
-                            UnorderedPasswordFormElementsAre(&expected_forms)));
-  store->GetAllLogins(&consumer);
-  WaitForPasswordStore();
-
-  MockLoginDatabaseReturn ld_return;
-
-  if (GetParam() == WORKING_BACKEND) {
-    // No logins should be left in the login DB.
-    EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
-  } else {
-    // All logins should still be in the login DB.
-    EXPECT_CALL(ld_return,
-                OnLoginDatabaseQueryDone(
-                    UnorderedPasswordFormElementsAre(&expected_forms)));
-  }
-
-  LoginDatabaseQueryCallback(store->login_db(), &ld_return);
-  WaitForPasswordStore();
-
-  if (GetParam() == WORKING_BACKEND) {
-    // If the migration succeeded, then not only should there be no logins left
-    // in the login DB, but also the file should have been deleted and then
-    // recreated. We approximate checking for this by checking that the file
-    // size is equal to the size before we populated it, even though it was
-    // larger after populating it.
-    base::File::Info db_file_end_info;
-    ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
-    EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
-  }
-
-  store->ShutdownOnUIThread();
-}
-
-TEST_P(PasswordStoreXTest, MigrationToEncryption) {
+TEST_F(PasswordStoreXTest, MigrationCompleted) {
   IntegerPrefMember migration_step_pref_;
   migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
-                            &fake_pref_service_);
-
-  EXPECT_EQ(PasswordStoreX::NOT_ATTEMPTED, migration_step_pref_.GetValue());
-
-  // Add existing credentials into the backend.
-  std::vector<std::unique_ptr<PasswordForm>> old_credentials;
-  InitExpectedForms(true, 6, &old_credentials);
-  std::unique_ptr<PasswordStoreX::NativeBackend> backend =
-      GetBackend(GetParam());
-  std::vector<PasswordForm> native_backend_last_state;
-  if (GetParam() == WORKING_BACKEND) {
-    static_cast<MockBackend*>(backend.get())
-        ->SaveFormsOnDestruct(&native_backend_last_state);
-  }
-  if (GetParam() != NO_BACKEND) {
-    for (int i = 0; i < 3; i++)
-      backend->AddLogin(*old_credentials[i]);
-  }
-
-  // Add existing credentials into the unencrypted loginDB.
-  auto login_db = std::make_unique<password_manager::LoginDatabase>(
-      test_login_db_file_path());
-  login_db->disable_encryption();
-  ASSERT_TRUE(login_db->Init());
-  for (int i = 3; i < 6; i++)
-    ignore_result(login_db->AddLogin(*old_credentials[i]));
-
-  login_db = std::make_unique<password_manager::LoginDatabase>(
-      test_login_db_file_path());
-  scoped_refptr<PasswordStoreX> store(
-      new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                         test_encrypted_login_db_file_path(),
-                         std::move(backend), &fake_pref_service_));
-  store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
-
-  // Make modifications, so that we trigger the migration and so that we can
-  // verify where the store serves from. The migration is triggered
-  // opportunistically during access to the store.
-  const auto new_form = password_manager::FillPasswordFormWithData(
-      {PasswordForm::Scheme::kHtml, "https://www.fakebook.com",
-       "https://www.fakebook.com/li", "https://www.fakebook.com/a",
-       L"submit_element", L"username_element", L"password_element",
-       L"username_value", L"password_value", true, 1.0});
-  store->RemoveLogin(*old_credentials[0]);
-  store->AddLogin(*new_form);
-
-  MockPasswordStoreConsumer consumer;
-  if (GetParam() == WORKING_BACKEND) {
-    // The store has the native backend data, the initial unencrypted loginDB
-    // data and modifications.
-    EXPECT_CALL(
-        consumer,
-        OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
-            Pointee(*new_form), Pointee(*old_credentials[1]),
-            Pointee(*old_credentials[2]), Pointee(*old_credentials[3]),
-            Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
-  } else {
-    // The has the initial unencrypted loginDB data and modifications.
-    EXPECT_CALL(
-        consumer,
-        OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
-            Pointee(*new_form), Pointee(*old_credentials[3]),
-            Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
-  }
-  store->GetAutofillableLogins(&consumer);
-
-  WaitForPasswordStore();
-  store->ShutdownOnUIThread();
-  store.reset();
-  WaitForPasswordStore();
-
-  // This will report that it was migrated on the next run.
-  histogram_tester_.ExpectBucketCount(
-      "PasswordManager.LinuxBackendMigration.Adoption",
-      LinuxBackendMigrationStatus::kNotAttempted, 1);
-
-  if (GetParam() == WORKING_BACKEND) {
-    // Verify that the login database contains all the values, now encrypted.
-    std::vector<std::unique_ptr<PasswordForm>> stored_forms =
-        ReadLoginDB(test_login_db_file_path(), true);
-    EXPECT_EQ(6u, stored_forms.size());
-    EXPECT_THAT(
-        stored_forms,
-        UnorderedElementsAre(
-            Pointee(*new_form), Pointee(*old_credentials[1]),
-            Pointee(*old_credentials[2]), Pointee(*old_credentials[3]),
-            Pointee(*old_credentials[4]), Pointee(*old_credentials[5])));
-    EXPECT_TRUE(native_backend_last_state.empty());
-
-    stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), false);
-    EXPECT_TRUE(stored_forms.empty());
-    EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED,
-              migration_step_pref_.GetValue());
-
-    histogram_tester_.ExpectBucketCount(
-        "PasswordManager.LinuxBackendMigration.AttemptResult",
-        LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
-  } else if (GetParam() == FAILING_BACKEND) {
-    // No values should be written if we can't read the backend.
-    auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
-    EXPECT_TRUE(stored_forms.empty());
-    EXPECT_THAT(migration_step_pref_.GetValue(), PasswordStoreX::POSTPONED);
-
-    histogram_tester_.ExpectBucketCount(
-        "PasswordManager.LinuxBackendMigration.AttemptResult",
-        LinuxBackendMigrationStatus::kPostponed, 1);
-  } else {  // NO_BACKEND
-    // No values should be moved without a working backend.
-    auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
-    EXPECT_TRUE(stored_forms.empty());
-    EXPECT_THAT(migration_step_pref_.GetValue(), PasswordStoreX::NOT_ATTEMPTED);
-  }
-}
-
-// Once the migration is performed, don't port anything else into the new
-// location.
-TEST_P(PasswordStoreXTest, MigrationToEncryption_OnlyOnce) {
-  if (GetParam() != WORKING_BACKEND)
-    return;
-
-  IntegerPrefMember migration_step_pref_;
-  migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
-                            &fake_pref_service_);
+                            fake_pref_service());
   // Signal that the migration has been completed.
   migration_step_pref_.SetValue(PasswordStoreX::LOGIN_DB_REPLACED);
 
-  // We add new credentials into a backend. They should be completely ignored by
-  // the store.
-  std::vector<std::unique_ptr<PasswordForm>> old_credentials;
-  InitExpectedForms(true, 6, &old_credentials);
-  auto backend = GetBackend(GetParam());
-  for (int i = 0; i < 3; i++)
-    backend->AddLogin(*old_credentials[i]);
-
-  // Add existing credentials into loginDB. They should be the only thing that's
+  // Add existing credential into loginDB. It should be the only thing that's
   // available in the store.
   auto login_db = std::make_unique<password_manager::LoginDatabase>(
       test_login_db_file_path());
   ASSERT_TRUE(login_db->Init());
-  for (int i = 3; i < 6; i++)
-    ignore_result(login_db->AddLogin(*old_credentials[i]));
+  ignore_result(login_db->AddLogin(MakePasswordForm()));
   login_db.reset();
 
   // Create the store.
   login_db = std::make_unique<password_manager::LoginDatabase>(
       test_login_db_file_path());
   scoped_refptr<PasswordStoreX> store =
-      new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                         test_encrypted_login_db_file_path(),
-                         std::move(backend), &fake_pref_service_);
+      new PasswordStoreX(std::move(login_db), fake_pref_service());
   store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
 
-  // Make modifications and check the contents.
+  // Check the contents are still around.
   MockPasswordStoreConsumer consumer;
-  EXPECT_CALL(consumer,
-              OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
-                  Pointee(*old_credentials[3]), Pointee(*old_credentials[4]),
-                  Pointee(*old_credentials[5]))));
-  store->GetAutofillableLogins(&consumer);
-  store->RemoveLogin(*old_credentials[3]);
-  EXPECT_CALL(consumer,
-              OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
-                  Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
+  EXPECT_CALL(consumer, OnGetPasswordStoreResultsConstRef(
+                            ElementsAre(Pointee(MakePasswordForm()))));
   store->GetAutofillableLogins(&consumer);
 
   WaitForPasswordStore();
@@ -763,87 +140,107 @@
   store.reset();
   WaitForPasswordStore();
 
-  // The previous results were served from an encrypted login database.
-  std::vector<std::unique_ptr<PasswordForm>> stored_forms =
-      ReadLoginDB(test_login_db_file_path(), true);
-  EXPECT_EQ(2u, stored_forms.size());
-  EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED, migration_step_pref_.GetValue());
-  EXPECT_THAT(stored_forms, UnorderedElementsAre(Pointee(*old_credentials[4]),
-                                                 Pointee(*old_credentials[5])));
-
-  histogram_tester_.ExpectBucketCount(
-      "PasswordManager.LinuxBackendMigration.Adoption",
-      LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
-  histogram_tester_.ExpectTotalCount(
-      "PasswordManager.LinuxBackendMigration.AttemptResult", 0);
+  // Check if the database is encrypted.
+  password_manager::LoginDatabase login_db2(test_login_db_file_path());
+  // Disable encryption.
+  login_db2.disable_encryption();
+  EXPECT_TRUE(login_db2.Init());
+  // Read the password again.
+  std::vector<std::unique_ptr<PasswordForm>> stored_forms;
+  EXPECT_TRUE(login_db2.GetAutofillableLogins(&stored_forms));
+  EXPECT_EQ(1U, stored_forms.size());
+  // Password values don't match because they have been stored encrypted and
+  // read unencrypted.
+  EXPECT_NE(kPassword, base::UTF16ToUTF8(stored_forms[0]->password_value));
 }
 
-TEST_P(PasswordStoreXTest, MigrationToEncryption_DropIllegalEntries) {
-  if (GetParam() != WORKING_BACKEND)
-    return;
-
+TEST_F(PasswordStoreXTest, MigrationNotAttemptedEmptyDB) {
   IntegerPrefMember migration_step_pref_;
   migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
-                            &fake_pref_service_);
+                            fake_pref_service());
+  // Signal that the migration has not been attempted.
+  migration_step_pref_.SetValue(PasswordStoreX::NOT_ATTEMPTED);
 
-  EXPECT_EQ(PasswordStoreX::NOT_ATTEMPTED, migration_step_pref_.GetValue());
-
-  // Add existing credentials into the backend.
-  std::vector<std::unique_ptr<PasswordForm>> old_credentials;
-  InitExpectedForms(true, 4, &old_credentials);
-  // Create illegal entries.
-  old_credentials[1]->origin = GURL();
-  old_credentials[3]->signon_realm.clear();
-
-  std::unique_ptr<PasswordStoreX::NativeBackend> backend =
-      GetBackend(GetParam());
-  std::vector<PasswordForm> native_backend_last_state;
-  static_cast<MockBackend*>(backend.get())
-      ->SaveFormsOnDestruct(&native_backend_last_state);
-  for (int i = 0; i < 3; i++)
-    backend->AddLogin(*old_credentials[i]);
-
+  // Create the store with an empty database.
   auto login_db = std::make_unique<password_manager::LoginDatabase>(
       test_login_db_file_path());
-  scoped_refptr<PasswordStoreX> store(
-      new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
-                         test_encrypted_login_db_file_path(),
-                         std::move(backend), &fake_pref_service_));
+  password_manager::LoginDatabase* login_db_ptr = login_db.get();
+
+  scoped_refptr<PasswordStoreX> store =
+      new PasswordStoreX(std::move(login_db), fake_pref_service());
   store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
-
-  MockPasswordStoreConsumer consumer;
-  // The store has the native backend data, minus the illegal entries.
-  // The call to GetAutofillableLogins() both triggers the opportunistic
-  // migration and returns the data for the test.
-  EXPECT_CALL(consumer,
-              OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
-                  Pointee(*old_credentials[0]), Pointee(*old_credentials[2]))));
-  store->GetAutofillableLogins(&consumer);
-
   WaitForPasswordStore();
+
+  // Add a password to the db.
+  PasswordStoreChangeList changes = login_db_ptr->AddLogin(MakePasswordForm());
+  EXPECT_EQ(1U, changes.size());
+
   store->ShutdownOnUIThread();
   store.reset();
   WaitForPasswordStore();
 
-  // Verify that the login database contains all the values, now encrypted.
-  std::vector<std::unique_ptr<PasswordForm>> stored_forms =
-      ReadLoginDB(test_login_db_file_path(), true);
-  EXPECT_EQ(2u, stored_forms.size());
-  EXPECT_THAT(stored_forms, UnorderedElementsAre(Pointee(*old_credentials[0]),
-                                                 Pointee(*old_credentials[2])));
-  EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED, migration_step_pref_.GetValue());
-
-  histogram_tester_.ExpectBucketCount(
-      "PasswordManager.LinuxBackendMigration.AttemptResult",
-      LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
+  // Check if the database is encrypted.
+  password_manager::LoginDatabase login_db2(test_login_db_file_path());
+  login_db2.disable_encryption();
+  EXPECT_TRUE(login_db2.Init());
+  // Read the password again.
+  std::vector<std::unique_ptr<PasswordForm>> stored_forms;
+  EXPECT_TRUE(login_db2.GetAutofillableLogins(&stored_forms));
+  EXPECT_EQ(1U, stored_forms.size());
+  // Password values don't match because they have been stored encrypted and
+  // read unencrypted.
+  EXPECT_NE(kPassword, base::UTF16ToUTF8(stored_forms[0]->password_value));
 }
 
-INSTANTIATE_TEST_SUITE_P(NoBackend,
-                         PasswordStoreXTest,
-                         testing::Values(NO_BACKEND));
-INSTANTIATE_TEST_SUITE_P(FailingBackend,
-                         PasswordStoreXTest,
-                         testing::Values(FAILING_BACKEND));
-INSTANTIATE_TEST_SUITE_P(WorkingBackend,
-                         PasswordStoreXTest,
-                         testing::Values(WORKING_BACKEND));
+TEST_F(PasswordStoreXTest, MigrationNotAttemptedNonEmptyDB) {
+  IntegerPrefMember migration_step_pref_;
+  migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
+                            fake_pref_service());
+  // Signal that the migration has not been attempted.
+  migration_step_pref_.SetValue(PasswordStoreX::NOT_ATTEMPTED);
+
+  // Add existing credential into loginDB.
+  auto login_db = std::make_unique<password_manager::LoginDatabase>(
+      test_login_db_file_path());
+  ASSERT_TRUE(login_db->Init());
+  ignore_result(login_db->AddLogin(MakePasswordForm()));
+  login_db.reset();
+
+  // Create the store with a non-empty database.
+  login_db = std::make_unique<password_manager::LoginDatabase>(
+      test_login_db_file_path());
+  password_manager::LoginDatabase* login_db_ptr = login_db.get();
+
+  scoped_refptr<PasswordStoreX> store =
+      new PasswordStoreX(std::move(login_db), fake_pref_service());
+  store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
+  WaitForPasswordStore();
+
+  // Add another password to the db.
+  PasswordForm form = MakePasswordForm();
+  form.username_value = base::UTF8ToUTF16(kAnotherUsername);
+  PasswordStoreChangeList changes = login_db_ptr->AddLogin(form);
+  EXPECT_EQ(1U, changes.size());
+
+  store->ShutdownOnUIThread();
+  store.reset();
+  WaitForPasswordStore();
+
+  // Check if the database is unencrypted.
+  password_manager::LoginDatabase login_db2(test_login_db_file_path());
+  // Disable encryption.
+  login_db2.disable_encryption();
+  EXPECT_TRUE(login_db2.Init());
+  // Read the password again.
+  std::vector<std::unique_ptr<PasswordForm>> stored_forms;
+  EXPECT_TRUE(login_db2.GetAutofillableLogins(&stored_forms));
+  EXPECT_EQ(2U, stored_forms.size());
+  for (const std::unique_ptr<PasswordForm>& stored_form : stored_forms) {
+    if (base::UTF16ToUTF8(stored_forms[0]->username_value) ==
+        kAnotherUsername) {
+      // Password values match because they have been stored unencrypted and
+      // read unencrypted.
+      EXPECT_EQ(kPassword, base::UTF16ToUTF8(stored_form->password_value));
+    }
+  }
+}
diff --git a/chrome/browser/platform_util.cc b/chrome/browser/platform_util.cc
index 43f63c1..b3300f13 100644
--- a/chrome/browser/platform_util.cc
+++ b/chrome/browser/platform_util.cc
@@ -62,8 +62,14 @@
               OpenItemType item_type,
               const OpenOperationCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TaskPriority::USER_BLOCKING because this is usually opened as a result of a
+  // user action (e.g. open-downloaded-file or show-item-in-folder).
+  // TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN because this doesn't need global
+  // state and can hang shutdown without this trait as it may result in an
+  // interactive dialog.
   base::PostTaskWithTraits(FROM_HERE,
-                           {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+                           {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
+                            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
                            base::BindOnce(&VerifyAndOpenItemOnBlockingThread,
                                           full_path, item_type, callback));
 }
diff --git a/chrome/browser/platform_util_internal.h b/chrome/browser/platform_util_internal.h
index 0b93b7dcd..3c6f02c 100644
--- a/chrome/browser/platform_util_internal.h
+++ b/chrome/browser/platform_util_internal.h
@@ -16,8 +16,9 @@
 
 // Called by platform_util.cc on desktop platforms to invoke platform specific
 // logic to open |path| using a suitable handler. |path| has been verified to be
-// of type |type|.
-// Always called on the blocking pool.
+// of type |type|. Called on the thread pool with
+// base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN semantics (and thus can't
+// use global state torn down during shutdown).
 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type);
 
 // Prevent shell or external applications from being invoked during testing.
diff --git a/chrome/browser/platform_util_linux.cc b/chrome/browser/platform_util_linux.cc
index 9fb1a7a..f16d0eb 100644
--- a/chrome/browser/platform_util_linux.cc
+++ b/chrome/browser/platform_util_linux.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "base/version.h"
 #include "chrome/browser/platform_util_internal.h"
 #include "content/public/browser/browser_thread.h"
@@ -136,6 +137,9 @@
 namespace internal {
 
 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
+  // May result in an interactive dialog.
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
   switch (type) {
     case OPEN_FILE:
       XDGOpen(path.DirName(), path.value());
diff --git a/chrome/browser/platform_util_win.cc b/chrome/browser/platform_util_win.cc
index 9d15886..3cb79b0 100644
--- a/chrome/browser/platform_util_win.cc
+++ b/chrome/browser/platform_util_win.cc
@@ -122,6 +122,9 @@
 namespace internal {
 
 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
+  // May result in an interactive dialog.
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
   switch (type) {
     case OPEN_FILE:
       ui::win::OpenFileViaShell(path);
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc
index 0d79ed2..f8d158b 100644
--- a/chrome/browser/policy/policy_conversions.cc
+++ b/chrome/browser/policy/policy_conversions.cc
@@ -53,67 +53,17 @@
 namespace em = enterprise_management;
 
 namespace policy {
-
-// Fills |policy_dump| with device specific information if this device is
-// enterprise managed.
-void FillIdentityFields(Value* policy_dump);
-
 namespace {
 
-// Maps known policy names to their schema. If a policy is not present, it is
-// not known (either through policy_templates.json or through an extenion's
-// managed storage schema).
-using PolicyToSchemaMap = base::flat_map<std::string, Schema>;
-
-// Utility function that returns a JSON serialization of the given |dict|.
-std::string DictionaryToJSONString(const Value& dict, bool is_pretty_print) {
-  std::string json_string;
-  base::JSONWriter::WriteWithOptions(
-      dict, (is_pretty_print ? base::JSONWriter::OPTIONS_PRETTY_PRINT : 0),
-      &json_string);
-  return json_string;
-}
-
-// Returns a copy of |value|. If necessary (which is specified by
-// |convert_values|), converts some values to a representation that
-// i18n_template.js will display.
-Value CopyAndMaybeConvert(const Value& value,
-                          bool convert_values,
-                          const base::Optional<Schema>& schema,
-                          bool is_pretty_print) {
-  Value value_copy = value.Clone();
-  if (schema.has_value())
-    schema->MaskSensitiveValues(&value_copy);
-  if (!convert_values)
-    return value_copy;
-  if (value_copy.is_dict())
-    return Value(DictionaryToJSONString(value_copy, is_pretty_print));
-
-  if (!value_copy.is_list()) {
-    return value_copy;
-  }
-
-  Value result(Value::Type::LIST);
-  for (const auto& element : value_copy.GetList()) {
-    if (element.is_dict()) {
-      result.GetList().emplace_back(
-          Value(DictionaryToJSONString(element, is_pretty_print)));
-    } else {
-      result.GetList().push_back(element.Clone());
-    }
-  }
-  return result;
-}
-
-PolicyService* GetPolicyService(content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
+PolicyService* GetPolicyService(Profile* profile) {
   return profile->GetProfilePolicyConnector()->policy_service();
 }
 
 // Returns the Schema for |policy_name| if that policy is known. If the policy
 // is unknown, returns |base::nullopt|.
 base::Optional<Schema> GetKnownPolicySchema(
-    const base::Optional<PolicyToSchemaMap>& known_policy_schemas,
+    const base::Optional<PolicyConversions::PolicyToSchemaMap>&
+        known_policy_schemas,
     const std::string& policy_name) {
   if (!known_policy_schemas.has_value())
     return base::nullopt;
@@ -123,30 +73,307 @@
   return known_policy_iterator->second;
 }
 
-// Create a description of the policy |policy_name| using |policy| and the
-// optional errors in |errors| to determine the status of each policy. If
-// |convert_values| is true, converts the values to show them in javascript.
-// |known_policy_schemas| contains |Schema|s for known policies in the same
-// policy namespace of |map|. A policy without an entry in
-// |known_policy_schemas| is an unknown policy.
-// When |convert_types| is true, policy types are converted into string. For
-// example, POLICY_SCOPE_USER is converted to 'user'. Otherwise, policy types
-// are returned as integers.
-Value GetPolicyValue(
+base::Optional<PolicyConversions::PolicyToSchemaMap> GetKnownPolicies(
+    const scoped_refptr<SchemaMap> schema_map,
+    const PolicyNamespace& policy_namespace) {
+  const Schema* schema = schema_map->GetSchema(policy_namespace);
+  // There is no policy name verification without valid schema.
+  if (!schema || !schema->valid())
+    return base::nullopt;
+
+  // Build a vector first and construct the PolicyToSchemaMap (which is a
+  // |flat_map|) from that. The reason is that insertion into a |flat_map| is
+  // O(n), which would make the loop O(n^2), but constructing from a
+  // pre-populated vector is less expensive.
+  std::vector<std::pair<std::string, Schema>> policy_to_schema_entries;
+  for (auto it = schema->GetPropertiesIterator(); !it.IsAtEnd(); it.Advance()) {
+    policy_to_schema_entries.push_back(std::make_pair(it.key(), it.schema()));
+  }
+  return PolicyConversions::PolicyToSchemaMap(
+      std::move(policy_to_schema_entries));
+}
+
+}  // namespace
+
+const LocalizedString kPolicySources[POLICY_SOURCE_COUNT] = {
+    {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT},
+    {"cloud", IDS_POLICY_SOURCE_CLOUD},
+    {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY},
+    {"sourceDeviceLocalAccountOverride",
+     IDS_POLICY_SOURCE_DEVICE_LOCAL_ACCOUNT_OVERRIDE},
+    {"platform", IDS_POLICY_SOURCE_PLATFORM},
+    {"priorityCloud", IDS_POLICY_SOURCE_CLOUD},
+    {"merged", IDS_POLICY_SOURCE_MERGED},
+};
+
+PolicyConversions::PolicyConversions() = default;
+PolicyConversions::~PolicyConversions() = default;
+
+PolicyConversions& PolicyConversions::WithBrowserContext(
+    content::BrowserContext* context) {
+  profile_ = Profile::FromBrowserContext(
+      chrome::GetBrowserContextRedirectedInIncognito(context));
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnableConvertTypes(bool enabled) {
+  convert_types_enabled_ = enabled;
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnableConvertValues(bool enabled) {
+  convert_values_enabled_ = enabled;
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnableDevicePolicies(bool enabled) {
+  device_policies_enabled_ = enabled;
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnableDeviceInfo(bool enabled) {
+  device_info_enabled_ = enabled;
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnablePrettyPrint(bool enabled) {
+  pretty_print_enabled_ = enabled;
+  return *this;
+}
+
+PolicyConversions& PolicyConversions::EnableUserPolicies(bool enabled) {
+  user_policies_enabled_ = enabled;
+  return *this;
+}
+
+std::string PolicyConversions::ToJSON() {
+  return ConvertValueToJSON(ToValue());
+}
+
+Value PolicyConversions::GetChromePolicies() {
+  PolicyService* policy_service = GetPolicyService(profile_);
+  PolicyMap map;
+
+  auto* schema_registry_service = profile_->GetPolicySchemaRegistryService();
+  if (!schema_registry_service || !schema_registry_service->registry()) {
+    LOG(ERROR) << "Can not dump Chrome policies, no schema registry service";
+    return Value(Value::Type::DICTIONARY);
+  }
+
+  const scoped_refptr<SchemaMap> schema_map =
+      schema_registry_service->registry()->schema_map();
+
+  PolicyNamespace policy_namespace =
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Make a copy that can be modified, since some policy values are modified
+  // before being displayed.
+  map.CopyFrom(policy_service->GetPolicies(policy_namespace));
+
+  // Get a list of all the errors in the policy values.
+  const ConfigurationPolicyHandlerList* handler_list =
+      g_browser_process->browser_policy_connector()->GetHandlerList();
+  PolicyErrorMap errors;
+  handler_list->ApplyPolicySettings(map, NULL, &errors);
+
+  // Convert dictionary values to strings for display.
+  handler_list->PrepareForDisplaying(&map);
+
+  return GetPolicyValues(map, &errors,
+                         GetKnownPolicies(schema_map, policy_namespace));
+}
+
+Value PolicyConversions::GetExtensionsPolicies() {
+  Value policies(Value::Type::LIST);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // Add extension policy values.
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(profile_);
+  if (!registry) {
+    LOG(ERROR) << "Can not dump extension policies, no extension registry";
+    return policies;
+  }
+  auto* schema_registry_service = profile_->GetPolicySchemaRegistryService();
+  if (!schema_registry_service || !schema_registry_service->registry()) {
+    LOG(ERROR) << "Can not dump extension policies, no schema registry service";
+    return policies;
+  }
+  const scoped_refptr<SchemaMap> schema_map =
+      schema_registry_service->registry()->schema_map();
+  for (const scoped_refptr<const extensions::Extension>& extension :
+       registry->enabled_extensions()) {
+    // Skip this extension if it's not an enterprise extension.
+    if (!extension->manifest()->HasPath(
+            extensions::manifest_keys::kStorageManagedSchema)) {
+      continue;
+    }
+
+    PolicyNamespace policy_namespace =
+        PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, extension->id());
+    PolicyErrorMap empty_error_map;
+    Value extension_policies = GetPolicyValues(
+        GetPolicyService(profile_)->GetPolicies(policy_namespace),
+        &empty_error_map, GetKnownPolicies(schema_map, policy_namespace));
+    Value extension_policies_data(Value::Type::DICTIONARY);
+    extension_policies_data.SetKey("name", Value(extension->name()));
+    extension_policies_data.SetKey("id", Value(extension->id()));
+    extension_policies_data.SetKey("policies", std::move(extension_policies));
+    policies.GetList().push_back(std::move(extension_policies_data));
+  }
+#endif
+  return policies;
+}
+
+#if defined(OS_CHROMEOS)
+Value PolicyConversions::GetDeviceLocalAccountPolicies() {
+  Value policies(Value::Type::LIST);
+  // DeviceLocalAccount policies are only available for affiliated users and for
+  // system logs.
+  if (!device_policies_enabled_ &&
+      (!user_manager::UserManager::IsInitialized() ||
+       !user_manager::UserManager::Get()->GetPrimaryUser() ||
+       !user_manager::UserManager::Get()->GetPrimaryUser()->IsAffiliated())) {
+    return policies;
+  }
+
+  BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  DCHECK(connector);  // always not-null
+
+  auto* device_local_account_policy_service =
+      connector->GetDeviceLocalAccountPolicyService();
+  DCHECK(device_local_account_policy_service);  // always non null for
+                                                // affiliated users
+  std::vector<DeviceLocalAccount> device_local_accounts =
+      GetDeviceLocalAccounts(chromeos::CrosSettings::Get());
+  for (const auto& account : device_local_accounts) {
+    std::string user_id = account.user_id;
+
+    auto* device_local_account_policy_broker =
+        device_local_account_policy_service->GetBrokerForUser(user_id);
+    if (!device_local_account_policy_broker) {
+      LOG(ERROR)
+          << "Can not get policy broker for device local account with user id: "
+          << user_id;
+      continue;
+    }
+
+    auto* cloud_policy_core = device_local_account_policy_broker->core();
+    DCHECK(cloud_policy_core);
+    auto* cloud_policy_store = cloud_policy_core->store();
+    DCHECK(cloud_policy_store);
+
+    const scoped_refptr<SchemaMap> schema_map =
+        device_local_account_policy_broker->schema_registry()->schema_map();
+
+    PolicyNamespace policy_namespace =
+        PolicyNamespace(POLICY_DOMAIN_CHROME, std::string());
+
+    // Make a copy that can be modified, since some policy values are modified
+    // before being displayed.
+    PolicyMap map;
+    map.CopyFrom(cloud_policy_store->policy_map());
+
+    // Get a list of all the errors in the policy values.
+    const ConfigurationPolicyHandlerList* handler_list =
+        connector->GetHandlerList();
+    PolicyErrorMap errors;
+    handler_list->ApplyPolicySettings(map, NULL, &errors);
+
+    // Convert dictionary values to strings for display.
+    handler_list->PrepareForDisplaying(&map);
+
+    Value current_account_policies = GetPolicyValues(
+        map, &errors, GetKnownPolicies(schema_map, policy_namespace));
+    Value current_account_policies_data(Value::Type::DICTIONARY);
+    current_account_policies_data.SetKey("id", Value(user_id));
+    current_account_policies_data.SetKey("user_id", Value(user_id));
+    current_account_policies_data.SetKey("name", Value(user_id));
+    current_account_policies_data.SetKey("policies",
+                                         std::move(current_account_policies));
+    policies.GetList().push_back(std::move(current_account_policies_data));
+  }
+  return policies;
+}
+
+Value PolicyConversions::GetIdentityFields() {
+  Value identity_fields(Value::Type::DICTIONARY);
+  if (!device_info_enabled_)
+    return Value();
+  BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  if (!connector) {
+    LOG(ERROR) << "Can not dump identity fields, no policy connector";
+    return Value();
+  }
+  if (connector->IsEnterpriseManaged()) {
+    identity_fields.SetKey("enrollment_domain",
+                           Value(connector->GetEnterpriseEnrollmentDomain()));
+
+    if (connector->IsActiveDirectoryManaged()) {
+      Value active_directory_info = GetIdentityFieldsFromPolicy(
+          connector->GetDeviceActiveDirectoryPolicyManager()
+              ->store()
+              ->policy());
+      identity_fields.MergeDictionary(&active_directory_info);
+    }
+
+    if (connector->IsCloudManaged()) {
+      Value cloud_info = GetIdentityFieldsFromPolicy(
+          connector->GetDeviceCloudPolicyManager()->device_store()->policy());
+      identity_fields.MergeDictionary(&cloud_info);
+    }
+  }
+  return identity_fields;
+}
+#endif
+
+std::string PolicyConversions::ConvertValueToJSON(const Value& value) {
+  std::string json_string;
+  base::JSONWriter::WriteWithOptions(
+      value,
+      (pretty_print_enabled_ ? base::JSONWriter::OPTIONS_PRETTY_PRINT : 0),
+      &json_string);
+  return json_string;
+}
+
+Value PolicyConversions::CopyAndMaybeConvert(
+    const Value& value,
+    const base::Optional<Schema>& schema) {
+  Value value_copy = value.Clone();
+  if (schema.has_value())
+    schema->MaskSensitiveValues(&value_copy);
+  if (!convert_values_enabled_)
+    return value_copy;
+  if (value_copy.is_dict())
+    return Value(ConvertValueToJSON(value_copy));
+
+  if (!value_copy.is_list()) {
+    return value_copy;
+  }
+
+  Value result(Value::Type::LIST);
+  for (const auto& element : value_copy.GetList()) {
+    if (element.is_dict()) {
+      result.GetList().emplace_back(Value(ConvertValueToJSON(element)));
+    } else {
+      result.GetList().push_back(element.Clone());
+    }
+  }
+  return result;
+}
+
+Value PolicyConversions::GetPolicyValue(
     const std::string& policy_name,
     const PolicyMap::Entry& policy,
     PolicyErrorMap* errors,
-    bool convert_values,
-    const base::Optional<PolicyToSchemaMap>& known_policy_schemas,
-    bool is_pretty_print,
-    bool convert_types) {
+    const base::Optional<PolicyToSchemaMap>& known_policy_schemas) {
   base::Optional<Schema> known_policy_schema =
       GetKnownPolicySchema(known_policy_schemas, policy_name);
   Value value(Value::Type::DICTIONARY);
   value.SetKey("value",
-               CopyAndMaybeConvert(*policy.value, convert_values,
-                                   known_policy_schema, is_pretty_print));
-  if (convert_types) {
+               CopyAndMaybeConvert(*policy.value, known_policy_schema));
+  if (convert_types_enabled_) {
     value.SetKey(
         "scope",
         Value((policy.scope == POLICY_SCOPE_USER) ? "user" : "machine"));
@@ -194,8 +421,7 @@
     Value conflict_values(Value::Type::LIST);
     for (const auto& conflict : policy.conflicts) {
       base::Value conflicted_policy_value =
-          GetPolicyValue(policy_name, conflict, errors, convert_values,
-                         known_policy_schemas, is_pretty_print, convert_types);
+          GetPolicyValue(policy_name, conflict, errors, known_policy_schemas);
       conflict_values.GetList().push_back(std::move(conflicted_policy_value));
     }
 
@@ -205,396 +431,195 @@
   return value;
 }
 
-// Inserts a description of each policy in |map| into |values|, using the
-// optional errors in |errors| to determine the status of each policy. If
-// |convert_values| is true, converts the values to show them in javascript.
-// |known_policy_schemas| contains |Schema|s for known policies in the same
-// policy namespace of |map|. A policy in |map| but without an entry
-// |known_policy_schemas| is an unknown policy.
-void GetPolicyValues(
+Value PolicyConversions::GetPolicyValues(
     const PolicyMap& map,
     PolicyErrorMap* errors,
-    bool with_user_policies,
-    bool convert_values,
-    const base::Optional<PolicyToSchemaMap>& known_policy_schemas,
-    Value* values,
-    bool is_pretty_print,
-    bool convert_types) {
-  DCHECK(values);
+    const base::Optional<PolicyToSchemaMap>& known_policy_schemas) {
+  base::Value values(base::Value::Type::DICTIONARY);
   for (const auto& entry : map) {
     const std::string& policy_name = entry.first;
     const PolicyMap::Entry& policy = entry.second;
-    if (policy.scope == POLICY_SCOPE_USER && !with_user_policies)
+    if (policy.scope == POLICY_SCOPE_USER && !user_policies_enabled_)
       continue;
     base::Value value =
-        GetPolicyValue(policy_name, policy, errors, convert_values,
-                       known_policy_schemas, is_pretty_print, convert_types);
-    values->SetKey(policy_name, std::move(value));
+        GetPolicyValue(policy_name, policy, errors, known_policy_schemas);
+    values.SetKey(policy_name, std::move(value));
   }
-}
-
-base::Optional<PolicyToSchemaMap> GetKnownPolicies(
-    const scoped_refptr<SchemaMap> schema_map,
-    const PolicyNamespace& policy_namespace,
-    bool is_pretty_print) {
-  const Schema* schema = schema_map->GetSchema(policy_namespace);
-  // There is no policy name verification without valid schema.
-  if (!schema || !schema->valid())
-    return base::nullopt;
-
-  // Build a vector first and construct the PolicyToSchemaMap (which is a
-  // |flat_map|) from that. The reason is that insertion into a |flat_map| is
-  // O(n), which would make the loop O(n^2), but constructing from a
-  // pre-populated vector is less expensive.
-  std::vector<std::pair<std::string, Schema>> policy_to_schema_entries;
-  for (auto it = schema->GetPropertiesIterator(); !it.IsAtEnd(); it.Advance()) {
-    policy_to_schema_entries.push_back(std::make_pair(it.key(), it.schema()));
-  }
-  return PolicyToSchemaMap(std::move(policy_to_schema_entries));
-}
-
-void GetChromePolicyValues(content::BrowserContext* context,
-                           bool keep_user_policies,
-                           bool convert_values,
-                           Value* values,
-                           bool is_pretty_print,
-                           bool convert_types) {
-  PolicyService* policy_service = GetPolicyService(context);
-  PolicyMap map;
-
-  Profile* profile = Profile::FromBrowserContext(context);
-  auto* schema_registry_service = profile->GetPolicySchemaRegistryService();
-  if (!schema_registry_service || !schema_registry_service->registry()) {
-    LOG(ERROR) << "Can not dump extension policies, no schema registry service";
-    return;
-  }
-
-  const scoped_refptr<SchemaMap> schema_map =
-      schema_registry_service->registry()->schema_map();
-
-  PolicyNamespace policy_namespace =
-      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string());
-
-  // Make a copy that can be modified, since some policy values are modified
-  // before being displayed.
-  map.CopyFrom(policy_service->GetPolicies(policy_namespace));
-
-  // Get a list of all the errors in the policy values.
-  const ConfigurationPolicyHandlerList* handler_list =
-      g_browser_process->browser_policy_connector()->GetHandlerList();
-  PolicyErrorMap errors;
-  handler_list->ApplyPolicySettings(map, NULL, &errors);
-
-  // Convert dictionary values to strings for display.
-  handler_list->PrepareForDisplaying(&map);
-
-  GetPolicyValues(
-      map, &errors, keep_user_policies, convert_values,
-      GetKnownPolicies(schema_map, policy_namespace, is_pretty_print), values,
-      is_pretty_print, convert_types);
+  return values;
 }
 
 #if defined(OS_CHROMEOS)
-void GetDeviceLocalAccountPolicies(bool convert_values,
-                                   Value* values,
-                                   bool with_device_data,
-                                   bool is_pretty_print,
-                                   bool convert_types) {
-  // DeviceLocalAccount policies are only available for affiliated users and for
-  // system logs.
-  if (!with_device_data &&
-      (!user_manager::UserManager::IsInitialized() ||
-       !user_manager::UserManager::Get()->GetPrimaryUser() ||
-       !user_manager::UserManager::Get()->GetPrimaryUser()->IsAffiliated())) {
-    return;
-  }
-
-  BrowserPolicyConnectorChromeOS* connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
-  DCHECK(connector);  // always not-null
-
-  auto* device_local_account_policy_service =
-      connector->GetDeviceLocalAccountPolicyService();
-  DCHECK(device_local_account_policy_service);  // always non null for
-                                                // affiliated users
-  std::vector<DeviceLocalAccount> device_local_accounts =
-      GetDeviceLocalAccounts(chromeos::CrosSettings::Get());
-  for (const auto& account : device_local_accounts) {
-    std::string user_id = account.user_id;
-    Value current_account_policies(Value::Type::DICTIONARY);
-
-    auto* device_local_account_policy_broker =
-        device_local_account_policy_service->GetBrokerForUser(user_id);
-    if (!device_local_account_policy_broker) {
-      LOG(ERROR)
-          << "Can not get policy broker for device local account with user id: "
-          << user_id;
-      continue;
-    }
-
-    auto* cloud_policy_core = device_local_account_policy_broker->core();
-    DCHECK(cloud_policy_core);
-    auto* cloud_policy_store = cloud_policy_core->store();
-    DCHECK(cloud_policy_store);
-
-    const scoped_refptr<SchemaMap> schema_map =
-        device_local_account_policy_broker->schema_registry()->schema_map();
-
-    PolicyNamespace policy_namespace =
-        PolicyNamespace(POLICY_DOMAIN_CHROME, std::string());
-
-    // Make a copy that can be modified, since some policy values are modified
-    // before being displayed.
-    PolicyMap map;
-    map.CopyFrom(cloud_policy_store->policy_map());
-
-    // Get a list of all the errors in the policy values.
-    const ConfigurationPolicyHandlerList* handler_list =
-        connector->GetHandlerList();
-    PolicyErrorMap errors;
-    handler_list->ApplyPolicySettings(map, NULL, &errors);
-
-    // Convert dictionary values to strings for display.
-    handler_list->PrepareForDisplaying(&map);
-
-    GetPolicyValues(
-        map, &errors, true, convert_values,
-        GetKnownPolicies(schema_map, policy_namespace, is_pretty_print),
-        &current_account_policies, is_pretty_print, convert_types);
-
-    if (values->is_list()) {
-      Value current_account_policies_data(Value::Type::DICTIONARY);
-      current_account_policies_data.SetKey("id", Value(user_id));
-      current_account_policies_data.SetKey("user_id", Value(user_id));
-      current_account_policies_data.SetKey("name", Value(user_id));
-      current_account_policies_data.SetKey("policies",
-                                           std::move(current_account_policies));
-      values->GetList().push_back(std::move(current_account_policies_data));
-    } else {
-      values->SetKey(user_id, std::move(current_account_policies));
-    }
-  }
-}
-#endif  // defined(OS_CHROMEOS)
-
-}  // namespace
-
-const LocalizedString kPolicySources[POLICY_SOURCE_COUNT] = {
-    {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT},
-    {"cloud", IDS_POLICY_SOURCE_CLOUD},
-    {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY},
-    {"sourceDeviceLocalAccountOverride",
-     IDS_POLICY_SOURCE_DEVICE_LOCAL_ACCOUNT_OVERRIDE},
-    {"platform", IDS_POLICY_SOURCE_PLATFORM},
-    {"priorityCloud", IDS_POLICY_SOURCE_CLOUD},
-    {"merged", IDS_POLICY_SOURCE_MERGED},
-};
-
-Value GetAllPolicyValuesAsArray(content::BrowserContext* context,
-                                bool with_user_policies,
-                                bool convert_values,
-                                bool with_device_data,
-                                bool is_pretty_print,
-                                bool convert_types) {
-  Value all_policies(Value::Type::LIST);
-  DCHECK(context);
-
-  context = chrome::GetBrowserContextRedirectedInIncognito(context);
-
-  // Add Chrome policy values.
-  Value chrome_policies(Value::Type::DICTIONARY);
-  GetChromePolicyValues(context, with_user_policies, convert_values,
-                        &chrome_policies, is_pretty_print, convert_types);
-  Value chrome_policies_data(Value::Type::DICTIONARY);
-  chrome_policies_data.SetKey("name", Value("Chrome Policies"));
-  chrome_policies_data.SetKey("policies", std::move(chrome_policies));
-
-  all_policies.GetList().push_back(std::move(chrome_policies_data));
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // Add extension policy values.
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(Profile::FromBrowserContext(context));
-  if (!registry) {
-    LOG(ERROR) << "Can not dump extension policies, no extension registry";
-    return all_policies;
-  }
-  Profile* profile = Profile::FromBrowserContext(context);
-  auto* schema_registry_service = profile->GetPolicySchemaRegistryService();
-  if (!schema_registry_service || !schema_registry_service->registry()) {
-    LOG(ERROR) << "Can not dump extension policies, no schema registry service";
-    return all_policies;
-  }
-  const scoped_refptr<SchemaMap> schema_map =
-      schema_registry_service->registry()->schema_map();
-  for (const scoped_refptr<const extensions::Extension>& extension :
-       registry->enabled_extensions()) {
-    // Skip this extension if it's not an enterprise extension.
-    if (!extension->manifest()->HasPath(
-            extensions::manifest_keys::kStorageManagedSchema)) {
-      continue;
-    }
-
-    Value extension_policies(Value::Type::DICTIONARY);
-    PolicyNamespace policy_namespace =
-        PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, extension->id());
-    PolicyErrorMap empty_error_map;
-    GetPolicyValues(
-        GetPolicyService(context)->GetPolicies(policy_namespace),
-        &empty_error_map, with_user_policies, convert_values,
-        GetKnownPolicies(schema_map, policy_namespace, is_pretty_print),
-        &extension_policies, is_pretty_print, convert_types);
-    Value extension_policies_data(Value::Type::DICTIONARY);
-    extension_policies_data.SetKey("name", Value(extension->name()));
-    extension_policies_data.SetKey("id", Value(extension->id()));
-    extension_policies_data.SetKey("policies", std::move(extension_policies));
-    all_policies.GetList().push_back(std::move(extension_policies_data));
-  }
-#endif
-
-#if defined(OS_CHROMEOS)
-  Value device_local_account_policies(Value::Type::DICTIONARY);
-  GetDeviceLocalAccountPolicies(convert_values, &all_policies, with_device_data,
-                                is_pretty_print, convert_types);
-#endif  // defined(OS_CHROMEOS)
-
-  return all_policies;
-}
-
-Value GetAllPolicyValuesAsDictionary(content::BrowserContext* context,
-                                     bool with_user_policies,
-                                     bool convert_values,
-                                     bool with_device_data,
-                                     bool is_pretty_print,
-                                     bool convert_types) {
-  Value all_policies(Value::Type::DICTIONARY);
-  if (!context) {
-    LOG(ERROR) << "Can not dump policies, null context";
-    return all_policies;
-  }
-
-  context = chrome::GetBrowserContextRedirectedInIncognito(context);
-
-  // Add Chrome policy values.
-  Value chrome_policies(Value::Type::DICTIONARY);
-  GetChromePolicyValues(context, with_user_policies, convert_values,
-                        &chrome_policies, is_pretty_print, convert_types);
-  all_policies.SetKey("chromePolicies", std::move(chrome_policies));
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // Add extension policy values.
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(Profile::FromBrowserContext(context));
-  if (!registry) {
-    LOG(ERROR) << "Can not dump extension policies, no extension registry";
-    return all_policies;
-  }
-  Value extension_values(Value::Type::DICTIONARY);
-  Profile* profile = Profile::FromBrowserContext(context);
-  auto* schema_registry_service = profile->GetPolicySchemaRegistryService();
-  if (!schema_registry_service || !schema_registry_service->registry()) {
-    LOG(ERROR) << "Can not dump extension policies, no schema registry service";
-    return all_policies;
-  }
-  const scoped_refptr<SchemaMap> schema_map =
-      schema_registry_service->registry()->schema_map();
-  for (const scoped_refptr<const extensions::Extension>& extension :
-       registry->enabled_extensions()) {
-    // Skip this extension if it's not an enterprise extension.
-    if (!extension->manifest()->HasPath(
-            extensions::manifest_keys::kStorageManagedSchema))
-      continue;
-    Value extension_policies(Value::Type::DICTIONARY);
-    PolicyNamespace policy_namespace =
-        PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, extension->id());
-    PolicyErrorMap empty_error_map;
-    GetPolicyValues(
-        GetPolicyService(context)->GetPolicies(policy_namespace),
-        &empty_error_map, with_user_policies, convert_values,
-        GetKnownPolicies(schema_map, policy_namespace, is_pretty_print),
-        &extension_policies, is_pretty_print, convert_types);
-    extension_values.SetKey(extension->id(), std::move(extension_policies));
-  }
-  all_policies.SetKey("extensionPolicies", std::move(extension_values));
-#endif
-
-#if defined(OS_CHROMEOS)
-  Value device_local_account_policies(Value::Type::DICTIONARY);
-  GetDeviceLocalAccountPolicies(convert_values, &device_local_account_policies,
-                                with_device_data, is_pretty_print,
-                                convert_types);
-  all_policies.SetKey("deviceLocalAccountPolicies",
-                      std::move(device_local_account_policies));
-#endif  // defined(OS_CHROMEOS)
-
-  if (with_device_data)
-    FillIdentityFields(&all_policies);
-
-  return all_policies;
-}
-
-#if defined(OS_CHROMEOS)
-void FillIdentityFieldsFromPolicy(const em::PolicyData* policy,
-                                  Value* policy_dump) {
+Value PolicyConversions::GetIdentityFieldsFromPolicy(
+    const em::PolicyData* policy) {
+  Value identity_fields(Value::Type::DICTIONARY);
   if (!policy) {
-    return;
+    return identity_fields;
   }
-  DCHECK(policy_dump);
 
   if (policy->has_device_id())
-    policy_dump->SetKey("client_id", Value(policy->device_id()));
+    identity_fields.SetKey("client_id", Value(policy->device_id()));
 
-  if (policy->has_annotated_location())
-    policy_dump->SetKey("device_location", Value(policy->annotated_location()));
+  if (policy->has_annotated_location()) {
+    identity_fields.SetKey("device_location",
+                           Value(policy->annotated_location()));
+  }
 
   if (policy->has_annotated_asset_id())
-    policy_dump->SetKey("asset_id", Value(policy->annotated_asset_id()));
+    identity_fields.SetKey("asset_id", Value(policy->annotated_asset_id()));
 
   if (policy->has_display_domain())
-    policy_dump->SetKey("display_domain", Value(policy->display_domain()));
+    identity_fields.SetKey("display_domain", Value(policy->display_domain()));
 
   if (policy->has_machine_name())
-    policy_dump->SetKey("machine_name", Value(policy->machine_name()));
+    identity_fields.SetKey("machine_name", Value(policy->machine_name()));
+
+  return identity_fields;
 }
+
 #endif  // defined(OS_CHROMEOS)
 
-void FillIdentityFields(Value* policy_dump) {
+/**
+ * DictionaryPolicyConversions
+ */
+
+DictionaryPolicyConversions::DictionaryPolicyConversions() = default;
+DictionaryPolicyConversions::~DictionaryPolicyConversions() = default;
+
+Value DictionaryPolicyConversions::ToValue() {
+  Value all_policies(Value::Type::DICTIONARY);
+
+  if (profile()) {
+    all_policies.SetKey("chromePolicies", GetChromePolicies());
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    all_policies.SetKey("extensionPolicies", GetExtensionsPolicies());
+#endif
+  }
+
 #if defined(OS_CHROMEOS)
-  DCHECK(policy_dump);
-  BrowserPolicyConnectorChromeOS* connector =
-      g_browser_process->platform_part()->browser_policy_connector_chromeos();
-  if (!connector) {
-    LOG(ERROR) << "Can not dump identity fields, no policy connector";
-    return;
-  }
-  if (connector->IsEnterpriseManaged()) {
-    policy_dump->SetKey("enrollment_domain",
-                        Value(connector->GetEnterpriseEnrollmentDomain()));
-
-    if (connector->IsActiveDirectoryManaged()) {
-      FillIdentityFieldsFromPolicy(
-          connector->GetDeviceActiveDirectoryPolicyManager()->store()->policy(),
-          policy_dump);
-    }
-
-    if (connector->IsCloudManaged()) {
-      FillIdentityFieldsFromPolicy(
-          connector->GetDeviceCloudPolicyManager()->device_store()->policy(),
-          policy_dump);
-    }
-  }
+  all_policies.SetKey("deviceLocalAccountPolicies",
+                      GetDeviceLocalAccountPolicies());
+  Value identity_fields = GetIdentityFields();
+  if (!identity_fields.is_none())
+    all_policies.MergeDictionary(&identity_fields);
 #endif  // defined(OS_CHROMEOS)
+  return all_policies;
+}
+
+#if defined(OS_CHROMEOS)
+Value DictionaryPolicyConversions::GetDeviceLocalAccountPolicies() {
+  Value policies = PolicyConversions::GetDeviceLocalAccountPolicies();
+  Value device_values(Value::Type::DICTIONARY);
+  for (auto&& policy : policies.GetList()) {
+    device_values.SetKey(policy.FindKey("id")->GetString(),
+                         std::move(*policy.FindKey("policies")));
+  }
+  return device_values;
+}
+#endif
+
+Value DictionaryPolicyConversions::GetExtensionsPolicies() {
+  Value policies = PolicyConversions::GetExtensionsPolicies();
+  Value extension_values(Value::Type::DICTIONARY);
+  for (auto&& policy : policies.GetList()) {
+    extension_values.SetKey(policy.FindKey("id")->GetString(),
+                            std::move(*policy.FindKey("policies")));
+  }
+  return extension_values;
+}
+
+/**
+ * ArrayPolicyConversions
+ */
+
+ArrayPolicyConversions::ArrayPolicyConversions() = default;
+ArrayPolicyConversions::~ArrayPolicyConversions() = default;
+
+Value ArrayPolicyConversions::ToValue() {
+  Value all_policies(Value::Type::LIST);
+
+  if (profile()) {
+    all_policies.GetList().push_back(GetChromePolicies());
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    Value extension_policies = GetExtensionsPolicies();
+    all_policies.GetList().insert(
+        all_policies.GetList().end(),
+        std::make_move_iterator(extension_policies.GetList().begin()),
+        std::make_move_iterator(extension_policies.GetList().end()));
+#endif
+  }
+
+#if defined(OS_CHROMEOS)
+  Value device_policeis = GetDeviceLocalAccountPolicies();
+  all_policies.GetList().insert(
+      all_policies.GetList().end(),
+      std::make_move_iterator(device_policeis.GetList().begin()),
+      std::make_move_iterator(device_policeis.GetList().end()));
+
+  Value identity_fields = GetIdentityFields();
+  if (!identity_fields.is_none())
+    all_policies.GetList().push_back(std::move(identity_fields));
+#endif  // defined(OS_CHROMEOS)
+
+  return all_policies;
+}
+
+Value ArrayPolicyConversions::GetChromePolicies() {
+  Value chrome_policies_data(Value::Type::DICTIONARY);
+  chrome_policies_data.SetKey("name", Value("Chrome Policies"));
+  chrome_policies_data.SetKey("policies",
+                              PolicyConversions::GetChromePolicies());
+  return chrome_policies_data;
+}
+
+base::Value GetAllPolicyValuesAsArray(content::BrowserContext* context,
+                                      bool with_user_policies,
+                                      bool convert_values,
+                                      bool with_device_data,
+                                      bool is_pretty_print,
+                                      bool convert_types) {
+  return ArrayPolicyConversions()
+      .WithBrowserContext(context)
+      .EnableConvertTypes(convert_types)
+      .EnableConvertValues(convert_values)
+      .EnableDevicePolicies(with_device_data)
+      .EnableDeviceInfo(false)
+      .EnablePrettyPrint(is_pretty_print)
+      .EnableUserPolicies(with_user_policies)
+      .ToValue();
+}
+
+base::Value GetAllPolicyValuesAsDictionary(content::BrowserContext* context,
+                                           bool with_user_policies,
+                                           bool convert_values,
+                                           bool with_device_data,
+                                           bool is_pretty_print,
+                                           bool convert_types) {
+  return DictionaryPolicyConversions()
+      .WithBrowserContext(context)
+      .EnableConvertTypes(convert_types)
+      .EnableConvertValues(convert_values)
+      .EnableDevicePolicies(with_device_data)
+      .EnableDeviceInfo(with_device_data)
+      .EnablePrettyPrint(is_pretty_print)
+      .EnableUserPolicies(with_user_policies)
+      .ToValue();
 }
 
 std::string GetAllPolicyValuesAsJSON(content::BrowserContext* context,
                                      bool with_user_policies,
                                      bool with_device_data,
                                      bool is_pretty_print) {
-  Value all_policies = GetAllPolicyValuesAsDictionary(
-      context, with_user_policies, false /* convert_values */, with_device_data,
-      is_pretty_print, true /* convert_types */);
-
-  return DictionaryToJSONString(all_policies, is_pretty_print);
+  return DictionaryPolicyConversions()
+      .WithBrowserContext(context)
+      .EnableConvertTypes(true)
+      .EnableConvertValues(false)
+      .EnableDevicePolicies(with_device_data)
+      .EnableDeviceInfo(with_device_data)
+      .EnablePrettyPrint(is_pretty_print)
+      .EnableUserPolicies(with_user_policies)
+      .ToJSON();
 }
 
 }  // namespace policy
diff --git a/chrome/browser/policy/policy_conversions.h b/chrome/browser/policy/policy_conversions.h
index f1e3b1ff..1204d2f 100644
--- a/chrome/browser/policy/policy_conversions.h
+++ b/chrome/browser/policy/policy_conversions.h
@@ -10,23 +10,161 @@
 
 #include "base/values.h"
 #include "chrome/browser/ui/webui/localized_string.h"
+#include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_types.h"
 
+class Profile;
+
 namespace content {
 class BrowserContext;
 }  // namespace content
 
+namespace enterprise_management {
+class PolicyData;
+}
+
 namespace policy {
 
-// TODO(crbug.com/983174): Refactor the file as these functions have too many
-// parameters.
+class PolicyErrorMap;
+class Schema;
 
 extern const LocalizedString kPolicySources[POLICY_SOURCE_COUNT];
 
+// A convenience class to retrieve all policies values.
+class PolicyConversions {
+ public:
+  // Maps known policy names to their schema. If a policy is not present, it is
+  // not known (either through policy_templates.json or through an extenion's
+  // managed storage schema).
+  using PolicyToSchemaMap = base::flat_map<std::string, Schema>;
+
+  PolicyConversions();
+  virtual ~PolicyConversions();
+
+  // Set to get Chrome and extension policies.
+  PolicyConversions& WithBrowserContext(content::BrowserContext* context);
+  // Set to get policy types as human friendly string instead of enum integer.
+  // Policy types includes policy source, policy scope and policy level.
+  // Enabled by default.
+  PolicyConversions& EnableConvertTypes(bool enabled);
+  // Set to get dictionary policy value as JSON string.
+  // Disabled by default.
+  PolicyConversions& EnableConvertValues(bool enabled);
+  // Set to get device policies on ChromeOS.
+  // Disabled by default.
+  PolicyConversions& EnableDevicePolicies(bool enabled);
+  // Set to get device basic information on ChromeOS.
+  // Disabled by default.
+  PolicyConversions& EnableDeviceInfo(bool enabled);
+  // Set to enable pretty print for all JSON string.
+  // Enabled by default.
+  PolicyConversions& EnablePrettyPrint(bool enabled);
+  // Set to get all user scope policies.
+  // Enabled by default.
+  PolicyConversions& EnableUserPolicies(bool enabled);
+
+  // Returns the policy data as a base::Value object.
+  virtual base::Value ToValue() = 0;
+
+  // Returns the policy data as a JSON string;
+  virtual std::string ToJSON();
+
+ protected:
+  const Profile* profile() const { return profile_; }
+
+  // Returns policies for Chrome browser.
+  virtual base::Value GetChromePolicies();
+  // Returns policies for Chrome extensions.
+  virtual base::Value GetExtensionsPolicies();
+#if defined(OS_CHROMEOS)
+  // Returns policies for ChromeOS device.
+  virtual base::Value GetDeviceLocalAccountPolicies();
+  // Returns device specific information if this device is enterprise managed.
+  virtual base::Value GetIdentityFields();
+#endif
+
+  std::string ConvertValueToJSON(const base::Value& value);
+
+ private:
+  // Returns a copy of |value|. If necessary (which is specified by
+  // |convert_values_enabled_|), converts some values to a representation that
+  // i18n_template.js will display.
+  base::Value CopyAndMaybeConvert(const base::Value& value,
+                                  const base::Optional<Schema>& schema);
+
+  // Creates a description of the policy |policy_name| using |policy| and the
+  // optional errors in |errors| to determine the status of each policy.
+  // |known_policy_schemas| contains |Schema|s for known policies in the same
+  // policy namespace of |map|. A policy without an entry in
+  // |known_policy_schemas| is an unknown policy.
+  base::Value GetPolicyValue(
+      const std::string& policy_name,
+      const PolicyMap::Entry& policy,
+      PolicyErrorMap* errors,
+      const base::Optional<PolicyToSchemaMap>& known_policy_schemas);
+
+  // Returns a description of each policy in |map| as Value, using the
+  // optional errors in |errors| to determine the status of each policy.
+  // |known_policy_schemas| contains |Schema|s for known policies in the same
+  // policy namespace of |map|. A policy in |map| but without an entry
+  // |known_policy_schemas| is an unknown policy.
+  base::Value GetPolicyValues(
+      const PolicyMap& map,
+      PolicyErrorMap* errors,
+      const base::Optional<PolicyToSchemaMap>& known_policy_schemas);
+
+#if defined(OS_CHROMEOS)
+  base::Value GetIdentityFieldsFromPolicy(
+      const enterprise_management::PolicyData* policy);
+#endif
+
+  Profile* profile_;
+
+  bool convert_types_enabled_ = true;
+  bool convert_values_enabled_ = false;
+  bool device_policies_enabled_ = false;
+  bool device_info_enabled_ = false;
+  bool pretty_print_enabled_ = true;
+  bool user_policies_enabled_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyConversions);
+};
+
+class DictionaryPolicyConversions : public PolicyConversions {
+ public:
+  DictionaryPolicyConversions();
+  ~DictionaryPolicyConversions() override;
+
+  base::Value ToValue() override;
+
+ private:
+  base::Value GetExtensionsPolicies() override;
+
+#if defined(OS_CHROMEOS)
+  base::Value GetDeviceLocalAccountPolicies() override;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(DictionaryPolicyConversions);
+};
+
+class ArrayPolicyConversions : public PolicyConversions {
+ public:
+  ArrayPolicyConversions();
+  ~ArrayPolicyConversions() override;
+
+  base::Value ToValue() override;
+
+ private:
+  base::Value GetChromePolicies() override;
+
+  DISALLOW_COPY_AND_ASSIGN(ArrayPolicyConversions);
+};
+
 // Returns an array with the values of all set policies, with some values
 // converted to be shown in javascript, if it is specified.
 // |with_user_policies| governs if values with POLICY_SCOPE_USER are included.
 // |convert_types| governs if policy types are returned as string.
+// DEPRECATED, use ArrayPolicyConversions::ToValue() instead.
 base::Value GetAllPolicyValuesAsArray(content::BrowserContext* context,
                                       bool with_user_policies,
                                       bool convert_values,
@@ -42,6 +180,7 @@
 // it is used in logs uploads to the server.
 // |is_pretty_print| governs if JSON policy value is pretty printed.
 // |convert_types| governs if policy types are returned as string.
+// DEPRECATED, use DictionaryPolicyConversions::ToValue() instead.
 base::Value GetAllPolicyValuesAsDictionary(content::BrowserContext* context,
                                            bool with_user_policies,
                                            bool convert_values,
@@ -55,6 +194,7 @@
 // enrollment client ID) and device local accounts policies are included,
 // it is used in logs uploads to the server.
 // |is_pretty_print| governs if the output is formatted.
+// DEPRECATED, use DictionaryPolicyConversions::ToJSON() instead.
 std::string GetAllPolicyValuesAsJSON(content::BrowserContext* context,
                                      bool with_user_policies,
                                      bool with_device_data,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 17f773f..a76821e 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/accessibility/accessibility_labels_service.h"
 #include "chrome/browser/accessibility/accessibility_ui.h"
 #include "chrome/browser/accessibility/invert_bubble_prefs.h"
+#include "chrome/browser/availability/availability_prober.h"
 #include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/chromeos/policy/tpm_auto_update_mode_policy_handler.h"
@@ -51,7 +52,6 @@
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/browser/previews/previews_offline_helper.h"
-#include "chrome/browser/previews/previews_prober.h"
 #include "chrome/browser/profiles/chrome_version_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -691,6 +691,7 @@
   // User prefs. Please keep this list alphabetized.
   AccessibilityLabelsService::RegisterProfilePrefs(registry);
   AccessibilityUIMessageHandler::RegisterProfilePrefs(registry);
+  AvailabilityProber::RegisterProfilePrefs(registry);
   autofill::prefs::RegisterProfilePrefs(registry);
   browsing_data::prefs::RegisterBrowserUserPrefs(registry);
   certificate_transparency::prefs::RegisterPrefs(registry);
@@ -733,7 +734,6 @@
   PrefsTabHelper::RegisterProfilePrefs(registry, locale);
   PreviewsLitePageDecider::RegisterProfilePrefs(registry);
   PreviewsOfflineHelper::RegisterProfilePrefs(registry);
-  PreviewsProber::RegisterProfilePrefs(registry);
   Profile::RegisterProfilePrefs(registry);
   ProfileImpl::RegisterProfilePrefs(registry);
   ProfileNetworkContextService::RegisterProfilePrefs(registry);
@@ -1099,4 +1099,5 @@
   // Added 7/2019.
   syncer::MigrateSyncSuppressedPref(profile_prefs);
   profile_prefs->ClearPref(kSignedInTime);
+  syncer::ClearObsoleteMemoryPressurePrefs(profile_prefs);
 }
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 3c2845c..b03ff9c02 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -130,10 +130,16 @@
             ->GetAppCacheService();
     do {
       base::RunLoop wait_loop;
-      base::PostTaskWithTraits(
-          FROM_HERE, {content::BrowserThread::IO},
-          base::BindOnce(WaitForAppcacheOnIO, manifest_url, appcache_service,
-                         wait_loop.QuitClosure(), &found_manifest));
+      if (base::FeatureList::IsEnabled(features::kNavigationLoaderOnUI)) {
+        WaitForAppcacheOnLoaderThread(manifest_url, appcache_service,
+                                      base::DoNothing(), &found_manifest);
+      } else {
+        base::PostTaskWithTraits(
+            FROM_HERE, {content::BrowserThread::IO},
+            base::BindOnce(WaitForAppcacheOnLoaderThread, manifest_url,
+                           appcache_service, wait_loop.QuitClosure(),
+                           &found_manifest));
+      }
       // There seems to be some flakiness in the appcache getting back to us, so
       // use a timeout task to try the appcache query again.
       base::PostDelayedTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
@@ -179,10 +185,11 @@
   // |found_manifest| if an appcache exists for |manifest_url|. |callback| will
   // be called on the UI thread after the info is retrieved, whether or not the
   // manifest exists.
-  static void WaitForAppcacheOnIO(const GURL& manifest_url,
-                                  content::AppCacheService* appcache_service,
-                                  base::Closure callback,
-                                  bool* found_manifest) {
+  static void WaitForAppcacheOnLoaderThread(
+      const GURL& manifest_url,
+      content::AppCacheService* appcache_service,
+      base::Closure callback,
+      bool* found_manifest) {
     scoped_refptr<content::AppCacheInfoCollection> info_collection =
         new content::AppCacheInfoCollection();
     appcache_service->GetAllAppCacheInfo(
diff --git a/chrome/browser/previews/previews_prober_browsertest.cc b/chrome/browser/previews/previews_prober_browsertest.cc
deleted file mode 100644
index dd16b80..0000000
--- a/chrome/browser/previews/previews_prober_browsertest.cc
+++ /dev/null
@@ -1,177 +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 <string>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/run_loop.h"
-#include "build/build_config.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 "content/public/test/network_connection_change_simulator.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 "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "services/network/public/mojom/network_service_test.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace {
-
-void WaitForCompletedProbe(PreviewsProber* prober) {
-  while (true) {
-    if (prober->LastProbeWasSuccessful().has_value())
-      return;
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
-}  // namespace
-
-class TestDelegate : public PreviewsProber::Delegate {
- public:
-  TestDelegate() = default;
-  ~TestDelegate() = default;
-
-  bool ShouldSendNextProbe() override { return should_send_next_probe_; }
-
-  bool IsResponseSuccess(net::Error net_error,
-                         const network::ResourceResponseHead& head,
-                         std::unique_ptr<std::string> body) override {
-    return net_error == net::OK &&
-           head.headers->response_code() == net::HTTP_OK;
-  }
-
-  void set_should_send_next_probe(bool should_send_next_probe) {
-    should_send_next_probe_ = should_send_next_probe;
-  }
-
- private:
-  bool should_send_next_probe_ = true;
-};
-
-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");
-  TestDelegate delegate;
-  net::HttpRequestHeaders headers;
-  PreviewsProber::RetryPolicy retry_policy;
-  PreviewsProber::TimeoutPolicy timeout_policy;
-
-  PreviewsProber prober(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        browser()->profile()->GetPrefs(),
-                        PreviewsProber::ClientName::kLitepages, url,
-                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
-                        timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
-                        base::TimeDelta::FromDays(1));
-  prober.SendNowIfInactive(false);
-  WaitForCompletedProbe(&prober);
-
-  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
-}
-
-IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, Timeout) {
-  GURL url = TestURLWithPath("/timeout");
-  TestDelegate delegate;
-  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(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        browser()->profile()->GetPrefs(),
-                        PreviewsProber::ClientName::kLitepages, url,
-                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
-                        timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
-                        base::TimeDelta::FromDays(1));
-  prober.SendNowIfInactive(false);
-  WaitForCompletedProbe(&prober);
-
-  EXPECT_FALSE(prober.LastProbeWasSuccessful().value());
-}
-
-IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, NetworkChange) {
-  content::NetworkConnectionChangeSimulator().SetConnectionType(
-      network::mojom::ConnectionType::CONNECTION_2G);
-
-  GURL url = TestURLWithPath("/ok");
-  TestDelegate delegate;
-  net::HttpRequestHeaders headers;
-  PreviewsProber::RetryPolicy retry_policy;
-  PreviewsProber::TimeoutPolicy timeout_policy;
-
-  PreviewsProber prober(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        browser()->profile()->GetPrefs(),
-                        PreviewsProber::ClientName::kLitepages, url,
-                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
-                        timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
-                        base::TimeDelta::FromDays(1));
-
-  content::NetworkConnectionChangeSimulator().SetConnectionType(
-      network::mojom::ConnectionType::CONNECTION_4G);
-  WaitForCompletedProbe(&prober);
-
-  EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
-}
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
index d76f910..2e86b5f 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
@@ -15,7 +15,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump.h"
 #include "base/process/kill.h"
 #include "base/process/process.h"
 #include "base/rand_util.h"
@@ -24,6 +24,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
+#include "base/task/single_thread_task_executor.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
@@ -184,7 +185,7 @@
 // determine the failure.
 int CloudPrintMockService_Main(SetExpectationsCallback set_expectations) {
   base::PlatformThread::SetName("Main Thread");
-  base::MessageLoopForUI main_message_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::UI);
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   content::RegisterPathProvider();
 
@@ -200,7 +201,7 @@
   base::FilePath executable_path =
       command_line->GetSwitchValuePath(kTestExecutablePath);
   EXPECT_FALSE(executable_path.empty());
-  MockLaunchd mock_launchd(executable_path, main_message_loop.task_runner(),
+  MockLaunchd mock_launchd(executable_path, executor.task_runner(),
                            run_loop.QuitClosure(), true);
   Launchd::ScopedInstance use_mock(&mock_launchd);
 #endif
diff --git a/chrome/browser/profile_resetter/triggered_profile_resetter_win.cc b/chrome/browser/profile_resetter/triggered_profile_resetter_win.cc
index c52eb7c4..e3f4155 100644
--- a/chrome/browser/profile_resetter/triggered_profile_resetter_win.cc
+++ b/chrome/browser/profile_resetter/triggered_profile_resetter_win.cc
@@ -9,13 +9,14 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/win/registry.h"
+#include "build/branding_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
 #define PRODUCT_NAME L"Google\\Chrome"
-#elif defined(CHROMIUM_BUILD)
+#elif BUILDFLAG(CHROMIUM_BRANDING)
 #define PRODUCT_NAME L"Chromium"
 #else
 #error Unknown branding
diff --git a/chrome/browser/profiles/profile_attributes_storage_unittest.cc b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
index d167180..befdeca 100644
--- a/chrome/browser/profiles/profile_attributes_storage_unittest.cc
+++ b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
@@ -493,12 +493,14 @@
   ASSERT_FALSE(entry->IsChild());
   ASSERT_TRUE(entry->IsLegacySupervised());
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   EXPECT_CALL(observer(), OnProfileSupervisedUserIdChanged(path)).Times(1);
   entry->SetSupervisedUserId(supervised_users::kChildAccountSUID);
   VerifyAndResetCallExpectations();
   ASSERT_TRUE(entry->IsSupervised());
   ASSERT_TRUE(entry->IsChild());
   ASSERT_FALSE(entry->IsLegacySupervised());
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 }
 
 TEST_F(ProfileAttributesStorageTest, ReSortTriggered) {
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 17a23f76..084648e 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -232,6 +232,7 @@
 #include "extensions/browser/extension_pref_store.h"
 #include "extensions/browser/extension_pref_value_map.h"
 #include "extensions/browser/extension_pref_value_map_factory.h"
+#include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 #endif
 
@@ -951,6 +952,12 @@
   if (g_browser_process->local_state())
     MigrateObsoleteBrowserPrefs(this, g_browser_process->local_state());
   MigrateObsoleteProfilePrefs(this);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // Note: Extension preferences can be keyed off the extension ID, so need to
+  // be handled specially (rather than directly as part of
+  // MigrateObsoleteProfilePrefs()).
+  extensions::ExtensionPrefs::Get(this)->MigrateObsoleteExtensionPrefs();
+#endif
 
   // |kSessionExitType| was added after |kSessionExitedCleanly|. If the pref
   // value is empty fallback to checking for |kSessionExitedCleanly|.
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc
index a3d4c62..3266a24 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.cc
+++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -167,8 +167,13 @@
     EXPECT_EQ(icon->width(), actual_icon->width());
     EXPECT_EQ(icon->height(), actual_icon->height());
 #endif
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
     EXPECT_EQ(i == 3, GetCache()->ProfileIsSupervisedAtIndex(i));
     EXPECT_EQ(i == 3, GetCache()->IsOmittedProfileAtIndex(i));
+#else
+    EXPECT_FALSE(GetCache()->ProfileIsSupervisedAtIndex(i));
+    EXPECT_FALSE(GetCache()->IsOmittedProfileAtIndex(i));
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
     EXPECT_EQ(supervised_user_id,
               GetCache()->GetSupervisedUserIdOfProfileAtIndex(i));
   }
diff --git a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
index 61aec11..e91b613 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
@@ -199,13 +199,13 @@
   // Posts a task to call ShellUtil::CreateOrUpdateShortcut on the COM thread.
   void PostCreateOrUpdateShortcut(
       const base::Location& location,
+      ShellUtil::ShortcutLocation shortcut_location,
       const ShellUtil::ShortcutProperties& properties) {
     base::PostTaskAndReplyWithResult(
         base::CreateCOMSTATaskRunnerWithTraits({base::MayBlock()}).get(),
         location,
-        base::Bind(&ShellUtil::CreateOrUpdateShortcut,
-                   ShellUtil::SHORTCUT_LOCATION_DESKTOP, properties,
-                   ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS),
+        base::Bind(&ShellUtil::CreateOrUpdateShortcut, shortcut_location,
+                   properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS),
         base::Bind([](bool succeeded) { EXPECT_TRUE(succeeded); }));
     thread_bundle_.RunUntilIdle();
   }
@@ -222,7 +222,8 @@
     ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
     ShellUtil::AddDefaultShortcutProperties(GetExePath(), &properties);
     properties.set_shortcut_name(shortcut_name);
-    PostCreateOrUpdateShortcut(location, properties);
+    PostCreateOrUpdateShortcut(location, ShellUtil::SHORTCUT_LOCATION_DESKTOP,
+                               properties);
     EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();
 
     return shortcut_path;
@@ -232,7 +233,8 @@
       const base::Location& location) {
     ShellUtil::ShortcutProperties properties(ShellUtil::SYSTEM_LEVEL);
     ShellUtil::AddDefaultShortcutProperties(GetExePath(), &properties);
-    PostCreateOrUpdateShortcut(location, properties);
+    PostCreateOrUpdateShortcut(location, ShellUtil::SHORTCUT_LOCATION_DESKTOP,
+                               properties);
     const base::FilePath system_level_shortcut_path =
         GetSystemShortcutsDirectory().Append(InstallUtil::GetShortcutName() +
                                              installer::kLnkExt);
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index b2cf441..5f26c76 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -11,6 +11,7 @@
   group("closure_compile") {
     deps = [
       "engagement:closure_compile",
+      "interventions_internals:closure_compile",
     ]
     if (is_linux || is_win || is_mac) {
       deps += [
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index e0335ab..ee74beb6 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -460,8 +460,7 @@
   background-image: url(../images/camera_button_mic_off.svg);
 }
 
-body:not(.multi-fps) #toggle-fps,
-body:not(.video-mode) #toggle-fps {
+#toggle-fps {
   display: none;
 }
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/browser_proxy_interface.js b/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/browser_proxy_interface.js
index 22367be..115a7669 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/browser_proxy_interface.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/browser_proxy_interface.js
@@ -30,7 +30,7 @@
 
   /**
    * @param {(string|!Array<string>|!Object)} keys
-   * @param {function(!Object)=} callback
+   * @param {function(!Object)} callback
    */
   localStorageGet(keys, callback) {}
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/webui_browser_proxy.js b/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/webui_browser_proxy.js
index cb5720b..9e895695 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/webui_browser_proxy.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/browser_proxy/webui_browser_proxy.js
@@ -39,17 +39,50 @@
 
   /** @override */
   localStorageGet(keys, callback) {
-    NOTIMPLEMENTED();
+    let sanitizedKeys = [];
+    if (typeof keys === 'string') {
+      sanitizedKeys = [keys];
+    } else if (Array.isArray(keys)) {
+      sanitizedKeys = keys;
+    } else if (keys !== null && typeof keys === 'object') {
+      sanitizedKeys = Object.keys(keys);
+    } else {
+      throw new Error('WebUI localStorageGet() cannot be run with ' + keys);
+    }
+
+    let result = {};
+    for (let key of sanitizedKeys) {
+      let value = window.localStorage.getItem(key);
+      if (value !== null) {
+        value = JSON.parse(value);
+      }
+      result[key] = value === null ? {} : value;
+    }
+
+    callback(result);
   }
 
   /** @override */
   localStorageSet(items, callback) {
-    NOTIMPLEMENTED();
+    for (let [key, val] of Object.entries(items)) {
+      window.localStorage.setItem(key, JSON.stringify(val));
+    }
+    if (callback) {
+      callback();
+    }
   }
 
   /** @override */
   localStorageRemove(items, callback) {
-    NOTIMPLEMENTED();
+    if (typeof items === 'string') {
+      items = [items];
+    }
+    for (let key of items) {
+      window.localStorage.removeItem(key);
+    }
+    if (callback) {
+      callback();
+    }
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/camera/src/js/main.js b/chrome/browser/resources/chromeos/camera/src/js/main.js
index f5f0408..1a9281d8 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/main.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/main.js
@@ -27,24 +27,24 @@
   this.resolBroker_ = new cca.ResolutionEventBroker();
 
   /**
-   * @type {cca.camera.PhotoResolPreferrer}
+   * @type {cca.device.PhotoResolPreferrer}
    * @private
    */
-  this.photoPreferrer_ = new cca.camera.PhotoResolPreferrer(
+  this.photoPreferrer_ = new cca.device.PhotoResolPreferrer(
       this.resolBroker_, () => this.cameraView_.restart());
 
   /**
-   * @type {cca.camera.VideoConstraintsPreferrer}
+   * @type {cca.device.VideoConstraintsPreferrer}
    * @private
    */
-  this.videoPreferrer_ = new cca.camera.VideoConstraintsPreferrer(
+  this.videoPreferrer_ = new cca.device.VideoConstraintsPreferrer(
       this.resolBroker_, () => this.cameraView_.restart());
 
   /**
-   * @type {cca.camera.DeviceInfoUpdater}
+   * @type {cca.device.DeviceInfoUpdater}
    * @private
    */
-  this.infoUpdater_ = new cca.camera.DeviceInfoUpdater(
+  this.infoUpdater_ = new cca.device.DeviceInfoUpdater(
       this.photoPreferrer_, this.videoPreferrer_);
 
   /**
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
index 80959fc..5b9efd7 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -17,9 +17,9 @@
 /**
  * Creates the camera-view controller.
  * @param {cca.models.Gallery} model Model object.
- * @param {cca.camera.DeviceInfoUpdater} infoUpdater
- * @param {cca.camera.PhotoResolPreferrer} photoPreferrer
- * @param {cca.camera.VideoConstraintsPreferrer} videoPreferrer
+ * @param {cca.device.DeviceInfoUpdater} infoUpdater
+ * @param {cca.device.PhotoResolPreferrer} photoPreferrer
+ * @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
  * @constructor
  */
 cca.views.Camera = function(
@@ -34,7 +34,7 @@
   this.model_ = model;
 
   /**
-   * @type {cca.camera.DeviceInfoUpdater}
+   * @type {cca.device.DeviceInfoUpdater}
    * @private
    */
   this.infoUpdater_ = infoUpdater;
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index fc2e674..60ee0142 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -85,8 +85,8 @@
 
 /**
  * Mode controller managing capture sequence of different camera mode.
- * @param {cca.camera.PhotoResolPreferrer} photoResolPreferrer
- * @param {cca.camera.VideoConstraintsPreferrer} videoPreferrer
+ * @param {cca.device.PhotoResolPreferrer} photoResolPreferrer
+ * @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
  * @param {function()} doSwitchMode Callback to trigger mode switching.
  * @param {DoSavePhoto} doSavePhoto
  * @param {DoSaveVideo} doSaveVideo
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 33d79a74..f60d433 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
@@ -78,13 +78,13 @@
 
 /**
  * Creates a controller for the options of Camera view.
- * @param {cca.camera.DeviceInfoUpdater} infoUpdater
+ * @param {cca.device.DeviceInfoUpdater} infoUpdater
  * @param {function()} doSwitchDevice Callback to trigger device switching.
  * @constructor
  */
 cca.views.camera.Options = function(infoUpdater, doSwitchDevice) {
   /**
-   * @type {cca.camera.DeviceInfoUpdater}
+   * @type {cca.device.DeviceInfoUpdater}
    * @private
    */
   this.infoUpdater_ = infoUpdater;
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
index 6c396dcb..575adfc 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
@@ -109,9 +109,9 @@
 
 /**
  * Creates the controller of resolution settings view.
- * @param {!cca.camera.DeviceInfoUpdater} infoUpdater
- * @param {!cca.camera.PhotoResolPreferrer} photoPreferrer
- * @param {!cca.camera.VideoConstraintsPreferrer} videoPreferrer
+ * @param {!cca.device.DeviceInfoUpdater} infoUpdater
+ * @param {!cca.device.PhotoResolPreferrer} photoPreferrer
+ * @param {!cca.device.VideoConstraintsPreferrer} videoPreferrer
  * @param {!cca.ResolutionEventBroker} resolBroker
  * @extends {cca.views.BaseSettings}
  * @constructor
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html
index 761482c7..d056d3d 100644
--- a/chrome/browser/resources/chromeos/camera/src/views/main.html
+++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -20,9 +20,9 @@
     <script src="../js/sound.js"></script>
     <script src="../js/scrollbar.js"></script>
     <script src="../js/gallerybutton.js"></script>
-    <script src="../js/camera/camera3_device_info.js"></script>
-    <script src="../js/camera/constraints_preferrer.js"></script>
-    <script src="../js/camera/device_info_updater.js"></script>
+    <script src="../js/device/camera3_device_info.js"></script>
+    <script src="../js/device/constraints_preferrer.js"></script>
+    <script src="../js/device/device_info_updater.js"></script>
     <script src="../js/models/filenamer.js"></script>
     <script src="../js/models/gallery.js"></script>
     <script src="../js/models/filesystem.js"></script>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js
index 312c361c..62d0426 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js
@@ -84,9 +84,12 @@
 
 /**
  * The distance between black and dark grey is the threshold.
+ * 0x000000 = Black.
+ * 0x282828 = Dark Grey. This value was chosen somewhat arbitrarily. It encodes
+ * a shade of grey that could be visibly identified as black.
  * @const {number}
  */
-Color.DISTANCE_THRESHOLD = Color.findDistance(0X000000, 0X181818);
+Color.DISTANCE_THRESHOLD = Color.findDistance(0X000000, 0X282828);
 
 /**
  * Holds objects that contain hexadecimal RGB values of colors and their
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs
index 8eee010..c9f492c 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs
@@ -38,10 +38,11 @@
   var blue = 0x0000ff;
   var black = 0x000000;
 
+  var gmailDefaultTextColor = 0x222222;
   var looksLikePink = 0xF4CCCC;
   var looksLikeGreen = 0x38761D;
-
-  var unknownColor = 0x0C343D;
+  var looksLikeDarkGrey = 0x0C343D;
+  var unknownColor = 0x003DAC;
 
   // Exact matches.
   assertEquals('White', Color.findClosestMatchingColor(white));
@@ -51,8 +52,10 @@
   assertEquals('Black', Color.findClosestMatchingColor(black));
 
   // Inexact matches.
+  assertEquals('Black', Color.findClosestMatchingColor(gmailDefaultTextColor));
   assertEquals('Pink', Color.findClosestMatchingColor(looksLikePink));
   assertEquals('Forest Green', Color.findClosestMatchingColor(looksLikeGreen));
+  assertEquals('Dark Slate Grey', Color.findClosestMatchingColor(looksLikeDarkGrey));
 
   // No match.
   assertEquals('', Color.findClosestMatchingColor(unknownColor));
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.css b/chrome/browser/resources/chromeos/login/oobe_eula.css
index 758a7fa..b78674a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.css
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.css
@@ -43,6 +43,7 @@
 }
 
 #usageStatsLabelContainer {
+  color: var(--google-grey-refresh-700); /* #5F6368 */
   line-height: 20px;
   margin-inline-start: 16px;
 }
diff --git a/chrome/browser/resources/chromeos/password_change/confirm_password_change.html b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
index 96a768e..67c7380 100644
--- a/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
+++ b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
@@ -35,16 +35,17 @@
         }
 
         [slot='body'] {
-          color: rgb(20, 21, 24);
+          color: var(--google-grey-900);
           font-family: Roboto, sans-serif;
           font-size: 13px;
           padding: 0 48px 0 24px;
         }
 
         [slot='button-container'] {
-          bottom: 16px;
+          bottom: 0;
           box-sizing: border-box;
-          padding: 0 16px;
+          margin: 0;
+          padding: 16px;
           position: fixed;
           width: 100%;
         }
diff --git a/chrome/browser/resources/chromeos/password_change/urgent_password_expiry_notification.html b/chrome/browser/resources/chromeos/password_change/urgent_password_expiry_notification.html
index dd539af..9702996 100644
--- a/chrome/browser/resources/chromeos/password_change/urgent_password_expiry_notification.html
+++ b/chrome/browser/resources/chromeos/password_change/urgent_password_expiry_notification.html
@@ -27,30 +27,31 @@
         }
 
         [slot='title'] {
-          color: rgb(20, 21, 25);
+          color: var(--google-grey-900);
           font-family: Google Sans, sans-serif;
           font-size: 28px;
           padding: 64px 64px 8px;
         }
 
         #title-icon {
-          --iron-icon-fill-color: rgb(20, 21, 25);
+          --iron-icon-fill-color: var(--google-grey-900);
           height: 32px;
           padding-bottom: 24px;
           width: 32px;
         }
 
         [slot='body'] {
-          color: rgb(21, 21, 21);
+          color: var(--google-grey-900);
           font-family: Roboto, sans-serif;
           font-size: 13px;
           padding: 0 64px;
         }
 
         [slot='button-container'] {
-          bottom: 32px;
+          bottom: 0;
           box-sizing: border-box;
-          padding: 0 32px;
+          margin: 0;
+          padding: 32px;
           position: fixed;
           width: 100%;
         }
diff --git a/chrome/browser/resources/interventions_internals/BUILD.gn b/chrome/browser/resources/interventions_internals/BUILD.gn
new file mode 100644
index 0000000..e6c48042
--- /dev/null
+++ b/chrome/browser/resources/interventions_internals/BUILD.gn
@@ -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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":index",
+  ]
+}
+
+js_library("index") {
+  deps = [
+    "//chrome/browser/ui/webui/interventions_internals:mojo_bindings_js_library_for_compile",
+    "//ui/webui/resources/js:assert",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:util",
+  ]
+}
diff --git a/chrome/browser/resources/interventions_internals/index.js b/chrome/browser/resources/interventions_internals/index.js
index c02243f..c417033d 100644
--- a/chrome/browser/resources/interventions_internals/index.js
+++ b/chrome/browser/resources/interventions_internals/index.js
@@ -284,7 +284,7 @@
  * Helper function to check if all keywords, case insensitive, are in the given
  * text.
  *
- * @param {string[]} keywords The collection of keywords.
+ * @param {Array<string>} keywords The collection of keywords.
  * @param {string} text The given text to search.
  * @return True iff all keywords present in the given text.
  */
@@ -370,7 +370,7 @@
  * Create and add a copy to clipboard button to a given node.
  *
  * @param {string} text The text that will be copied to the clipboard.
- * @param {element!} node The node that will have the button appended to.
+ * @param {Element} node The node that will have the button appended to.
  */
 function appendCopyToClipBoardButton(text, node) {
   if (!document.queryCommandSupported ||
@@ -449,15 +449,20 @@
   $('clear-log-button').addEventListener('click', removeAllLogMessagesRows);
 }
 
-/** @constructor */
-const InterventionsInternalPageImpl = function() {};
+/**
+ * @constructor
+ * @implements {mojom.InterventionsInternalsPageInterface}
+ */
+const InterventionsInternalPageImpl = function() {
+  this.receiver_ = new mojom.InterventionsInternalsPageReceiver(this);
+};
 
 InterventionsInternalPageImpl.prototype = {
   /**
    * Post a new log message to the web page.
    *
    * @override
-   * @param {!MessageLog} log The new log message recorded by
+   * @param {!mojom.MessageLog} log The new log message recorded by
    * PreviewsLogger.
    */
   logNewMessage: function(log) {
@@ -580,6 +585,14 @@
     nqeCol.textContent = type;
     nqeRow.appendChild(nqeCol);
   },
+
+  /**
+   * Returns a remote interface to the receiver.
+   */
+  bindNewPipeAndPassRemote: function() {
+    const helper = this.receiver_.$;
+    return helper.bindNewPipeAndPassRemote();
+  },
 };
 
 cr.define('interventions_internals', () => {
@@ -600,22 +613,6 @@
   }
 
   /**
-   * Sort keys by the value of each value by its description attribute of a
-   * |mapObject|.
-   *
-   * @param mapObject {!Map<string, Object} A map where all values have a
-   * description attribute.
-   * @return A list of keys sorted by their descriptions.
-   */
-  function getSortedKeysByDescription(mapObject) {
-    const sortedKeys = Array.from(mapObject.keys());
-    sortedKeys.sort((a, b) => {
-      return mapObject.get(a).description > mapObject.get(b).description;
-    });
-    return sortedKeys;
-  }
-
-  /**
    * Retrieves the statuses of previews (i.e. Offline, Lite Pages, etc),
    * and posts them on chrome://intervention-internals.
    */
@@ -699,12 +696,11 @@
     if (window.testPageHandler) {
       pageHandler = window.testPageHandler;
     } else {
-      pageHandler = mojom.InterventionsInternalsPageHandler.getProxy();
+      pageHandler = mojom.InterventionsInternalsPageHandler.getRemote();
 
       // Set up client side mojo interface.
       pageImpl = new InterventionsInternalPageImpl();
-      const client = new mojom.InterventionsInternalsPage(pageImpl);
-      pageHandler.setClientPage(client.$.createProxy());
+      pageHandler.setClientPage(pageImpl.bindNewPipeAndPassRemote());
     }
 
     interventions_internals.init(pageHandler);
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 213afc16..a288671 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -1450,6 +1450,7 @@
   // Selecting a local image for the background should close the picker.
   if (configData.richerPicker) {
     ntpApiHandle.onlocalbackgroundselected = () => {
+      customize.selectedOptions.backgroundData = null;
       customize.richerPicker_deselectBackgroundTile(
           customize.selectedOptions.background);
       customize.richerPicker_applyCustomization();
diff --git a/chrome/browser/resources/omnibox/BUILD.gn b/chrome/browser/resources/omnibox/BUILD.gn
index 10b8777..0465f91 100644
--- a/chrome/browser/resources/omnibox/BUILD.gn
+++ b/chrome/browser/resources/omnibox/BUILD.gn
@@ -16,6 +16,7 @@
     ":omnibox_input",
     ":omnibox_output",
     "//chrome/browser/ui/webui/omnibox:mojo_bindings_js_library_for_compile",
+    "//ui/webui/resources/js:load_time_data",
   ]
 }
 
diff --git a/chrome/browser/resources/omnibox/omnibox.html b/chrome/browser/resources/omnibox/omnibox.html
index 4947569..1f9f1f8 100644
--- a/chrome/browser/resources/omnibox/omnibox.html
+++ b/chrome/browser/resources/omnibox/omnibox.html
@@ -8,12 +8,14 @@
   <link rel="stylesheet" href="omnibox.css">
   <script src="chrome://resources/js/cr.js"></script>
   <script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
+  <script src="chrome://resources/js/load_time_data.js"></script>
   <script src="chrome://resources/js/util.js"></script>
   <script src="chrome/browser/ui/webui/omnibox/omnibox.mojom-lite.js"></script>
   <script src="omnibox_element.js"></script>
   <script src="omnibox_input.js"></script>
   <script src="omnibox_output.js"></script>
   <script src="omnibox.js"></script>
+  <script src="strings.js"></script>
 </head>
 
 <body>
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index 74e3738..7bcb89eb 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -81,8 +81,13 @@
    * @param {boolean} isPageController
    */
   handleNewAutocompleteResponse(response, isPageController) {
+    // Note: Using inputText is a sufficient fix for the way this is used today,
+    // but in principle it would be better to associate requests with responses
+    // using a unique session identifier, for example by rolling an integer each
+    // time a request is made. Doing so would require extra bookkeeping on the
+    // host side, so for now we keep it simple.
     const isForLastPageRequest = isPageController && this.lastRequest &&
-        this.lastRequest.inputText === response.host;
+        this.lastRequest.inputText === response.inputText;
 
     // When unfocusing the browser omnibox, the autocomplete controller
     // sends a response with no combined results. This response is ignored
@@ -95,6 +100,9 @@
       omniboxOutput.addAutocompleteResponse(response);
     }
 
+    // TODO(orinj|manukh): If |response.done| but not |isForLastPageRequest|
+    // then callback is being dropped. We should guarantee that callback is
+    // always called because some callers await promises.
     if (isForLastPageRequest && response.done) {
       this.lastRequest.callback(response);
       this.lastRequest = null;
@@ -213,8 +221,9 @@
    * This is the worker function that transforms query inputs to accumulate
    * batch exports, then finally initiates a download for the complete set.
    * @param {!Array<!QueryInputs>} batchQueryInputs
+   * @param {string} batchName
    */
-  async processBatch(batchQueryInputs) {
+  async processBatch(batchQueryInputs, batchName) {
     const batchExports = [];
     for (const queryInputs of batchQueryInputs) {
       const omniboxResponse = await browserProxy
@@ -233,8 +242,21 @@
       };
       batchExports.push(exportData);
     }
-    const fileName = `omnibox_batch_${ExportDelegate.getTimeStamp()}.json`;
-    const batchData = { appVersion: navigator.appVersion, batchExports };
+    const now = new Date();
+    const fileName = `omnibox_batch_${ExportDelegate.getTimeStamp(now)}.json`;
+    // If this data format changes, please roll schemaVersion.
+    const batchData = {
+      schemaKind: 'Omnibox Batch Export',
+      schemaVersion: 2,
+      dateCreated: now.toISOString(),
+      author: '',
+      description: '',
+      authorTool: 'chrome://omnibox',
+      batchName,
+      versionDetails: window.loadTimeData.data_,
+      appVersion: navigator.appVersion,
+      batchExports
+    };
     ExportDelegate.download_(batchData, fileName);
   }
 
@@ -244,11 +266,14 @@
    * @param {!BatchSpecifier} processBatchData
    */
   processBatchData(processBatchData) {
-    if (processBatchData.batchMode && processBatchData.batchQueryInputs) {
-      this.processBatch(processBatchData.batchQueryInputs);
+    if (processBatchData.batchMode && processBatchData.batchQueryInputs &&
+        processBatchData.batchName) {
+      this.processBatch(
+          processBatchData.batchQueryInputs, processBatchData.batchName);
     } else {
       const expected = {
         batchMode: "combined",
+        batchName: "name for this batch of queries",
         batchQueryInputs: [
           {
             inputText: "example input text",
@@ -305,9 +330,15 @@
     a.click();
   }
 
-  /** @return {string} A sortable timestamp string for use in filenames. */
-  static getTimeStamp() {
-    const iso = new Date().toISOString();
+  /** 
+    * @param {Date=} date
+    * @return {string} A sortable timestamp string for use in filenames.
+    */
+  static getTimeStamp(date) {
+    if (!date) {
+      date = new Date();
+    }
+    const iso = date.toISOString();
     return iso.replace(/:/g, '').split('.')[0];
   }
 }
diff --git a/chrome/browser/resources/policy/policy_base.js b/chrome/browser/resources/policy/policy_base.js
index 47e5f49d..cba1b9a5 100644
--- a/chrome/browser/resources/policy/policy_base.js
+++ b/chrome/browser/resources/policy/policy_base.js
@@ -505,7 +505,7 @@
                       link:
                           knownPolicyNames === policyNames.chrome.policyNames &&
                               knownPolicyNamesSet.has(name) ?
-                          `https://chromium.org/administrators/policy-list-3#${
+                          `https://cloud.google.com/docs/chrome-enterprise/policies/?policy=${
                               name}` :
                           undefined,
                     },
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
index 85fc4d8..81ef1c0 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
@@ -7,14 +7,14 @@
 js_type_check("closure_compile") {
   deps = [
     ":os_powerwash_dialog",
+    ":os_reset_browser_proxy",
     ":os_reset_page",
-    ":reset_os_proxy",
   ]
 }
 
 js_library("os_powerwash_dialog") {
   deps = [
-    ":reset_os_proxy",
+    ":os_reset_browser_proxy",
     "../..:lifetime_browser_proxy",
   ]
 }
@@ -28,7 +28,7 @@
   ]
 }
 
-js_library("reset_os_proxy") {
+js_library("os_reset_browser_proxy") {
   deps = [
     "//ui/webui/resources/js:cr",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
index c021575..51a81006 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
@@ -2,7 +2,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="reset_os_proxy.html">
+<link rel="import" href="os_reset_browser_proxy.html">
 <link rel="import" href="../../lifetime_browser_proxy.html">
 <link rel="import" href="../../settings_shared_css.html">
 
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
index 32b6c036..99d0905 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
@@ -19,7 +19,7 @@
 
   /** @override */
   attached: function() {
-    settings.ResetOsProxyImpl.getInstance().onPowerwashDialogShow();
+    settings.OsResetBrowserProxyImpl.getInstance().onPowerwashDialogShow();
     this.$.dialog.showModal();
   },
 
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html
new file mode 100644
index 0000000..09bda1a
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="os_reset_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.js
similarity index 67%
rename from chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js
rename to chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.js
index 83929c9..ebdf4fa 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.js
@@ -4,22 +4,22 @@
 
 cr.define('settings', function() {
   /** @interface */
-  class ResetOsProxy {
+  class OsResetBrowserProxy {
     /**
      * A method to be called when the reset powerwash dialog is shown.
      */
     onPowerwashDialogShow() {}
 
     /**
-     * Initiates a factory reset and restarts ChromeOS.
+     * Initiates a factory reset and restarts.
      */
     requestFactoryResetRestart() {}
   }
 
   /**
-   * @implements {settings.ResetOsProxy}
+   * @implements {settings.OsResetBrowserProxy}
    */
-  class ResetOsProxyImpl {
+  class OsResetBrowserProxyImpl {
     /** @override */
     onPowerwashDialogShow() {
       chrome.send('onPowerwashDialogShow');
@@ -31,10 +31,10 @@
     }
   }
 
-  cr.addSingletonGetter(ResetOsProxyImpl);
+  cr.addSingletonGetter(OsResetBrowserProxyImpl);
 
   return {
-    ResetOsProxy: ResetOsProxy,
-    ResetOsProxyImpl: ResetOsProxyImpl,
+    OsResetBrowserProxy: OsResetBrowserProxy,
+    OsResetBrowserProxyImpl: OsResetBrowserProxyImpl,
   };
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html b/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html
deleted file mode 100644
index 77b9e53..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<script src="reset_os_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
index 0f74897..79b9e54 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_page.html
@@ -6,6 +6,8 @@
 <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/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
 <link rel="import" href="../../controls/extension_controlled_indicator.html">
 <link rel="import" href="../../google_assistant_page/google_assistant_page.html">
 <link rel="import" href="../../google_assistant_page/google_assistant_browser_proxy.html">
@@ -27,8 +29,20 @@
         min-height: var(--settings-row-min-height);
       }
 
+      /* TODO(jamescook): Style all OS settings tooltips to new spec. */
+      paper-tooltip {
+        --paper-tooltip: {
+          @apply --cr-tooltip;
+          padding: 12px 10px;
+        };
+      }
+
       iron-icon {
-        padding-inline-end: 16px;
+        --iron-icon-fill-color: var(--google-grey-refresh-500);
+      }
+
+      cr-policy-pref-indicator {
+        padding-inline-end: 8px;
       }
     </style>
     <settings-animated-pages id="pages" section="search"
@@ -40,7 +54,14 @@
         <div class="settings-box first block">
           <div id="search-wrapper">
             <div id="searchExplanation" class="start settings-box-text">
-              $i18nRaw{searchExplanation}
+              $i18n{osSearchEngineLabel}
+              <iron-icon id="help-icon" icon="cr:help-outline"
+                  aria-label="$i18n{osSearchEngineTooltip}"
+                  aria-describedby="tooltip"></iron-icon>
+              <paper-tooltip id="tooltip" for="help-icon" position="bottom"
+                  fit-to-visible-bounds>
+                $i18n{osSearchEngineTooltip}
+              </paper-tooltip>
             </div>
             <template is="dom-if" if="[[isDefaultSearchControlledByPolicy_(
                 prefs.default_search_provider_data.template_url_data)]]">
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
index 808af92..cb8f1dc 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js
@@ -108,10 +108,8 @@
    * @private
    */
   doesAndroidMessagesRequireSetUp_: function() {
-    // The pairing state is preferred over the FeatureState here since
-    // FeatureState.UNAVAILABLE_SUITE_DISABLED is returned when the suite is
-    // disabled, regardless if Messages requires further setup.
-    return !this.pageContentData.isAndroidSmsPairingComplete;
+    return this.getFeatureState(settings.MultiDeviceFeature.MESSAGES) ===
+        settings.MultiDeviceFeatureState.FURTHER_SETUP_REQUIRED;
   },
 
   /**
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 8ad9dd0..4b86241 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -468,11 +468,11 @@
       <structure name="IDR_OS_SETTINGS_RESET_PAGE_JS"
                  file="chromeos/os_reset_page/os_reset_page.js"
                  type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_RESET_OS_PROXY_JS"
-                 file="chromeos/os_reset_page/reset_os_proxy.js"
+      <structure name="IDR_OS_SETTINGS_RESET_BROWSER_PROXY_JS"
+                 file="chromeos/os_reset_page/os_reset_browser_proxy.js"
                  type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_RESET_OS_PROXY_HTML"
-                 file="chromeos/os_reset_page/reset_os_proxy.html"
+      <structure name="IDR_OS_SETTINGS_RESET_BROWSER_PROXY_HTML"
+                 file="chromeos/os_reset_page/os_reset_browser_proxy.html"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_LANGUAGES_HTML"
                  file="languages_page/languages.html"
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.html b/chrome/browser/resources/settings/printing_page/cups_printers.html
index 5500878..7f23152 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.html
@@ -51,7 +51,7 @@
       }
     </style>
 
-    <template is="dom-if" if="[[!enableUpdatedUI_]]">
+    <template is="dom-if" if="[[!enableUpdatedUi_]]">
       <div class="settings-box first">
         <div class="start">
           <span>$i18n{cupsPrintersLearnMoreLabel}</span>
@@ -82,7 +82,7 @@
       </settings-cups-printers-list>
     </template>
 
-    <template is="dom-if" if="[[enableUpdatedUI_]]">
+    <template is="dom-if" if="[[enableUpdatedUi_]]">
       <div class="settings-box first">
         <div class="start">
           <span>$i18n{savedPrintersTitle}</span>
@@ -132,7 +132,8 @@
     </template>
 
     <settings-cups-add-printer-dialog id="addPrinterDialog"
-        on-close="onAddPrinterDialogClose_">
+        on-close="onAddPrinterDialogClose_"
+        enable-updated-ui="[[enableUpdatedUi_]]">
     </settings-cups-add-printer-dialog>
 
     <template is="dom-if" if="[[showCupsEditPrinterDialog_]]" restamp>
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.js b/chrome/browser/resources/settings/printing_page/cups_printers.js
index bcab11f..7e41ed4 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.js
@@ -50,7 +50,7 @@
      * is launched.
      * @private
      */
-    enableUpdatedUI_: {
+    enableUpdatedUi_: {
       type: Boolean,
       value: function() {
         return loadTimeData.getBoolean('updatedCupsPrintersUiEnabled');
@@ -87,7 +87,7 @@
           this.onActiveNetworksChanged(responseParams.result);
         });
 
-    if (this.enableUpdatedUI_) {
+    if (this.enableUpdatedUi_) {
       return;
     }
 
@@ -123,7 +123,7 @@
     const printerName = event.detail.printerName;
     switch (event.detail.resultCode) {
       case PrinterSetupResult.SUCCESS:
-        if (this.enableUpdatedUI_) {
+        if (this.enableUpdatedUi_) {
           this.$$('#savedPrinters').updateSavedPrintersList();
         } else {
           this.updateCupsPrintersList_();
@@ -133,7 +133,7 @@
                                     printerName);
         break;
       case PrinterSetupResult.EDIT_SUCCESS:
-        if (this.enableUpdatedUI_) {
+        if (this.enableUpdatedUi_) {
           this.$$('#savedPrinters').updateSavedPrintersList();
         } else {
           this.updateCupsPrintersList_();
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index b94ace4..2be129c 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -142,6 +142,8 @@
         "download_protection/download_url_sb_client.h",
         "download_protection/file_analyzer.cc",
         "download_protection/file_analyzer.h",
+        "download_protection/multipart_uploader.cc",
+        "download_protection/multipart_uploader.h",
         "download_protection/path_sanitizer.cc",
         "download_protection/path_sanitizer.h",
         "download_protection/ppapi_download_request.cc",
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
index 56648e7e..40ce757 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
@@ -159,7 +159,14 @@
 }
 
 // Reads |buffer_size| bytes from a pipe of PIPE_TYPE_MESSAGE.
-bool ReadMessageFromPipe(HANDLE handle, LPVOID buffer, DWORD buffer_size) {
+// Uses |error_category| to report WinAPI errors if needed.
+// Uses |custom_error| to report short reads if needed.
+bool ReadMessageFromPipe(
+    HANDLE handle,
+    LPVOID buffer,
+    DWORD buffer_size,
+    ChromePromptChannelProtobuf::ErrorCategory error_category,
+    ChromePromptChannelProtobuf::CustomErrors short_read_error) {
   // If the process at the other end of the pipe is behaving correctly it will
   // write exactly |buffer_size| bytes to the pipe and PIPE_TYPE_MESSAGE
   // ensures that we read all of them at once. If the process writes more bytes
@@ -167,13 +174,20 @@
   // we treat that as an error.
   DWORD bytes_read = 0;
   if (!::ReadFile(handle, buffer, buffer_size, &bytes_read, nullptr)) {
+    WriteStatusErrorCodeToHistogram(error_category,
+                                    logging::GetLastSystemErrorCode());
     PLOG(ERROR) << "ReadFile failed";
     return false;
   }
+
   CHECK_LE(bytes_read, buffer_size);
   if (bytes_read != buffer_size) {
-    PLOG(ERROR) << "Short read (read " << bytes_read << " of " << buffer_size
-                << ")";
+    LOG(ERROR) << "Short read (read " << bytes_read << " of " << buffer_size
+               << ")";
+
+    WriteStatusErrorCodeToHistogram(
+        ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+        short_read_error);
     return false;
   }
   return true;
@@ -209,7 +223,6 @@
     HANDLE request_read_handle,
     std::unique_ptr<CleanerProcessDelegate> cleaner_process,
     base::OnceClosure on_connection_closed) {
-  static constexpr uint32_t kMaxMessageLength = 1 * 1024 * 1024;  // 1M bytes
 
   // Always call OnConnectionClosed when finished whether it's with an error or
   // because a CloseConnectionRequest was received.
@@ -253,18 +266,29 @@
     // Read the request length followed by a request.
     uint32_t request_length = 0;
     if (!ReadMessageFromPipe(request_read_handle, &request_length,
-                             sizeof(request_length))) {
+                             sizeof(request_length),
+                             ChromePromptChannelProtobuf::ErrorCategory::
+                                 kReadRequestLengthWinError,
+                             ChromePromptChannelProtobuf::CustomErrors::
+                                 kRequestLengthShortRead)) {
       return;
     }
-    if (request_length < 1 || request_length > kMaxMessageLength) {
-      PLOG(ERROR) << "Bad request length: " << request_length;
+
+    if (request_length < 1 ||
+        request_length > ChromePromptChannelProtobuf::kMaxMessageLength) {
+      WriteStatusErrorCodeToHistogram(
+          ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+          ChromePromptChannelProtobuf::CustomErrors::kRequestInvalidSize);
+
+      LOG(ERROR) << "Bad request length: " << request_length;
       return;
     }
     std::string request;
-    // Include space for the null terminator in the WriteInto call.
-    if (!ReadMessageFromPipe(request_read_handle,
-                             base::WriteInto(&request, request_length + 1),
-                             request_length)) {
+    if (!ReadMessageFromPipe(
+            request_read_handle, base::WriteInto(&request, request_length + 1),
+            request_length,
+            ChromePromptChannelProtobuf::ErrorCategory::kReadRequestWinError,
+            ChromePromptChannelProtobuf::CustomErrors::kRequestShortRead)) {
       return;
     }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
index 2d7252c..b5e075c2 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
@@ -137,6 +137,7 @@
 class ChromePromptChannelProtobuf : public ChromePromptChannel {
  public:
   static const char kErrorHistogramName[];
+  static constexpr uint32_t kMaxMessageLength = 1 * 1024 * 1024;  // 1M bytes
 
   // Values from this enum will serve as the high bits of the histogram values.
   // We will be able to use them to separate the errors by category if we ever
@@ -153,6 +154,7 @@
     kWrongHandshakeVersion = 1,
     kRequestLengthShortRead = 2,
     kRequestShortRead = 3,
+    kRequestInvalidSize = 4,
   };
 
   static int32_t GetErrorCodeInt(ErrorCategory category,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
index ee17414..17f4031 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -36,6 +37,8 @@
 namespace {
 
 static constexpr uint8_t kVersion = 1U;
+static constexpr uint32_t kErrorMoreData =
+    0xEA;  // Equivalent to Windows ERROR_MORE_DATA
 
 // Get the error category from a histogram sample.
 ChromePromptChannelProtobuf::ErrorCategory SampleToCategory(
@@ -142,6 +145,11 @@
   // Closes the cleaner process pipe handles to simulate the cleaner process
   // exiting.
   void CloseCleanerHandles() {
+    // Cancel anything that might be left to be read/written for fail-scenario
+    // tests.
+    ::CancelIoEx(response_read_handle_.Get(), nullptr);
+    ::CancelIoEx(request_write_handle_.Get(), nullptr);
+
     response_read_handle_.Close();
     request_write_handle_.Close();
   }
@@ -153,19 +161,45 @@
                        base::Unretained(this)));
   }
 
-  void WriteVersion(uint8_t version) {
+  template <typename T>
+  void WriteByValue(T value) {
     DWORD bytes_written = 0;
-    ASSERT_TRUE(::WriteFile(request_write_handle_.Get(), &version,
-                            sizeof(version), &bytes_written, nullptr));
-    ASSERT_EQ(bytes_written, sizeof(version));
+    ASSERT_TRUE(::WriteFile(request_write_handle_.Get(), &value, sizeof(value),
+                            &bytes_written, nullptr));
+    ASSERT_EQ(bytes_written, sizeof(value));
   }
 
-  // Writes the version to the pipe without blocking the main test thread.
-  void PostWriteVersion(uint8_t version) {
+  template <typename T>
+  void WriteByPointer(const T* ptr, uint32_t size, bool should_succeed) {
+    DWORD bytes_written = 0;
+    ASSERT_EQ(::WriteFile(request_write_handle_.Get(), ptr, size,
+                          &bytes_written, nullptr),
+              should_succeed);
+
+    // On a failed write we don't care about the number of bytes read.
+    if (should_succeed) {
+      ASSERT_EQ(bytes_written, size);
+    }
+  }
+
+  // Writes bytes taken by pointer to the pipe without blocking the main test
+  // thread.
+  template <typename T>
+  void PostWriteByPointer(const T* ptr, uint32_t size, bool should_succeed) {
     channel_->task_runner()->PostTask(
         FROM_HERE,
-        base::BindOnce(&ChromePromptChannelProtobufTest::WriteVersion,
-                       base::Unretained(this), version));
+        base::BindOnce(&ChromePromptChannelProtobufTest::WriteByPointer<T>,
+                       base::Unretained(this), ptr, size, should_succeed));
+  }
+
+  // Writes bytes taken by value to the pipe without blocking the main test
+  // thread.
+  template <typename T>
+  void PostWriteByValue(T value) {
+    channel_->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ChromePromptChannelProtobufTest::WriteByValue<T>,
+                       base::Unretained(this), value));
   }
 
   void ExpectReadFails() {
@@ -200,6 +234,7 @@
   base::RunLoop run_loop_;
   ChromePromptChannelPtr channel_ =
       ChromePromptChannelPtr(nullptr, base::OnTaskRunnerDeleter(nullptr));
+
   base::win::ScopedHandle response_read_handle_;
   base::win::ScopedHandle request_write_handle_;
 
@@ -239,7 +274,8 @@
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
   // Invalid version
-  PostWriteVersion(128);
+  constexpr uint8_t kVersion = 128;
+  PostWriteByValue(kVersion);
   WaitForDisconnect();
 
   // We expect the the handshake to have failed because of the version.
@@ -255,7 +291,8 @@
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
   // Invalid version
-  PostWriteVersion(0U);
+  constexpr uint8_t kVersion = 0;
+  PostWriteByValue(kVersion);
   WaitForDisconnect();
 
   // We expect the the handshake to have failed because of the version.
@@ -270,17 +307,148 @@
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
   // Write version 1.
-  PostWriteVersion(kVersion);
+  PostWriteByValue(kVersion);
 
   // Simulate the cleaner exiting after writing the version.
   PostCloseCleanerHandles();
 
   WaitForDisconnect();
 
-  // There were no errors so the histogram should be empty
-  // TODO(crbug.com/969139): We should be getting a kReadRequestLengthWinError
-  // here.
-  ExpectHistogramEmpty();
+  ExpectCategoryErrorCount(
+      ChromePromptChannelProtobuf::ErrorCategory::kReadRequestLengthWinError,
+      1);
+
+  ExpectReadFails();
+}
+
+TEST_F(ChromePromptChannelProtobufTest, PostSizeOfZero) {
+  SetupCommunicationFailure();
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Valid version
+  PostWriteByValue(kVersion);
+
+  // Send invalid size
+  PostWriteByValue(0U);
+  WaitForDisconnect();
+
+  ExpectUniqueSample(
+      ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+      ChromePromptChannelProtobuf::CustomErrors::kRequestInvalidSize);
+  ExpectReadFails();
+}
+
+TEST_F(ChromePromptChannelProtobufTest, PostSizeMoreThanMax) {
+  SetupCommunicationFailure();
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Valid version
+  PostWriteByValue(kVersion);
+
+  // Send invalid size
+  PostWriteByValue(ChromePromptChannelProtobuf::kMaxMessageLength + 1);
+  WaitForDisconnect();
+
+  ExpectUniqueSample(
+      ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+      ChromePromptChannelProtobuf::CustomErrors::kRequestInvalidSize);
+  ExpectReadFails();
+}
+
+TEST_F(ChromePromptChannelProtobufTest, PostExtraData) {
+  SetupCommunicationFailure();
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Valid version
+  PostWriteByValue(kVersion);
+
+  constexpr uint32_t kSize = 10;
+  const std::vector<uint8_t> bytes(kSize);
+
+  // Post the size of the read.
+  PostWriteByValue(kSize - 1);
+
+  // Post slightly more data.
+  PostWriteByPointer(bytes.data(), bytes.size(), false);
+
+  WaitForDisconnect();
+
+  ExpectUniqueSample(
+      ChromePromptChannelProtobuf::ErrorCategory::kReadRequestWinError,
+      kErrorMoreData);
+
+  ExpectReadFails();
+}
+
+// The pipes are valid before ConnectToCleaner just as much as after.
+TEST_F(ChromePromptChannelProtobufTest, VersionSentBeforeConnection) {
+  SetupCommunicationFailure();
+
+  // Valid version but BEFORE connection
+  PostWriteByValue(kVersion);
+
+  // Connect
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Disconnect
+  PostCloseCleanerHandles();
+  WaitForDisconnect();
+
+  // The first read that fails is the reading of the length of the first
+  // request. That is because the sending of the version was successful (unless
+  // we see an error in the histogram which will cause a test failure) and we
+  // disconnect before sending a length.
+  ExpectCategoryErrorCount(
+      ChromePromptChannelProtobuf::ErrorCategory::kReadRequestLengthWinError,
+      1);
+
+  ExpectReadFails();
+}
+
+TEST_F(ChromePromptChannelProtobufTest, LengthShortWrite) {
+  SetupCommunicationFailure();
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Valid version
+  PostWriteByValue(kVersion);
+
+  // The receiving side expects to receive the size of the request using 4
+  // bytes. Setup data that is one byte less than that.
+  const std::vector<uint8_t> bytes(sizeof(uint32_t) - 1);
+
+  // Post the incomplete size data.
+  PostWriteByPointer(bytes.data(), bytes.size(), true);
+
+  WaitForDisconnect();
+
+  ExpectUniqueSample(
+      ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+      ChromePromptChannelProtobuf::CustomErrors::kRequestLengthShortRead);
+
+  ExpectReadFails();
+}
+
+TEST_F(ChromePromptChannelProtobufTest, RequestShortWrite) {
+  SetupCommunicationFailure();
+  channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
+
+  // Valid version
+  PostWriteByValue(kVersion);
+
+  constexpr uint32_t kSize = 10;
+  const std::vector<uint8_t> bytes(kSize);
+
+  // Post the size of the read. It's too big.
+  PostWriteByValue(kSize + 1);
+
+  // Post slightly less data.
+  PostWriteByPointer(bytes.data(), bytes.size(), true);
+
+  WaitForDisconnect();
+
+  ExpectUniqueSample(
+      ChromePromptChannelProtobuf::ErrorCategory::kCustomError,
+      ChromePromptChannelProtobuf::CustomErrors::kRequestShortRead);
 
   ExpectReadFails();
 }
diff --git a/chrome/browser/safe_browsing/download_protection/multipart_uploader.cc b/chrome/browser/safe_browsing/download_protection/multipart_uploader.cc
new file mode 100644
index 0000000..446d3b8
--- /dev/null
+++ b/chrome/browser/safe_browsing/download_protection/multipart_uploader.cc
@@ -0,0 +1,129 @@
+// Copyright 2019 The Chromium Authors. 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/safe_browsing/download_protection/multipart_uploader.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/mime_util.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/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// Constants associated with exponential backoff. On each failure, we will
+// increase the backoff by |kBackoffFactor|, starting from
+// |kInitialBackoffSeconds|. If we fail after |kMaxRetryAttempts| retries, the
+// upload fails.
+const int kInitialBackoffSeconds = 1;
+const int kBackoffFactor = 2;
+const int kMaxRetryAttempts = 2;
+
+// Content type of a full multipart request
+const char kUploadContentType[] = "multipart/related; boundary=";
+
+// Content type of the metadata and file contents.
+const char kDataContentType[] = "Content-Type: application/octet-stream";
+
+}  // namespace
+
+MultipartUploadRequest::MultipartUploadRequest(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const GURL& base_url,
+    const std::string& metadata,
+    const std::string& data,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+    Callback callback)
+    : base_url_(base_url),
+      metadata_(metadata),
+      data_(data),
+      boundary_(net::GenerateMimeMultipartBoundary()),
+      callback_(std::move(callback)),
+      current_backoff_(base::TimeDelta::FromSeconds(kInitialBackoffSeconds)),
+      retry_count_(0),
+      url_loader_factory_(url_loader_factory),
+      traffic_annotation_(traffic_annotation),
+      weak_factory_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+MultipartUploadRequest::~MultipartUploadRequest() {}
+
+void MultipartUploadRequest::Start() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SendRequest();
+}
+
+std::string MultipartUploadRequest::GenerateRequestBody(
+    const std::string& metadata,
+    const std::string& data) {
+  return "--" + boundary_ + "\r\n" + kDataContentType + "\r\n\r\n" + metadata +
+         "\r\n--" + boundary_ + "\r\n" + kDataContentType + "\r\n\r\n" + data +
+         "\r\n--" + boundary_ + "--\r\n";
+}
+
+void MultipartUploadRequest::SendRequest() {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = base_url_;
+  resource_request->method = "POST";
+  resource_request->headers.SetHeader("X-Goog-Upload-Protocol", "multipart");
+
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation_);
+  url_loader_->SetAllowHttpErrorResults(true);
+  url_loader_->AttachStringForUpload(GenerateRequestBody(metadata_, data_),
+                                     kUploadContentType + boundary_);
+
+  url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&MultipartUploadRequest::OnURLLoaderComplete,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MultipartUploadRequest::OnURLLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  int response_code = 0;
+  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
+    response_code = url_loader_->ResponseInfo()->headers->response_code();
+
+  RetryOrFinish(url_loader_->NetError(), response_code,
+                std::move(response_body));
+}
+
+void MultipartUploadRequest::RetryOrFinish(
+    int net_error,
+    int response_code,
+    std::unique_ptr<std::string> response_body) {
+  // TODO(drubery): Add metrics for success rates here.
+  if (net_error == net::OK && response_code == net::HTTP_OK) {
+    std::move(callback_).Run(net::OK, net::HTTP_OK, *response_body.get());
+  } else {
+    if (retry_count_ < kMaxRetryAttempts) {
+      base::PostDelayedTaskWithTraits(
+          FROM_HERE, {content::BrowserThread::UI},
+          base::BindOnce(&MultipartUploadRequest::SendRequest,
+                         weak_factory_.GetWeakPtr()),
+          current_backoff_);
+      current_backoff_ *= kBackoffFactor;
+      retry_count_++;
+    } else {
+      std::move(callback_).Run(net_error, response_code, *response_body.get());
+    }
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/multipart_uploader.h b/chrome/browser/safe_browsing/download_protection/multipart_uploader.h
new file mode 100644
index 0000000..63c95302
--- /dev/null
+++ b/chrome/browser/safe_browsing/download_protection/multipart_uploader.h
@@ -0,0 +1,93 @@
+// Copyright 2019 The Chromium 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_SAFE_BROWSING_DOWNLOAD_PROTECTION_MULTIPART_UPLOADER_H_
+#define CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_MULTIPART_UPLOADER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
+namespace safe_browsing {
+
+// This class encapsulates the upload of a file with metadata using the
+// multipart protocol. This class is neither movable nor copyable.
+class MultipartUploadRequest {
+ public:
+  using Callback = base::OnceCallback<
+      void(int net_error, int response_code, const std::string& response_data)>;
+
+  // Creates a MultipartUploadRequest, which will upload |data| to the given
+  // |base_url| with |metadata| attached.
+  MultipartUploadRequest(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const GURL& base_url,
+      const std::string& metadata,
+      const std::string& data,
+      const net::NetworkTrafficAnnotationTag& traffic_annotation,
+      Callback callback);
+  MultipartUploadRequest(const MultipartUploadRequest&) = delete;
+  MultipartUploadRequest& operator=(const MultipartUploadRequest&) = delete;
+  MultipartUploadRequest(MultipartUploadRequest&&) = delete;
+  MultipartUploadRequest& operator=(MultipartUploadRequest&&) = delete;
+
+  ~MultipartUploadRequest();
+
+  // Start the upload. This must be called on the UI thread. When complete, this
+  // will call |callback_| on the UI thread.
+  void Start();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(MultipartUploadRequestTest, GeneratesCorrectBody);
+  FRIEND_TEST_ALL_PREFIXES(MultipartUploadRequestTest, RetriesCorrectly);
+
+  // Set the boundary between parts.
+  void set_boundary(const std::string& boundary) { boundary_ = boundary; }
+
+  // Helper method to create the multipart request body.
+  std::string GenerateRequestBody(const std::string& metadata,
+                                  const std::string& data);
+
+  // Called whenever a net request finishes (on success or failure).
+  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  // Called whenever a net request finishes (on success or failure).
+  void RetryOrFinish(int net_error,
+                     int response_code,
+                     std::unique_ptr<std::string> response_body);
+
+  // Called to send a single request. Is overridden in tests.
+  virtual void SendRequest();
+
+  GURL base_url_;
+  std::string metadata_;
+  std::string data_;
+  std::string boundary_;
+  Callback callback_;
+
+  base::TimeDelta current_backoff_;
+  int retry_count_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+  net::NetworkTrafficAnnotationTag traffic_annotation_;
+
+  base::WeakPtrFactory<MultipartUploadRequest> weak_factory_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_MULTIPART_UPLOADER_H_
diff --git a/chrome/browser/safe_browsing/download_protection/multipart_uploader_unittest.cc b/chrome/browser/safe_browsing/download_protection/multipart_uploader_unittest.cc
new file mode 100644
index 0000000..bfec3928
--- /dev/null
+++ b/chrome/browser/safe_browsing/download_protection/multipart_uploader_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/download_protection/multipart_uploader.h"
+
+#include <memory>
+
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+using ::testing::Invoke;
+
+class MultipartUploadRequestTest : public testing::Test {
+ public:
+  MultipartUploadRequestTest()
+      : thread_bundle_(
+            base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME) {}
+
+ protected:
+  content::TestBrowserThreadBundle thread_bundle_;
+};
+
+class MockMultipartUploadRequest : public MultipartUploadRequest {
+ public:
+  MockMultipartUploadRequest(const std::string& metadata,
+                             const std::string& data,
+                             Callback callback)
+      : MultipartUploadRequest(nullptr,
+                               GURL(),
+                               metadata,
+                               data,
+                               TRAFFIC_ANNOTATION_FOR_TESTS,
+                               std::move(callback)) {}
+
+  MOCK_METHOD(void, SendRequest, (), (override));
+};
+
+TEST_F(MultipartUploadRequestTest, GeneratesCorrectBody) {
+  MultipartUploadRequest request(nullptr, GURL(), "metadata", "data",
+                                 TRAFFIC_ANNOTATION_FOR_TESTS,
+                                 base::DoNothing());
+
+  std::string expected_body =
+      "--boundary\r\n"
+      "Content-Type: application/octet-stream\r\n"
+      "\r\n"
+      "metadata\r\n"
+      "--boundary\r\n"
+      "Content-Type: application/octet-stream\r\n"
+      "\r\n"
+      "file data\r\n"
+      "--boundary--\r\n";
+
+  request.set_boundary("boundary");
+  EXPECT_EQ(request.GenerateRequestBody("metadata", "file data"),
+            expected_body);
+}
+
+TEST_F(MultipartUploadRequestTest, RetriesCorrectly) {
+  MockMultipartUploadRequest mock_request("metadata", "data",
+                                          base::DoNothing());
+  EXPECT_CALL(mock_request, SendRequest())
+      .Times(3)
+      .WillRepeatedly(Invoke([&mock_request]() {
+        mock_request.RetryOrFinish(net::OK, net::HTTP_BAD_REQUEST,
+                                   std::make_unique<std::string>("response"));
+      }));
+  mock_request.Start();
+  thread_bundle_.FastForwardUntilNoTasksRemain();
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service.cc b/chrome/browser/search/chrome_colors/chrome_colors_service.cc
index f1b2975..9a13ba7 100644
--- a/chrome/browser/search/chrome_colors/chrome_colors_service.cc
+++ b/chrome/browser/search/chrome_colors/chrome_colors_service.cc
@@ -5,15 +5,35 @@
 #include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "base/metrics/user_metrics.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/common/search/generated_colors_info.h"
 #include "chrome/common/search/selected_colors_info.h"
 
+namespace {
+
+// Records whether current theme changes are confirmed or reverted.
+void RecordChangesConfirmedHistogram(bool confirmed) {
+  UMA_HISTOGRAM_BOOLEAN("ChromeColors.ChangesConfirmed", confirmed);
+}
+
+}  // namespace
+
 namespace chrome_colors {
 
 ChromeColorsService::ChromeColorsService(Profile* profile)
-    : theme_service_(ThemeServiceFactory::GetForProfile(profile)) {}
+    : theme_service_(ThemeServiceFactory::GetForProfile(profile)) {
+  // Determine if we are using a third-party NTP. When user switches to
+  // third-party NTP we should revert all the changes.
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile);
+  if (template_url_service) {
+    search_provider_observer_ = std::make_unique<SearchProviderObserver>(
+        template_url_service,
+        base::BindRepeating(&ChromeColorsService::OnSearchProviderChanged,
+                            weak_ptr_factory_.GetWeakPtr()));
+  }
+}
 
 ChromeColorsService::~ChromeColorsService() = default;
 
@@ -35,33 +55,54 @@
 }
 
 void ChromeColorsService::ApplyDefaultTheme(content::WebContents* tab) {
+  if (!search_provider_observer_ || !search_provider_observer_->is_google())
+    return;
   SaveThemeRevertState(tab);
   theme_service_->UseDefaultTheme();
 }
 
 void ChromeColorsService::ApplyAutogeneratedTheme(SkColor color,
                                                   content::WebContents* tab) {
+  if (!search_provider_observer_ || !search_provider_observer_->is_google())
+    return;
   SaveThemeRevertState(tab);
   theme_service_->BuildFromColor(color);
 }
 
 void ChromeColorsService::RevertThemeChangesForTab(content::WebContents* tab) {
-  if (dialog_tab_ == tab) {
-    RecordAction(base::UserMetricsAction("ChromeColors_TabClosed"));
-    RevertThemeChanges();
-  }
+  if (!search_provider_observer_ || !search_provider_observer_->is_google() ||
+      dialog_tab_ != tab)
+    return;
+  RevertThemeChangesWithReason(RevertReason::TAB_CLOSED);
 }
 
 void ChromeColorsService::RevertThemeChanges() {
-  if (!revert_theme_changes_.is_null()) {
-    std::move(revert_theme_changes_).Run();
-    ConfirmThemeChanges();
-  }
+  if (!search_provider_observer_ || !search_provider_observer_->is_google())
+    return;
+  RevertThemeChangesWithReason(RevertReason::MENU_CANCEL);
 }
 
 void ChromeColorsService::ConfirmThemeChanges() {
+  if (!search_provider_observer_ || !search_provider_observer_->is_google())
+    return;
   revert_theme_changes_.Reset();
   dialog_tab_ = nullptr;
+  RecordChangesConfirmedHistogram(true);
+}
+
+void ChromeColorsService::RevertThemeChangesWithReason(RevertReason reason) {
+  if (!revert_theme_changes_.is_null()) {
+    std::move(revert_theme_changes_).Run();
+    revert_theme_changes_.Reset();
+    dialog_tab_ = nullptr;
+    UMA_HISTOGRAM_ENUMERATION("ChromeColors.RevertReason", reason);
+    RecordChangesConfirmedHistogram(false);
+  }
+}
+
+void ChromeColorsService::OnSearchProviderChanged() {
+  if (search_provider_observer_ && !search_provider_observer_->is_google())
+    RevertThemeChangesWithReason(RevertReason::SEARCH_PROVIDER_CHANGE);
 }
 
 void ChromeColorsService::SaveThemeRevertState(content::WebContents* tab) {
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service.h b/chrome/browser/search/chrome_colors/chrome_colors_service.h
index d89a142..020cded 100644
--- a/chrome/browser/search/chrome_colors/chrome_colors_service.h
+++ b/chrome/browser/search/chrome_colors/chrome_colors_service.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SEARCH_CHROME_COLORS_CHROME_COLORS_SERVICE_H_
 
 #include "base/callback.h"
+#include "chrome/browser/search/search_provider_observer.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/web_contents.h"
@@ -15,10 +16,22 @@
 
 namespace chrome_colors {
 
+// Different cases that will trigger a revert for theme changes.
+// This enum must match the numbering for ChromeColorsRevertReason in enums.xml.
+// Do not reorder or remove items, and update kMaxValue when new items are
+// added.
+enum class RevertReason {
+  MENU_CANCEL = 0,
+  SEARCH_PROVIDER_CHANGE = 1,
+  TAB_CLOSED = 2,
+  kMaxValue = TAB_CLOSED,
+};
+
 // Supports theme changes originating from the NTP customization menu. Users can
 // apply a Chrome color or the default theme, which will then either be reverted
 // or confirmed and made permanent. If third party themes are present, users
 // will also have a choice to permanently uninstall it.
+// This service only works for Google NTP.
 class ChromeColorsService : public KeyedService {
  public:
   explicit ChromeColorsService(Profile* profile);
@@ -37,7 +50,8 @@
   void ApplyDefaultTheme(content::WebContents* tab);
   void ApplyAutogeneratedTheme(SkColor color, content::WebContents* tab);
 
-  // Reverts to the previous theme state before first Apply* was used.
+  // Reverts to the previous theme state before first Apply* was used. Called
+  // because user action on colors menu.
   void RevertThemeChanges();
 
   // Same as |RevertThemeChanges| but only reverts theme changes if they were
@@ -51,6 +65,12 @@
  private:
   friend class ::TestChromeColorsService;
 
+  // Reverts to the previous theme state and records |reason|.
+  void RevertThemeChangesWithReason(RevertReason reason);
+
+  // Callback for search provider change.
+  void OnSearchProviderChanged();
+
   // Saves the necessary state(revert callback and the current tab) for
   // performing theme change revert. Saves the state only if it is not set.
   void SaveThemeRevertState(content::WebContents* tab);
@@ -67,6 +87,11 @@
   // callback's creation.
   base::OnceClosure revert_theme_changes_;
 
+  // Keeps track of any changes in search engine provider. May be null.
+  std::unique_ptr<SearchProviderObserver> search_provider_observer_;
+
+  base::WeakPtrFactory<ChromeColorsService> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(ChromeColorsService);
 };
 
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service_unittest.cc b/chrome/browser/search/chrome_colors/chrome_colors_service_unittest.cc
index 78a89008..c9865b0 100644
--- a/chrome/browser/search/chrome_colors/chrome_colors_service_unittest.cc
+++ b/chrome/browser/search/chrome_colors/chrome_colors_service_unittest.cc
@@ -3,10 +3,17 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/search/chrome_colors/chrome_colors_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/search_test_utils.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_service.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -16,6 +23,10 @@
 
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
+
+    template_url_service_ = TemplateURLServiceFactory::GetForProfile(profile());
+    search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service_);
+
     chrome_colors_service_ =
         chrome_colors::ChromeColorsFactory::GetForProfile(profile());
 
@@ -27,8 +38,33 @@
     return !chrome_colors_service_->revert_theme_changes_.is_null();
   }
 
+  void SetUserSelectedDefaultSearchProvider(const std::string& base_url) {
+    TemplateURLData data;
+    data.SetShortName(base::UTF8ToUTF16(base_url));
+    data.SetKeyword(base::UTF8ToUTF16(base_url));
+    data.SetURL(base_url + "url?bar={searchTerms}");
+    data.new_tab_url = base_url + "newtab";
+    data.alternate_urls.push_back(base_url + "alt#quux={searchTerms}");
+
+    TemplateURL* template_url =
+        template_url_service_->Add(std::make_unique<TemplateURL>(data));
+    template_url_service_->SetUserSelectedDefaultSearchProvider(template_url);
+  }
+
   chrome_colors::ChromeColorsService* chrome_colors_service_;
   content::WebContents* tab_;
+
+ private:
+  // BrowserWithTestWindowTest override:
+  TestingProfile* CreateProfile() override {
+    TestingProfile* profile = BrowserWithTestWindowTest::CreateProfile();
+    TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+        profile,
+        base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
+    return profile;
+  }
+
+  TemplateURLService* template_url_service_;
 };
 
 TEST_F(TestChromeColorsService, ApplyAndConfirmAutogeneratedTheme) {
@@ -167,3 +203,23 @@
   EXPECT_FALSE(theme_service->UsingAutogenerated());
   EXPECT_FALSE(HasThemeRevertCallback());
 }
+
+TEST_F(TestChromeColorsService, RevertThemeChangesWhenSwitchToThirdPartyNTP) {
+  ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile());
+  ASSERT_TRUE(theme_service->UsingDefaultTheme());
+
+  SkColor theme_color = SkColorSetRGB(100, 0, 200);
+  chrome_colors_service_->ApplyAutogeneratedTheme(theme_color, tab_);
+  EXPECT_TRUE(theme_service->UsingAutogenerated());
+  EXPECT_TRUE(HasThemeRevertCallback());
+
+  // Switching to third-party NTP should revert current changes.
+  SetUserSelectedDefaultSearchProvider("www.third-party-ntp.com");
+  EXPECT_FALSE(theme_service->UsingAutogenerated());
+  EXPECT_FALSE(HasThemeRevertCallback());
+
+  // When third-party NTP is present autogenerated theme shouldn't apply.
+  chrome_colors_service_->ApplyAutogeneratedTheme(theme_color, tab_);
+  EXPECT_FALSE(theme_service->UsingAutogenerated());
+  EXPECT_FALSE(HasThemeRevertCallback());
+}
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index d34cd2b..327c90e 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -44,9 +44,6 @@
 #include "components/ntp_tiles/constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/search/search.h"
-#include "components/search_engines/template_url_service.h"
-#include "components/search_engines/template_url_service_observer.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -146,44 +143,6 @@
 
 const char kNtpCustomBackgroundMainColor[] = "background_main_color";
 
-// Keeps track of any changes in search engine provider and notifies
-// InstantService if a third-party search provider (i.e. a third-party NTP) is
-// being used.
-class InstantService::SearchProviderObserver
-    : public TemplateURLServiceObserver {
- public:
-  explicit SearchProviderObserver(TemplateURLService* service,
-                                  base::RepeatingClosure callback)
-      : service_(service),
-        is_google_(search::DefaultSearchProviderIsGoogle(service_)),
-        callback_(std::move(callback)) {
-    DCHECK(service_);
-    service_->AddObserver(this);
-  }
-
-  ~SearchProviderObserver() override {
-    if (service_)
-      service_->RemoveObserver(this);
-  }
-
-  bool is_google() { return is_google_; }
-
- private:
-  void OnTemplateURLServiceChanged() override {
-    is_google_ = search::DefaultSearchProviderIsGoogle(service_);
-    callback_.Run();
-  }
-
-  void OnTemplateURLServiceShuttingDown() override {
-    service_->RemoveObserver(this);
-    service_ = nullptr;
-  }
-
-  TemplateURLService* service_;
-  bool is_google_;
-  base::RepeatingClosure callback_;
-};
-
 InstantService::InstantService(Profile* profile)
     : profile_(profile),
       most_visited_info_(std::make_unique<InstantMostVisitedInfo>()),
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 9cf98e2..723cb98e 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "build/build_config.h"
+#include "chrome/browser/search/search_provider_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -180,8 +181,6 @@
   void FetchCustomBackground(base::TimeTicks timestamp, const GURL& fetch_url);
 
  private:
-  class SearchProviderObserver;
-
   friend class InstantExtendedTest;
   friend class InstantUnitTestBase;
   friend class LocalNTPBackgroundsAndDarkModeTest;
diff --git a/chrome/browser/search/search_provider_observer.cc b/chrome/browser/search/search_provider_observer.cc
new file mode 100644
index 0000000..4a3bdaa
--- /dev/null
+++ b/chrome/browser/search/search_provider_observer.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 "chrome/browser/search/search_provider_observer.h"
+
+SearchProviderObserver::SearchProviderObserver(TemplateURLService* service,
+                                               base::RepeatingClosure callback)
+    : service_(service),
+      is_google_(search::DefaultSearchProviderIsGoogle(service_)),
+      callback_(std::move(callback)) {
+  DCHECK(service_);
+  service_->AddObserver(this);
+}
+
+SearchProviderObserver::~SearchProviderObserver() {
+  if (service_)
+    service_->RemoveObserver(this);
+}
+
+void SearchProviderObserver::OnTemplateURLServiceChanged() {
+  is_google_ = search::DefaultSearchProviderIsGoogle(service_);
+  callback_.Run();
+}
+
+void SearchProviderObserver::OnTemplateURLServiceShuttingDown() {
+  service_->RemoveObserver(this);
+  service_ = nullptr;
+}
diff --git a/chrome/browser/search/search_provider_observer.h b/chrome/browser/search/search_provider_observer.h
new file mode 100644
index 0000000..009506d7a
--- /dev/null
+++ b/chrome/browser/search/search_provider_observer.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_SEARCH_SEARCH_PROVIDER_OBSERVER_H_
+#define CHROME_BROWSER_SEARCH_SEARCH_PROVIDER_OBSERVER_H_
+
+#include "chrome/browser/search/search.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_service_observer.h"
+
+// Keeps track of any changes in search engine provider and call
+// the provided callback if a third-party search provider (i.e. a third-party
+// NTP) is being used.
+class SearchProviderObserver : public TemplateURLServiceObserver {
+ public:
+  explicit SearchProviderObserver(TemplateURLService* service,
+                                  base::RepeatingClosure callback);
+
+  ~SearchProviderObserver() override;
+
+  bool is_google() { return is_google_; }
+
+ private:
+  // TemplateURLServiceObserver:
+  void OnTemplateURLServiceChanged() override;
+  void OnTemplateURLServiceShuttingDown() override;
+
+  TemplateURLService* service_;
+  bool is_google_;
+  base::RepeatingClosure callback_;
+};
+
+#endif  // CHROME_BROWSER_SEARCH_SEARCH_PROVIDER_OBSERVER_H_
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
index 4424c63..7552030 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
@@ -77,6 +77,9 @@
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   return IsSendingEnabled() && IsUserSyncTypeActive(profile) &&
          HasValidTargetDevice(profile) &&
+         // Send tab to self should not be offered for tel links, click to call
+         // feature will be handling tel links.
+         !link_url.SchemeIs(url::kTelScheme) &&
          (IsContentRequirementsMet(web_contents->GetURL(), profile) ||
           IsContentRequirementsMet(link_url, profile));
 }
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
index 891d033..1a467b0 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
@@ -132,6 +132,39 @@
   EXPECT_FALSE(IsContentRequirementsMet(url_, incognito_profile_));
 }
 
+TEST_F(SendTabToSelfUtilTest, ShouldOfferFeatureForTelephoneLink) {
+  url_ = GURL("tel:07387252578");
+
+  // Set the IsSendingEnable, IsUserSyncTypeActive and
+  // HasValidTargetDevice to true
+  scoped_feature_list_.InitWithFeatures(
+      {switches::kSyncSendTabToSelf, kSendTabToSelfShowSendingUI}, {});
+  AddTab(browser(), url_);
+  SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), base::BindRepeating(&BuildTestSendTabToSelfSyncService));
+
+  // get web contents
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_FALSE(ShouldOfferFeatureForLink(web_contents, url_));
+}
+
+TEST_F(SendTabToSelfUtilTest, ShouldOfferFeatureForGoogleLink) {
+  // Set the IsSendingEnable, IsUserSyncTypeActive and
+  // HasValidTargetDevice to true
+  scoped_feature_list_.InitWithFeatures(
+      {switches::kSyncSendTabToSelf, kSendTabToSelfShowSendingUI}, {});
+  AddTab(browser(), url_);
+  SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), base::BindRepeating(&BuildTestSendTabToSelfSyncService));
+
+  // get web contents
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(ShouldOfferFeatureForLink(web_contents, url_));
+}
 }  // namespace
 
 }  // namespace send_tab_to_self
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
index 1bfe2943..437173c 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
@@ -61,7 +61,8 @@
   url_ = params.link_url;
   devices_ = sharing_service_->GetDeviceCandidates(
       static_cast<int>(SharingDeviceCapability::kTelephony));
-  LogClickToCallDevicesToShow(devices_.size());
+  LogClickToCallDevicesToShow(kSharingClickToCallUiContextMenu,
+                              devices_.size());
   if (devices_.empty())
     return;
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
index 3e1c96c..87d5fa2 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
@@ -47,14 +47,15 @@
 class MockSharingService : public SharingService {
  public:
   explicit MockSharingService(std::unique_ptr<SharingFCMHandler> fcm_handler)
-      : SharingService(nullptr,
-                       nullptr,
-                       nullptr,
-                       nullptr,
+      : SharingService(/* sync_prefs= */ nullptr,
+                       /* vapid_key_manager= */ nullptr,
+                       /* sharing_device_registration= */ nullptr,
+                       /* fcm_sender= */ nullptr,
                        std::move(fcm_handler),
-                       nullptr,
-                       nullptr,
-                       nullptr) {}
+                       /* gcm_driver= */ nullptr,
+                       /* device_info_tracker= */ nullptr,
+                       /* local_device_info_provider= */ nullptr,
+                       /* sync_service */ nullptr) {}
 
   ~MockSharingService() override = default;
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
index cc5d714..b7381087 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
@@ -81,7 +81,10 @@
 void ClickToCallSharingDialogController::ShowNewDialog() {
   if (dialog_)
     dialog_->Hide();
-  DCHECK(!dialog_);
+
+  // Treat the dialog as closed as the process of closing the native widget
+  // might be async.
+  dialog_ = nullptr;
 
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
   auto* window = browser ? browser->window() : nullptr;
@@ -174,7 +177,12 @@
                                                          web_contents_);
 }
 
-void ClickToCallSharingDialogController::OnDialogClosed() {
+void ClickToCallSharingDialogController::OnDialogClosed(
+    ClickToCallDialog* dialog) {
+  // Ignore already replaced dialogs.
+  if (dialog != dialog_)
+    return;
+
   dialog_ = nullptr;
   UpdateIcon();
 }
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
index 19964663..6c26b47 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
@@ -45,7 +45,7 @@
   void OnAppChosen(const App& app) override;
 
   // Called by the ClickToCallDialogView when it is being closed.
-  void OnDialogClosed();
+  void OnDialogClosed(ClickToCallDialog* dialog);
 
   // Called by the ClickToCallDialogView when the help text got clicked.
   void OnHelpTextClicked();
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
index 71994b7..54b85d0a 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
@@ -49,6 +49,7 @@
                        /* sharing_device_registration= */ nullptr,
                        /* fcm_sender= */ nullptr,
                        std::move(fcm_handler),
+                       /* gcm_driver= */ nullptr,
                        /* device_info_tracker= */ nullptr,
                        /* local_device_info_provider= */ nullptr,
                        /* sync_service */ nullptr) {}
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
index 6bfce60..9843dc9 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
@@ -39,14 +39,15 @@
 class MockSharingService : public SharingService {
  public:
   explicit MockSharingService(std::unique_ptr<SharingFCMHandler> fcm_handler)
-      : SharingService(nullptr,
-                       nullptr,
-                       nullptr,
-                       nullptr,
+      : SharingService(/* sync_prefs= */ nullptr,
+                       /* vapid_key_manager= */ nullptr,
+                       /* sharing_device_registration= */ nullptr,
+                       /* fcm_sender= */ nullptr,
                        std::move(fcm_handler),
-                       nullptr,
-                       nullptr,
-                       nullptr) {}
+                       /* gcm_driver= */ nullptr,
+                       /* device_info_tracker= */ nullptr,
+                       /* local_device_info_provider= */ nullptr,
+                       /* sync_service */ nullptr) {}
 
   ~MockSharingService() override = default;
 
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index b62c08d..4e7d35080 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -15,11 +15,9 @@
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_device_info.h"
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
-#include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
 #include "components/gcm_driver/crypto/p256_key_util.h"
-#include "components/gcm_driver/gcm_driver.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/sync_device_info/device_info.h"
 #include "components/sync_device_info/local_device_info_provider.h"
@@ -33,12 +31,10 @@
     SharingSyncPreference* sharing_sync_preference,
     instance_id::InstanceIDDriver* instance_id_driver,
     VapidKeyManager* vapid_key_manager,
-    gcm::GCMDriver* gcm_driver,
     syncer::LocalDeviceInfoProvider* local_device_info_provider)
     : sharing_sync_preference_(sharing_sync_preference),
       instance_id_driver_(instance_id_driver),
       vapid_key_manager_(vapid_key_manager),
-      gcm_driver_(gcm_driver),
       local_device_info_provider_(local_device_info_provider) {}
 
 SharingDeviceRegistration::~SharingDeviceRegistration() = default;
@@ -46,8 +42,6 @@
 void SharingDeviceRegistration::RegisterDevice(RegistrationCallback callback) {
   base::Optional<std::string> authorized_entity = GetAuthorizationEntity();
   if (!authorized_entity) {
-    LogSharingRegistrationResult(
-        SharingDeviceRegistrationResult::kEncryptionError);
     std::move(callback).Run(SharingDeviceRegistrationResult::kEncryptionError);
     return;
   }
@@ -56,7 +50,8 @@
   if (registration && registration->authorized_entity == authorized_entity &&
       (base::Time::Now() - registration->timestamp < kRegistrationExpiration)) {
     // Authorized entity hasn't changed nor has expired, skip to next step.
-    RetrieveEncrpytionInfo(std::move(callback), registration->fcm_token);
+    RetrieveEncryptionInfo(std::move(callback), registration->authorized_entity,
+                           registration->fcm_token);
     return;
   }
 
@@ -78,34 +73,33 @@
     case instance_id::InstanceID::SUCCESS:
       sharing_sync_preference_->SetFCMRegistration(
           {authorized_entity, fcm_registration_token, base::Time::Now()});
-      RetrieveEncrpytionInfo(std::move(callback), fcm_registration_token);
+      RetrieveEncryptionInfo(std::move(callback), authorized_entity,
+                             fcm_registration_token);
       break;
     case instance_id::InstanceID::NETWORK_ERROR:
     case instance_id::InstanceID::SERVER_ERROR:
     case instance_id::InstanceID::ASYNC_OPERATION_PENDING:
-      LogSharingRegistrationResult(
-          SharingDeviceRegistrationResult::kFcmTransientError);
       std::move(callback).Run(
           SharingDeviceRegistrationResult::kFcmTransientError);
       break;
     case instance_id::InstanceID::INVALID_PARAMETER:
     case instance_id::InstanceID::UNKNOWN_ERROR:
     case instance_id::InstanceID::DISABLED:
-      LogSharingRegistrationResult(
-          SharingDeviceRegistrationResult::kFcmFatalError);
       std::move(callback).Run(SharingDeviceRegistrationResult::kFcmFatalError);
       break;
   }
 }
 
-void SharingDeviceRegistration::RetrieveEncrpytionInfo(
+void SharingDeviceRegistration::RetrieveEncryptionInfo(
     RegistrationCallback callback,
+    const std::string& authorized_entity,
     const std::string& fcm_registration_token) {
-  gcm_driver_->GetEncryptionInfo(
-      kSharingFCMAppID,
-      base::BindOnce(&SharingDeviceRegistration::OnEncryptionInfoReceived,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     fcm_registration_token));
+  instance_id_driver_->GetInstanceID(kSharingFCMAppID)
+      ->GetEncryptionInfo(
+          authorized_entity,
+          base::BindOnce(&SharingDeviceRegistration::OnEncryptionInfoReceived,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                         fcm_registration_token));
 }
 
 void SharingDeviceRegistration::OnEncryptionInfoReceived(
@@ -116,8 +110,6 @@
   const syncer::DeviceInfo* local_device_info =
       local_device_info_provider_->GetLocalDeviceInfo();
   if (!local_device_info) {
-    LogSharingRegistrationResult(
-        SharingDeviceRegistrationResult::kSyncServiceError);
     std::move(callback).Run(SharingDeviceRegistrationResult::kSyncServiceError);
     return;
   }
@@ -127,30 +119,26 @@
       fcm_registration_token, std::move(p256dh), std::move(auth_secret),
       device_capabilities);
   sharing_sync_preference_->SetSyncDevice(local_device_info->guid(), device);
-  LogSharingRegistrationResult(SharingDeviceRegistrationResult::kSuccess);
   std::move(callback).Run(SharingDeviceRegistrationResult::kSuccess);
 }
 
 void SharingDeviceRegistration::UnregisterDevice(
     RegistrationCallback callback) {
-  sharing_sync_preference_->ClearFCMRegistration();
+  auto registration = sharing_sync_preference_->GetFCMRegistration();
+  if (!registration) {
+    std::move(callback).Run(
+        SharingDeviceRegistrationResult::kDeviceNotRegistered);
+    return;
+  }
 
   const syncer::DeviceInfo* local_device_info =
       local_device_info_provider_->GetLocalDeviceInfo();
   if (local_device_info)
     sharing_sync_preference_->RemoveDevice(local_device_info->guid());
 
-  base::Optional<std::string> authorized_entity = GetAuthorizationEntity();
-  if (!authorized_entity) {
-    LogSharingUnegistrationResult(
-        SharingDeviceRegistrationResult::kEncryptionError);
-    std::move(callback).Run(SharingDeviceRegistrationResult::kEncryptionError);
-    return;
-  }
-
   instance_id_driver_->GetInstanceID(kSharingFCMAppID)
       ->DeleteToken(
-          *authorized_entity, kFCMScope,
+          registration->authorized_entity, kFCMScope,
           base::BindOnce(&SharingDeviceRegistration::OnFCMTokenDeleted,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -163,21 +151,17 @@
       // INVALID_PARAMETER is expected if InstanceID.GetToken hasn't been
       // invoked since restart.
     case instance_id::InstanceID::INVALID_PARAMETER:
-      LogSharingUnegistrationResult(SharingDeviceRegistrationResult::kSuccess);
+      sharing_sync_preference_->ClearFCMRegistration();
       std::move(callback).Run(SharingDeviceRegistrationResult::kSuccess);
       return;
     case instance_id::InstanceID::NETWORK_ERROR:
     case instance_id::InstanceID::SERVER_ERROR:
     case instance_id::InstanceID::ASYNC_OPERATION_PENDING:
-      LogSharingUnegistrationResult(
-          SharingDeviceRegistrationResult::kFcmTransientError);
       std::move(callback).Run(
           SharingDeviceRegistrationResult::kFcmTransientError);
       return;
     case instance_id::InstanceID::UNKNOWN_ERROR:
     case instance_id::InstanceID::DISABLED:
-      LogSharingUnegistrationResult(
-          SharingDeviceRegistrationResult::kFcmFatalError);
       std::move(callback).Run(SharingDeviceRegistrationResult::kFcmFatalError);
       return;
   }
diff --git a/chrome/browser/sharing/sharing_device_registration.h b/chrome/browser/sharing/sharing_device_registration.h
index 5980cba..9e514bc 100644
--- a/chrome/browser/sharing/sharing_device_registration.h
+++ b/chrome/browser/sharing/sharing_device_registration.h
@@ -18,10 +18,6 @@
 class InstanceIDDriver;
 }
 
-namespace gcm {
-class GCMDriver;
-}
-
 namespace syncer {
 class LocalDeviceInfoProvider;
 }
@@ -41,7 +37,6 @@
       SharingSyncPreference* prefs,
       instance_id::InstanceIDDriver* instance_id_driver,
       VapidKeyManager* vapid_key_manager,
-      gcm::GCMDriver* gcm_driver,
       syncer::LocalDeviceInfoProvider* device_info_tracker);
   virtual ~SharingDeviceRegistration();
 
@@ -71,8 +66,9 @@
   void OnFCMTokenDeleted(RegistrationCallback callback,
                          instance_id::InstanceID::Result result);
 
-  // Retrieve encryption info from GCMDriver.
-  void RetrieveEncrpytionInfo(RegistrationCallback callback,
+  // Retrieve encryption info from InstanceID.
+  void RetrieveEncryptionInfo(RegistrationCallback callback,
+                              const std::string& authorized_entity,
                               const std::string& fcm_registration_token);
 
   // Callback function responsible for saving device registration information in
@@ -94,7 +90,6 @@
   SharingSyncPreference* sharing_sync_preference_;
   instance_id::InstanceIDDriver* instance_id_driver_;
   VapidKeyManager* vapid_key_manager_;
-  gcm::GCMDriver* gcm_driver_;
   syncer::LocalDeviceInfoProvider* local_device_info_provider_;
 
   base::WeakPtrFactory<SharingDeviceRegistration> weak_ptr_factory_{this};
diff --git a/chrome/browser/sharing/sharing_device_registration_result.h b/chrome/browser/sharing/sharing_device_registration_result.h
index f2fef8b..d72f527 100644
--- a/chrome/browser/sharing/sharing_device_registration_result.h
+++ b/chrome/browser/sharing/sharing_device_registration_result.h
@@ -20,8 +20,10 @@
   kFcmTransientError = 3,
   // Failed with FCM fatal error.
   kFcmFatalError = 4,
+  // Device has not been registered.
+  kDeviceNotRegistered = 5,
   // Max value for historgram.
-  kMaxValue = kFcmFatalError,
+  kMaxValue = kDeviceNotRegistered,
 };
 
 #endif  // CHROME_BROWSER_SHARING_SHARING_DEVICE_REGISTRATION_RESULT_H_
diff --git a/chrome/browser/sharing/sharing_device_registration_unittest.cc b/chrome/browser/sharing/sharing_device_registration_unittest.cc
index ba555d6..a0f8dfd 100644
--- a/chrome/browser/sharing/sharing_device_registration_unittest.cc
+++ b/chrome/browser/sharing/sharing_device_registration_unittest.cc
@@ -17,8 +17,6 @@
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
-#include "components/gcm_driver/fake_gcm_driver.h"
-#include "components/gcm_driver/gcm_driver.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/sync_device_info/device_info.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -95,17 +93,7 @@
 
   void SetFCMToken(std::string fcm_token) { fcm_token_ = std::move(fcm_token); }
 
- private:
-  InstanceID::Result result_;
-  std::string fcm_token_;
-};
-
-class FakeEncryptionGCMDriver : public FakeGCMDriver {
- public:
-  FakeEncryptionGCMDriver() = default;
-  ~FakeEncryptionGCMDriver() override = default;
-
-  void GetEncryptionInfo(const std::string& app_id,
+  void GetEncryptionInfo(const std::string& authorized_entity,
                          GetEncryptionInfoCallback callback) override {
     std::move(callback).Run(p256dh_, auth_secret_);
   }
@@ -117,7 +105,8 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(FakeEncryptionGCMDriver);
+  InstanceID::Result result_;
+  std::string fcm_token_;
   std::string p256dh_ = kDevicep256dh;
   std::string auth_secret_ = kDeviceAuthSecret;
 };
@@ -130,7 +119,6 @@
         sharing_device_registration_(&sync_prefs_,
                                      &mock_instance_id_driver_,
                                      &vapid_key_manager_,
-                                     &fake_encryption_gcm_driver_,
                                      &fake_local_device_info_provider_) {
     SharingSyncPreference::RegisterProfilePrefs(prefs_.registry());
   }
@@ -177,7 +165,6 @@
       base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME_AND_NOW};
 
   sync_preferences::TestingPrefServiceSyncable prefs_;
-  FakeEncryptionGCMDriver fake_encryption_gcm_driver_;
   NiceMock<MockInstanceIDDriver> mock_instance_id_driver_;
   FakeLocalDeviceInfoProvider fake_local_device_info_provider_;
   FakeInstanceID fake_instance_id_;
@@ -245,8 +232,7 @@
   // Instance ID now returns a new token, however it shouldn't be invoked.
   SetInstanceIDFCMToken(kFCMToken2);
   // GCMDriver now returns new encryption info.
-  fake_encryption_gcm_driver_.SetEncryptionInfo(kDevicep256dh2,
-                                                kDeviceAuthSecret2);
+  fake_instance_id_.SetEncryptionInfo(kDevicep256dh2, kDeviceAuthSecret2);
 
   // Register device again without changing VAPID keys.
   RegisterDeviceSync();
@@ -329,6 +315,10 @@
   ASSERT_EQ(devices_.end(), devices_.find(guid));
   EXPECT_FALSE(fcm_registration_);
 
+  // Further unregister does nothing and returns kDeviceNotRegistered.
+  UnregisterDeviceSync();
+  EXPECT_EQ(SharingDeviceRegistrationResult::kDeviceNotRegistered, result_);
+
   // Register the device again, Instance.GetToken will be attempted once more,
   // which will return a different FCM token.
   SetInstanceIDFCMToken(kFCMToken2);
diff --git a/chrome/browser/sharing/sharing_fcm_sender.cc b/chrome/browser/sharing/sharing_fcm_sender.cc
index 42a41bc..a2f66d91 100644
--- a/chrome/browser/sharing/sharing_fcm_sender.cc
+++ b/chrome/browser/sharing/sharing_fcm_sender.cc
@@ -77,9 +77,16 @@
   web_push_message.urgency = gcm::WebPushMessage::Urgency::kHigh;
   message.SerializeToString(&web_push_message.payload);
 
+  auto fcm_registration = sync_preference_->GetFCMRegistration();
+  if (!fcm_registration) {
+    LOG(ERROR) << "Unable to retrieve FCM registration";
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
+
   gcm_driver_->SendWebPushMessage(
-      kSharingFCMAppID,
-      /* authorized_entity= */ std::string(), target.p256dh, target.auth_secret,
-      target.fcm_token, vapid_key_manager_->GetOrCreateKey(),
-      std::move(web_push_message), std::move(callback));
+      kSharingFCMAppID, fcm_registration->authorized_entity, target.p256dh,
+      target.auth_secret, target.fcm_token,
+      vapid_key_manager_->GetOrCreateKey(), std::move(web_push_message),
+      std::move(callback));
 }
diff --git a/chrome/browser/sharing/sharing_fcm_sender_unittest.cc b/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
index 9ab543e7..12fbcd51 100644
--- a/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
+++ b/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
@@ -32,6 +32,7 @@
 constexpr int kNoCapabilities =
     static_cast<int>(SharingDeviceCapability::kNone);
 const char kSenderGuid[] = "test_sender_guid";
+const char kAuthorizedEntity[] = "authorized_entity";
 const int kTtlSeconds = 10;
 
 class MockGCMDriver : public gcm::FakeGCMDriver {
@@ -140,6 +141,7 @@
 TEST_F(SharingFCMSenderTest, SendMessageToDevice) {
   std::string guid = base::GenerateGUID();
   sync_prefs_->SetSyncDevice(guid, CreateFakeSyncDevice());
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
 
   std::unique_ptr<crypto::ECPrivateKey> vapid_key =
       crypto::ECPrivateKey::Create();
@@ -153,9 +155,9 @@
 
   EXPECT_CALL(
       mock_gcm_driver_,
-      SendWebPushMessage(Eq(kSharingFCMAppID), Eq(""), Eq(kP256dh),
-                         Eq(kAuthSecret), Eq(kFcmToken), Eq(vapid_key.get()),
-                         WebPushMessageMatcher(), _));
+      SendWebPushMessage(Eq(kSharingFCMAppID), Eq(kAuthorizedEntity),
+                         Eq(kP256dh), Eq(kAuthSecret), Eq(kFcmToken),
+                         Eq(vapid_key.get()), WebPushMessageMatcher(), _));
 
   sharing_fcm_sender_->SendMessageToDevice(
       guid, base::TimeDelta::FromSeconds(kTtlSeconds), SharingMessage(),
@@ -166,6 +168,7 @@
 TEST_F(SharingFCMSenderTest, SendMessageBeforeLocalDeviceInfoReady) {
   std::string guid = base::GenerateGUID();
   sync_prefs_->SetSyncDevice(guid, CreateFakeSyncDevice());
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
 
   std::unique_ptr<crypto::ECPrivateKey> vapid_key =
       crypto::ECPrivateKey::Create();
@@ -189,9 +192,9 @@
 
   EXPECT_CALL(
       mock_gcm_driver_,
-      SendWebPushMessage(Eq(kSharingFCMAppID), Eq(""), Eq(kP256dh),
-                         Eq(kAuthSecret), Eq(kFcmToken), Eq(vapid_key.get()),
-                         WebPushMessageMatcher(), _));
+      SendWebPushMessage(Eq(kSharingFCMAppID), Eq(kAuthorizedEntity),
+                         Eq(kP256dh), Eq(kAuthSecret), Eq(kFcmToken),
+                         Eq(vapid_key.get()), WebPushMessageMatcher(), _));
 
   local_device_info_provider_.SetReady(true);
 }
diff --git a/chrome/browser/sharing/sharing_metrics.cc b/chrome/browser/sharing/sharing_metrics.cc
index c1657e2..c95490b 100644
--- a/chrome/browser/sharing/sharing_metrics.cc
+++ b/chrome/browser/sharing/sharing_metrics.cc
@@ -54,18 +54,33 @@
   base::UmaHistogramEnumeration("Sharing.VapidKeyCreationResult", result);
 }
 
-void LogClickToCallDevicesToShow(int count) {
+void LogClickToCallDevicesToShow(const char* histogram_suffix, int count) {
+  // Explicitly log both the base and the suffixed histogram because the base
+  // aggregation is not automatically generated.
   base::UmaHistogramExactLinear("Sharing.ClickToCallDevicesToShow", count,
                                 /*value_max=*/20);
+  base::UmaHistogramExactLinear(
+      base::StrCat({"Sharing.ClickToCallDevicesToShow.", histogram_suffix}),
+      count,
+      /*value_max=*/20);
 }
 
-void LogClickToCallAppsToShow(int count) {
+void LogClickToCallAppsToShow(const char* histogram_suffix, int count) {
+  // Explicitly log both the base and the suffixed histogram because the base
+  // aggregation is not automatically generated.
   base::UmaHistogramExactLinear("Sharing.ClickToCallAppsToShow", count,
                                 /*value_max=*/20);
+  base::UmaHistogramExactLinear(
+      base::StrCat({"Sharing.ClickToCallAppsToShow.", histogram_suffix}), count,
+      /*value_max=*/20);
 }
 
 void LogClickToCallSelectedDeviceIndex(const char* histogram_suffix,
                                        int index) {
+  // Explicitly log both the base and the suffixed histogram because the base
+  // aggregation is not automatically generated.
+  base::UmaHistogramExactLinear("Sharing.ClickToCallSelectedDeviceIndex", index,
+                                /*value_max=*/20);
   base::UmaHistogramExactLinear(
       base::StrCat(
           {"Sharing.ClickToCallSelectedDeviceIndex.", histogram_suffix}),
@@ -74,6 +89,10 @@
 }
 
 void LogClickToCallSelectedAppIndex(const char* histogram_suffix, int index) {
+  // Explicitly log both the base and the suffixed histogram because the base
+  // aggregation is not automatically generated.
+  base::UmaHistogramExactLinear("Sharing.ClickToCallSelectedAppIndex", index,
+                                /*value_max=*/20);
   base::UmaHistogramExactLinear(
       base::StrCat({"Sharing.ClickToCallSelectedAppIndex.", histogram_suffix}),
       index,
@@ -83,3 +102,7 @@
 void LogSharingMessageAckTime(base::TimeDelta time) {
   base::UmaHistogramMediumTimes("Sharing.MessageAckTime", time);
 }
+
+void LogClickToCallDialogShown(SharingClickToCallDialogType type) {
+  base::UmaHistogramEnumeration("Sharing.ClickToCallDialogShown", type);
+}
diff --git a/chrome/browser/sharing/sharing_metrics.h b/chrome/browser/sharing/sharing_metrics.h
index 208ced2..669fe72 100644
--- a/chrome/browser/sharing/sharing_metrics.h
+++ b/chrome/browser/sharing/sharing_metrics.h
@@ -22,6 +22,18 @@
   kMaxValue = kExportPrivateKeyFailed,
 };
 
+// The types of dialogs that can be shown for Click to Call.
+// These values are logged to UMA. Entries should not be renumbered and numeric
+// values should never be reused. Please keep in sync with
+// "SharingClickToCallDialogType" in src/tools/metrics/histograms/enums.xml.
+enum class SharingClickToCallDialogType {
+  kDialogWithDevicesMaybeApps = 0,
+  kDialogWithoutDevicesWithApp = 1,
+  kEducationalDialog = 2,
+  kErrorDialog = 3,
+  kMaxValue = kErrorDialog,
+};
+
 // These histogram suffixes must match the ones in SharingClickToCallUi defined
 // in histograms.xml.
 const char kSharingClickToCallUiContextMenu[] = "ContextMenu";
@@ -45,25 +57,34 @@
 void LogSharingVapidKeyCreationResult(SharingVapidKeyCreationResult result);
 
 // Logs the number of available devices that are about to be shown in a UI for
-// picking a device to start a phone call on.
-void LogClickToCallDevicesToShow(int count);
+// picking a device to start a phone call on. The |histogram_suffix| indicates
+// in which UI this event happened and must match one from SharingClickToCallUi
+// defined in histograms.xml - use the constants defined in this file for that.
+void LogClickToCallDevicesToShow(const char* histogram_suffix, int count);
 
 // Logs the number of available apps that are about to be shown in a UI for
-// picking an app to start a phone call with.
-void LogClickToCallAppsToShow(int count);
+// picking an app to start a phone call with. The |histogram_suffix| indicates
+// in which UI this event happened and must match one from SharingClickToCallUi
+// defined in histograms.xml - use the constants defined in this file for that.
+void LogClickToCallAppsToShow(const char* histogram_suffix, int count);
 
 // Logs the |index| of the device selected by the user for Click to Call. The
 // |histogram_suffix| indicates in which UI this event happened and must match
-// one from SharingClickToCallUi defined in histograms.xml.
+// one from SharingClickToCallUi defined in histograms.xml - use the constants
+// defined in this file for that.
 void LogClickToCallSelectedDeviceIndex(const char* histogram_suffix, int index);
 
 // Logs the |index| of the app selected by the user for Click to Call. The
 // |histogram_suffix| indicates in which UI this event happened and must match
-// one from SharingClickToCallUi defined in histograms.xml.
+// one from SharingClickToCallUi defined in histograms.xml - use the constants
+// defined in this file for that.
 void LogClickToCallSelectedAppIndex(const char* histogram_suffix, int index);
 
 // Logs to UMA the time from sending a FCM message from the Sharing service
 // until an ack message is received for it.
 void LogSharingMessageAckTime(base::TimeDelta time);
 
+// Logs to UMA the |type| of dialog shown for Click to Call.
+void LogClickToCallDialogShown(SharingClickToCallDialogType type);
+
 #endif  // CHROME_BROWSER_SHARING_SHARING_METRICS_H_
diff --git a/chrome/browser/sharing/sharing_service.cc b/chrome/browser/sharing/sharing_service.cc
index 4a02587..7c3b3ca 100644
--- a/chrome/browser/sharing/sharing_service.cc
+++ b/chrome/browser/sharing/sharing_service.cc
@@ -27,6 +27,8 @@
 #include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
+#include "components/gcm_driver/gcm_driver.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync_device_info/device_info.h"
 #include "components/sync_device_info/device_info_tracker.h"
@@ -39,6 +41,7 @@
     std::unique_ptr<SharingDeviceRegistration> sharing_device_registration,
     std::unique_ptr<SharingFCMSender> fcm_sender,
     std::unique_ptr<SharingFCMHandler> fcm_handler,
+    gcm::GCMDriver* gcm_driver,
     syncer::DeviceInfoTracker* device_info_tracker,
     syncer::LocalDeviceInfoProvider* local_device_info_provider,
     syncer::SyncService* sync_service)
@@ -52,6 +55,19 @@
       sync_service_(sync_service),
       backoff_entry_(&kRetryBackoffPolicy),
       state_(State::DISABLED) {
+  // Remove old encryption info with empty authrozed_entity to avoid DCHECK.
+  // See http://crbug/987591
+  if (gcm_driver) {
+    gcm::GCMEncryptionProvider* encryption_provider =
+        gcm_driver->GetEncryptionProviderInternal();
+    if (encryption_provider) {
+      encryption_provider->RemoveEncryptionInfo(
+          kSharingFCMAppID, /*authorized_entity=*/std::string(),
+          base::DoNothing());
+    }
+  }
+
+  // Initialize sharing handlers.
   fcm_handler_->AddSharingHandler(
       chrome_browser_sharing::SharingMessage::kAckMessage,
       &ack_message_handler_);
@@ -76,8 +92,7 @@
     sync_service_->AddObserver(this);
 
   // Only unregister if sync is disabled (not initializing).
-  if (sync_service_ && sync_service->GetTransportState() ==
-                           syncer::SyncService::TransportState::DISABLED) {
+  if (IsSyncDisabled()) {
     // state_ is kept as State::DISABLED as SharingService has never registered,
     // and only doing clean up via UnregisterDevice().
     UnregisterDevice();
@@ -239,7 +254,7 @@
         sync_service_->RemoveObserver(this);
       UnregisterDevice();
     }
-  } else if (state_ == State::ACTIVE) {
+  } else if (IsSyncDisabled() && state_ == State::ACTIVE) {
     state_ = State::UNREGISTERING;
     fcm_handler_->StopListening();
     sync_prefs_->ClearVapidKeyChangeObserver();
@@ -259,6 +274,7 @@
 
 void SharingService::OnDeviceRegistered(
     SharingDeviceRegistrationResult result) {
+  LogSharingRegistrationResult(result);
   switch (result) {
     case SharingDeviceRegistrationResult::kSuccess:
       backoff_entry_.InformOfRequest(true);
@@ -271,7 +287,7 @@
           // state_ is kept as State::ACTIVE during re-registration.
           sync_prefs_->SetVapidKeyChangeObserver(base::BindRepeating(
               &SharingService::RegisterDevice, weak_ptr_factory_.GetWeakPtr()));
-        } else {
+        } else if (IsSyncDisabled()) {
           // In case sync is disabled during registration, unregister it.
           state_ = State::UNREGISTERING;
           UnregisterDevice();
@@ -298,11 +314,15 @@
       // No need to bother retrying in the case of one of fatal errors.
       LOG(ERROR) << "Device registration failed with fatal error";
       break;
+    case SharingDeviceRegistrationResult::kDeviceNotRegistered:
+      // Register device cannot return kDeviceNotRegistered.
+      NOTREACHED();
   }
 }
 
 void SharingService::OnDeviceUnregistered(
     SharingDeviceRegistrationResult result) {
+  LogSharingUnegistrationResult(result);
   if (IsSyncEnabled() &&
       base::FeatureList::IsEnabled(kSharingDeviceRegistration)) {
     // In case sync is enabled during un-registration, register it.
@@ -312,9 +332,22 @@
     state_ = State::DISABLED;
   }
 
-  // Unregistration failure is ignored, and will be attempted in next restart.
-  if (result != SharingDeviceRegistrationResult::kSuccess)
-    LOG(ERROR) << "Device unregistration failed";
+  switch (result) {
+    case SharingDeviceRegistrationResult::kSuccess:
+      // Successfully unregistered, no-op
+      break;
+    case SharingDeviceRegistrationResult::kFcmTransientError:
+    case SharingDeviceRegistrationResult::kSyncServiceError:
+      LOG(ERROR) << "Device un-registration failed with transient error";
+      break;
+    case SharingDeviceRegistrationResult::kEncryptionError:
+    case SharingDeviceRegistrationResult::kFcmFatalError:
+      LOG(ERROR) << "Device un-registration failed with fatal error";
+      break;
+    case SharingDeviceRegistrationResult::kDeviceNotRegistered:
+      // Device has not been registered, no-op.
+      break;
+  }
 }
 
 bool SharingService::IsSyncEnabled() const {
@@ -323,3 +356,12 @@
              syncer::SyncService::TransportState::ACTIVE &&
          sync_service_->GetActiveDataTypes().Has(syncer::PREFERENCES);
 }
+
+bool SharingService::IsSyncDisabled() const {
+  return sync_service_ &&
+         (sync_service_->GetTransportState() ==
+              syncer::SyncService::TransportState::DISABLED ||
+          (sync_service_->GetTransportState() ==
+               syncer::SyncService::TransportState::ACTIVE &&
+           !sync_service_->GetActiveDataTypes().Has(syncer::PREFERENCES)));
+}
diff --git a/chrome/browser/sharing/sharing_service.h b/chrome/browser/sharing/sharing_service.h
index 5cc47ab7..fa0811be 100644
--- a/chrome/browser/sharing/sharing_service.h
+++ b/chrome/browser/sharing/sharing_service.h
@@ -26,6 +26,10 @@
 #include "chrome/browser/sharing/click_to_call/click_to_call_message_handler_android.h"
 #endif  // defined(OS_ANDROID)
 
+namespace gcm {
+class GCMDriver;
+}  // namespace gcm
+
 namespace syncer {
 class DeviceInfoTracker;
 class LocalDeviceInfoProvider;
@@ -65,6 +69,7 @@
       std::unique_ptr<SharingDeviceRegistration> sharing_device_registration,
       std::unique_ptr<SharingFCMSender> fcm_sender,
       std::unique_ptr<SharingFCMHandler> fcm_handler,
+      gcm::GCMDriver* gcm_driver,
       syncer::DeviceInfoTracker* device_info_tracker,
       syncer::LocalDeviceInfoProvider* local_device_info_provider,
       syncer::SyncService* sync_service);
@@ -110,9 +115,13 @@
                      base::Optional<std::string> message_id);
   void InvokeSendMessageCallback(const std::string& message_guid, bool result);
 
-  // Returns true if sync is active and sync preference is enabled.
+  // Returns true if required sync feature is enabled.
   bool IsSyncEnabled() const;
 
+  // Returns true if required sync feature is disabled. Returns false if sync is
+  // in transitioning state.
+  bool IsSyncDisabled() const;
+
   std::unique_ptr<SharingSyncPreference> sync_prefs_;
   std::unique_ptr<VapidKeyManager> vapid_key_manager_;
   std::unique_ptr<SharingDeviceRegistration> sharing_device_registration_;
diff --git a/chrome/browser/sharing/sharing_service_factory.cc b/chrome/browser/sharing/sharing_service_factory.cc
index d3b9e0e..b8b551e 100644
--- a/chrome/browser/sharing/sharing_service_factory.cc
+++ b/chrome/browser/sharing/sharing_service_factory.cc
@@ -83,7 +83,7 @@
   std::unique_ptr<SharingDeviceRegistration> sharing_device_registration =
       std::make_unique<SharingDeviceRegistration>(
           sync_prefs.get(), instance_id_service->driver(),
-          vapid_key_manager.get(), gcm_driver, local_device_info_provider);
+          vapid_key_manager.get(), local_device_info_provider);
   std::unique_ptr<SharingFCMSender> fcm_sender =
       std::make_unique<SharingFCMSender>(gcm_driver, local_device_info_provider,
                                          sync_prefs.get(),
@@ -94,8 +94,8 @@
   return new SharingService(std::move(sync_prefs), std::move(vapid_key_manager),
                             std::move(sharing_device_registration),
                             std::move(fcm_sender), std::move(fcm_handler),
-                            device_info_tracker, local_device_info_provider,
-                            sync_service);
+                            gcm_driver, device_info_tracker,
+                            local_device_info_provider, sync_service);
 }
 
 content::BrowserContext* SharingServiceFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/sharing/sharing_service_unittest.cc b/chrome/browser/sharing/sharing_service_unittest.cc
index 20b83ec6..a66fe70 100644
--- a/chrome/browser/sharing/sharing_service_unittest.cc
+++ b/chrome/browser/sharing/sharing_service_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/sharing/sharing_fcm_sender.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/sync/driver/test_sync_service.h"
@@ -46,6 +47,7 @@
 const char kFcmToken[] = "fcm_token";
 const char kDeviceName[] = "other_name";
 const char kMessageId[] = "message_id";
+const char kAuthorizedEntity[] = "authorized_entity";
 constexpr base::TimeDelta kTtl = base::TimeDelta::FromSeconds(10);
 
 class FakeGCMDriver : public gcm::FakeGCMDriver {
@@ -69,6 +71,10 @@
       std::move(callback).Run(base::make_optional(kMessageId));
   }
 
+  gcm::GCMEncryptionProvider* GetEncryptionProviderInternal() override {
+    return nullptr;
+  }
+
   void set_should_respond(bool should_respond) {
     should_respond_ = should_respond;
   }
@@ -127,12 +133,10 @@
       SharingSyncPreference* prefs,
       instance_id::InstanceIDDriver* instance_id_driver,
       VapidKeyManager* vapid_key_manager,
-      gcm::GCMDriver* gcm_driver,
       syncer::LocalDeviceInfoProvider* device_info_tracker)
       : SharingDeviceRegistration(prefs,
                                   instance_id_driver,
                                   vapid_key_manager,
-                                  gcm_driver,
                                   device_info_tracker) {}
   ~FakeSharingDeviceRegistration() override = default;
 
@@ -166,7 +170,7 @@
     sync_prefs_ = new SharingSyncPreference(&prefs_);
     sharing_device_registration_ = new FakeSharingDeviceRegistration(
         sync_prefs_, &mock_instance_id_driver_, vapid_key_manager_,
-        &fake_gcm_driver_, &fake_local_device_info_provider_);
+        &fake_local_device_info_provider_);
     vapid_key_manager_ = new VapidKeyManager(sync_prefs_);
     fcm_sender_ = new SharingFCMSender(&fake_gcm_driver_,
                                        &fake_local_device_info_provider_,
@@ -204,8 +208,8 @@
           base::WrapUnique(sync_prefs_), base::WrapUnique(vapid_key_manager_),
           base::WrapUnique(sharing_device_registration_),
           base::WrapUnique(fcm_sender_), base::WrapUnique(fcm_handler_),
-          &device_info_tracker_, &fake_local_device_info_provider_,
-          &test_sync_service_);
+          &fake_gcm_driver_, &device_info_tracker_,
+          &fake_local_device_info_provider_, &test_sync_service_);
     }
     return sharing_service_.get();
   }
@@ -344,6 +348,7 @@
       CreateFakeDeviceInfo(id, kDeviceName);
   device_info_tracker_.Add(device_info.get());
   sync_prefs_->SetSyncDevice(id, CreateFakeSyncDevice());
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
 
   GetSharingService()->SendMessageToDevice(
       id, kTtl, chrome_browser_sharing::SharingMessage(),
@@ -373,6 +378,7 @@
       CreateFakeDeviceInfo(id, kDeviceName);
   device_info_tracker_.Add(device_info.get());
   sync_prefs_->SetSyncDevice(id, CreateFakeSyncDevice());
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
 
   // FCM driver will not respond to the send request.
   fake_gcm_driver_.set_should_respond(false);
@@ -411,6 +417,7 @@
       CreateFakeDeviceInfo(id, kDeviceName);
   device_info_tracker_.Add(device_info.get());
   sync_prefs_->SetSyncDevice(id, CreateFakeSyncDevice());
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
 
   GetSharingService()->SendMessageToDevice(
       id, kTtl, chrome_browser_sharing::SharingMessage(),
@@ -547,12 +554,22 @@
   EXPECT_CALL(*fcm_handler_, StartListening()).Times(1);
   test_sync_service_.FireStateChanged();
   EXPECT_EQ(1, sharing_device_registration_->registration_attempts());
+  EXPECT_EQ(0, sharing_device_registration_->unregistration_attempts());
   EXPECT_EQ(SharingService::State::ACTIVE, GetSharingService()->GetState());
 
   // Further state changes do nothing.
   EXPECT_CALL(*fcm_handler_, StartListening()).Times(0);
   test_sync_service_.FireStateChanged();
   EXPECT_EQ(1, sharing_device_registration_->registration_attempts());
+  EXPECT_EQ(0, sharing_device_registration_->unregistration_attempts());
+  EXPECT_EQ(SharingService::State::ACTIVE, GetSharingService()->GetState());
+
+  // Change sync to configuring, which will be ignored.
+  test_sync_service_.SetTransportState(
+      syncer::SyncService::TransportState::CONFIGURING);
+  test_sync_service_.FireStateChanged();
+  EXPECT_EQ(1, sharing_device_registration_->registration_attempts());
+  EXPECT_EQ(0, sharing_device_registration_->unregistration_attempts());
   EXPECT_EQ(SharingService::State::ACTIVE, GetSharingService()->GetState());
 
   // Disable sync and un-registration should happen.
@@ -560,12 +577,14 @@
       syncer::SyncService::TransportState::DISABLED);
   EXPECT_CALL(*fcm_handler_, StopListening()).Times(1);
   test_sync_service_.FireStateChanged();
+  EXPECT_EQ(1, sharing_device_registration_->registration_attempts());
   EXPECT_EQ(1, sharing_device_registration_->unregistration_attempts());
   EXPECT_EQ(SharingService::State::DISABLED, GetSharingService()->GetState());
 
   // Further state changes do nothing.
   EXPECT_CALL(*fcm_handler_, StopListening()).Times(0);
   test_sync_service_.FireStateChanged();
+  EXPECT_EQ(1, sharing_device_registration_->registration_attempts());
   EXPECT_EQ(1, sharing_device_registration_->unregistration_attempts());
   EXPECT_EQ(SharingService::State::DISABLED, GetSharingService()->GetState());
 
@@ -575,7 +594,16 @@
   EXPECT_CALL(*fcm_handler_, StartListening()).Times(1);
   test_sync_service_.FireStateChanged();
   EXPECT_EQ(2, sharing_device_registration_->registration_attempts());
+  EXPECT_EQ(1, sharing_device_registration_->unregistration_attempts());
   EXPECT_EQ(SharingService::State::ACTIVE, GetSharingService()->GetState());
+
+  // Disable syncing of preference and un-registration should happen.
+  test_sync_service_.SetActiveDataTypes(syncer::ModelTypeSet());
+  EXPECT_CALL(*fcm_handler_, StopListening()).Times(1);
+  test_sync_service_.FireStateChanged();
+  EXPECT_EQ(2, sharing_device_registration_->registration_attempts());
+  EXPECT_EQ(2, sharing_device_registration_->unregistration_attempts());
+  EXPECT_EQ(SharingService::State::DISABLED, GetSharingService()->GetState());
 }
 
 TEST_F(SharingServiceTest, StartListeningToFCMAtConstructor) {
@@ -586,8 +614,7 @@
 
   // Create new SharingService instance with FCM already registered at
   // constructor.
-  sync_prefs_->SetFCMRegistration(
-      {"authorized_entity", "fcm_registration_token", base::Time::Now()});
+  sync_prefs_->SetFCMRegistration({kAuthorizedEntity, "", base::Time::Now()});
   EXPECT_CALL(*fcm_handler_, StartListening()).Times(1);
   GetSharingService();
 }
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
index 60b10bd..707f2a9a 100644
--- a/chrome/browser/shell_integration_linux.cc
+++ b/chrome/browser/shell_integration_linux.cc
@@ -252,7 +252,7 @@
 #if BUILDFLAG(ENABLE_APP_LIST)
 #if defined(GOOGLE_CHROME_BUILD)
 const char kAppListDesktopName[] = "chrome-app-list";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
 const char kAppListDesktopName[] = "chromium-app-list";
 #endif
 #endif
@@ -426,7 +426,7 @@
     default:
       return "google-chrome.desktop";
   }
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   // Allow $CHROME_DESKTOP to override the built-in value, so that development
   // versions can set themselves as the default without interfering with
   // non-official, packaged versions using the built-in value.
@@ -440,7 +440,7 @@
 std::string GetIconName() {
 #if defined(GOOGLE_CHROME_BUILD)
   return "google-chrome";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   return "chromium-browser";
 #endif
 }
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index 24833967..46bf598 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -493,6 +493,16 @@
   delete this;
 }
 
+void MigrateChromeAndChromeProxyShortcuts(
+    const base::FilePath& chrome_exe,
+    const base::FilePath& chrome_proxy_path,
+    const base::FilePath& shortcut_path) {
+  win::MigrateShortcutsInPathInternal(chrome_exe, shortcut_path);
+
+  // Migrate any pinned PWA shortcuts in taskbar directory.
+  win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path);
+}
+
 }  // namespace
 
 bool SetAsDefaultBrowser() {
@@ -701,28 +711,44 @@
   // This needs to happen (e.g. so that the appid is fixed and the
   // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
   // urgent task.
-  base::FilePath pins_path;
-  if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
+  base::FilePath taskbar_path;
+  if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path)) {
+    NOTREACHED();
+    return;
+  }
+
+  // Migrate any pinned shortcuts in ImplicitApps sub-directories.
+  base::FilePath implicit_apps_path;
+  if (!base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
+                              &implicit_apps_path)) {
     NOTREACHED();
     return;
   }
 
   base::CreateCOMSTATaskRunnerWithTraits(
       {base::MayBlock(), base::TaskPriority::BEST_EFFORT})
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&MigrateTaskbarPinsCallback, pins_path));
+      ->PostTask(FROM_HERE, base::BindOnce(&MigrateTaskbarPinsCallback,
+                                           taskbar_path, implicit_apps_path));
 }
 
-void MigrateTaskbarPinsCallback(const base::FilePath& pins_path) {
+void MigrateTaskbarPinsCallback(const base::FilePath& taskbar_path,
+                                const base::FilePath& implicit_apps_path) {
   // Get full path of chrome.
   base::FilePath chrome_exe;
   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
     return;
+  base::FilePath chrome_proxy_path(web_app::GetChromeProxyPath());
 
-  win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
-
-  // Migrate any pinned PWA shortcuts.
-  win::MigrateShortcutsInPathInternal(web_app::GetChromeProxyPath(), pins_path);
+  MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
+                                       taskbar_path);
+  base::FileEnumerator directory_enum(implicit_apps_path, /*recursive=*/false,
+                                      base::FileEnumerator::DIRECTORIES);
+  for (base::FilePath implicit_app_sub_directory = directory_enum.Next();
+       !implicit_app_sub_directory.empty();
+       implicit_app_sub_directory = directory_enum.Next()) {
+    MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
+                                         implicit_app_sub_directory);
+  }
 }
 
 void GetIsPinnedToTaskbarState(
diff --git a/chrome/browser/shell_integration_win.h b/chrome/browser/shell_integration_win.h
index 3eb29a0..cc6d224 100644
--- a/chrome/browser/shell_integration_win.h
+++ b/chrome/browser/shell_integration_win.h
@@ -74,7 +74,8 @@
 void MigrateTaskbarPins();
 
 // Callback for MigrateTaskbarPins(). Exposed for testing.
-void MigrateTaskbarPinsCallback(const base::FilePath& pins_path);
+void MigrateTaskbarPinsCallback(const base::FilePath& pins_path,
+                                const base::FilePath& implicit_apps_path);
 
 // Migrates all shortcuts in |path| which point to |chrome_exe| such that they
 // have the appropriate AppUserModelId. Also clears the legacy dual_mode
diff --git a/chrome/browser/shell_integration_win_unittest.cc b/chrome/browser/shell_integration_win_unittest.cc
index df47071..9409383 100644
--- a/chrome/browser/shell_integration_win_unittest.cc
+++ b/chrome/browser/shell_integration_win_unittest.cc
@@ -43,7 +43,8 @@
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-
+    ASSERT_TRUE(
+        temp_dir_sub_dir_.CreateUniqueTempDirUnderPath(temp_dir_.GetPath()));
     // A path to a random target.
     base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &other_target_);
 
@@ -83,9 +84,10 @@
   // |shortcut_properties| after copying it to an internal structure for later
   // verification.
   void AddTestShortcutAndResetProperties(
+      const base::FilePath& shortcut_dir,
       base::win::ShortcutProperties* shortcut_properties) {
     ShortcutTestObject shortcut_test_object;
-    base::FilePath shortcut_path = temp_dir_.GetPath().Append(
+    base::FilePath shortcut_path = shortcut_dir.Append(
         L"Shortcut " + base::NumberToString16(shortcuts_.size()) +
         installer::kLnkExt);
     shortcut_test_object.path = shortcut_path;
@@ -105,22 +107,22 @@
     // Shortcut 0 doesn't point to chrome.exe and thus should never be migrated.
     temp_properties.set_target(other_target_);
     temp_properties.set_app_id(L"Dumbo");
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 1 points to chrome.exe and thus should be migrated.
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(L"Dumbo");
     temp_properties.set_dual_mode(false);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 2 points to chrome.exe, but already has the right appid and thus
     // should only be migrated if dual_mode is desired.
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(default_profile_chrome_app_id_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 3 is like shortcut 1, but it's appid is a prefix of the expected
     // appid instead of being totally different.
@@ -128,8 +130,8 @@
     chrome_app_id_is_prefix.push_back(L'1');
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(chrome_app_id_is_prefix);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 4 is like shortcut 1, but it's appid is of the same size as the
     // expected appid.
@@ -137,14 +139,14 @@
         default_profile_chrome_app_id_.size(), L'1');
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(same_size_as_chrome_app_id);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 5 doesn't have an app_id, nor is dual_mode even set; they should
     // be set as expected upon migration.
     temp_properties.set_target(chrome_exe_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 6 has a non-default profile directory and so should get a non-
     // default app id.
@@ -152,8 +154,8 @@
     temp_properties.set_app_id(L"Dumbo");
     temp_properties.set_arguments(
         L"--profile-directory=" + non_default_profile_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 7 has a non-default user data directory and so should get a non-
     // default app id.
@@ -161,8 +163,8 @@
     temp_properties.set_app_id(L"Dumbo");
     temp_properties.set_arguments(
         L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\"");
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 8 has a non-default user data directory as well as a non-default
     // profile directory and so should get a non-default app id.
@@ -171,8 +173,8 @@
     temp_properties.set_arguments(
         L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\" " +
         L"--profile-directory=" + non_default_profile_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 9 is a shortcut to an app and should get an app id for that app
     // rather than the chrome app id.
@@ -180,8 +182,8 @@
     temp_properties.set_app_id(L"Dumbo");
     temp_properties.set_arguments(
         L"--app-id=" + extension_id_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 10 is a shortcut to an app with a non-default profile and should
     // get an app id for that app with a non-default app id rather than the
@@ -191,8 +193,8 @@
     temp_properties.set_arguments(
         L"--app-id=" + extension_id_ +
         L" --profile-directory=" + non_default_profile_);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 11 points to chrome.exe, has the chrome appid, and has
     // dual_mode set and thus should be migrated to the
@@ -200,21 +202,24 @@
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(chrome_app_id_);
     temp_properties.set_dual_mode(true);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
 
     // Shortcut 12 is similar to 11 but with dual_mode explicitly set to false.
     temp_properties.set_target(chrome_exe_);
     temp_properties.set_app_id(chrome_app_id_);
     temp_properties.set_dual_mode(false);
-    ASSERT_NO_FATAL_FAILURE(
-        AddTestShortcutAndResetProperties(&temp_properties));
+    ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+        temp_dir_.GetPath(), &temp_properties));
   }
 
   base::win::ScopedCOMInitializer com_initializer_;
 
   base::ScopedTempDir temp_dir_;
 
+  // Used to test migration of shortcuts in ImplicitApps sub-directories.
+  base::ScopedTempDir temp_dir_sub_dir_;
+
   // Test shortcuts.
   std::vector<ShortcutTestObject> shortcuts_;
 
@@ -300,8 +305,9 @@
             MigrateShortcutsInPathInternal(chrome_exe_, temp_dir_.GetPath()));
 }
 
-// Test that a chrome_proxy.exe shortcut (PWA) has its app_id migrated
-// to include the default profile name.
+// Test that chrome_proxy.exe shortcuts (PWA) have their app_id migrated
+// to include the default profile name. This tests both shortcuts in the
+// DIR_TASKBAR_PINS and sub-directories of DIR_IMPLICIT_APP_SHORTCUTS.
 TEST_F(ShellIntegrationWinMigrateShortcutTest, MigrateChromeProxyTest) {
   // Create shortcut to chrome_proxy_exe in executable directory,
   // using the default profile, with the AppModelId not containing the
@@ -310,12 +316,22 @@
   base::win::ShortcutProperties temp_properties;
   temp_properties.set_target(web_app::GetChromeProxyPath());
   temp_properties.set_app_id(L"Dumbo");
-  ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(&temp_properties));
+  ASSERT_NO_FATAL_FAILURE(
+      AddTestShortcutAndResetProperties(temp_dir_.GetPath(), &temp_properties));
+  temp_properties.set_target(web_app::GetChromeProxyPath());
+  temp_properties.set_app_id(L"Dumbo2");
+  ASSERT_NO_FATAL_FAILURE(AddTestShortcutAndResetProperties(
+      temp_dir_sub_dir_.GetPath(), &temp_properties));
 
-  MigrateTaskbarPinsCallback(temp_dir_.GetPath());
-  // Verify that the migrated shortcut now contains the default profile name.
+  MigrateTaskbarPinsCallback(temp_dir_.GetPath(), temp_dir_.GetPath());
+  // Verify that the migrated shortcut in temp_dir_ now contains the default
+  // profile name.
   shortcuts_[0].properties.set_app_id(default_profile_chrome_app_id_);
   base::win::ValidateShortcut(shortcuts_[0].path, shortcuts_[0].properties);
+  // Verify that the migrated shortcut in temp_dir_sub now contains the default
+  // profile name.
+  shortcuts_[1].properties.set_app_id(default_profile_chrome_app_id_);
+  base::win::ValidateShortcut(shortcuts_[1].path, shortcuts_[1].properties);
 }
 
 TEST(ShellIntegrationWinTest, GetAppModelIdForProfileTest) {
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index e4e082b..515b7a3 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -389,13 +389,11 @@
       kManageAccountsHeaderReceivedUserDataKey,
       std::make_unique<ManageAccountsHeaderReceivedUserData>());
 
-  if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    ProcessMirrorHeaderUIThread(params, response->GetWebContentsGetter());
-  } else {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                             base::BindOnce(ProcessMirrorHeaderUIThread, params,
-                                            response->GetWebContentsGetter()));
-  }
+  // Post a task even if we are already on the UI thread to avoid making any
+  // requests while processing a throttle event.
+  base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
+                           base::BindOnce(ProcessMirrorHeaderUIThread, params,
+                                          response->GetWebContentsGetter()));
 }
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -428,15 +426,12 @@
   if (params.user_intention == DiceAction::NONE)
     return;
 
-  if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    ProcessDiceHeaderUIThread(std::move(params),
-                              response->GetWebContentsGetter());
-  } else {
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(ProcessDiceHeaderUIThread, std::move(params),
-                       response->GetWebContentsGetter()));
-  }
+  // Post a task even if we are already on the UI thread to avoid making any
+  // requests while processing a throttle event.
+  base::PostTaskWithTraits(
+      FROM_HERE, {content::BrowserThread::UI},
+      base::BindOnce(ProcessDiceHeaderUIThread, std::move(params),
+                     response->GetWebContentsGetter()));
 }
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index a8a8cbb..c0baaa7 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -2761,14 +2761,12 @@
     // broken in the good case.
     std::string replacement_path = GetFilePathWithHostAndPortReplacement(
         "/ssl/page_with_unsafe_contents.html", https_server_.host_port_pair());
-    ui_test_utils::BrowserAddedObserver popup_observer;
     ui_test_utils::NavigateToURL(browser(),
                                  https_server_.GetURL(replacement_path));
     WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
     // The state is expected to be authenticated.
     CheckAuthenticatedState(tab, AuthState::NONE);
     // The iframe should be able to open a popup.
-    popup_observer.WaitForSingleNewBrowser();
     EXPECT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
     // In order to check that the image was loaded, check its width.
     // The actual image (Google logo) is 276 pixels wide.
@@ -3059,15 +3057,12 @@
       "/ssl/page_with_unsafe_popup.html",
       https_server_expired_.host_port_pair());
   WebContents* tab1 = browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::BrowserAddedObserver popup_observer;
   content::TestNavigationObserver nav_observer(
       https_server_expired_.GetURL("/ssl/bad_iframe.html"));
   nav_observer.StartWatchingNewWebContents();
   ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL(replacement_path));
-  // Wait for popup window to appear and finish navigating.
-  popup_observer.WaitForSingleNewBrowser();
   ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
 
   // Last activated browser should be the popup.
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 961cecf..1bb4c3e 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -612,7 +612,11 @@
 }
 
 syncer::SyncTypePreferenceProvider* ChromeSyncClient::GetPreferenceProvider() {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   return SupervisedUserServiceFactory::GetForProfile(profile_);
+#else
+  return nullptr;
+#endif
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/test/integration/apps_helper.cc b/chrome/browser/sync/test/integration/apps_helper.cc
index ab95d85b..cbe212e6 100644
--- a/chrome/browser/sync/test/integration/apps_helper.cc
+++ b/chrome/browser/sync/test/integration/apps_helper.cc
@@ -7,7 +7,8 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/sync_app_helper.h"
@@ -118,7 +119,7 @@
   if (!base::FeatureList::IsEnabled(features::kAppServiceAsh))
     return;
 
-  apps::AppServiceProxyImpl::GetImplForTesting(profile)
+  apps::AppServiceProxyFactory::GetForProfile(profile)
       ->FlushMojoCallsForTesting();
 }
 
diff --git a/chrome/browser/tracing/chrome_tracing_delegate.cc b/chrome/browser/tracing/chrome_tracing_delegate.cc
index a0b3724..60d81ff 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate.cc
+++ b/chrome/browser/tracing/chrome_tracing_delegate.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -43,6 +44,25 @@
 
 const int kMinDaysUntilNextUpload = 7;
 
+// These values are logged to UMA. Entries should not be renumbered and numeric
+// values should never be reused. Please keep in sync with
+// "TracingFinalizationDisallowedReason" in
+// src/tools/metrics/histograms/enums.xml.
+enum class TracingFinalizationDisallowedReason {
+  kIncognitoLaunched = 0,
+  kProfileNotLoaded = 1,
+  kCrashMetricsNotLoaded = 2,
+  kLastSessionCrashed = 3,
+  kMetricsReportingDisabled = 4,
+  kTraceUploadedRecently = 5,
+  kMaxValue = kTraceUploadedRecently
+};
+
+void RecordDisallowedMetric(TracingFinalizationDisallowedReason reason) {
+  UMA_HISTOGRAM_ENUMERATION("Tracing.Background.FinalizationDisallowedReason",
+                            reason);
+}
+
 }  // namespace
 
 void ChromeTracingDelegate::RegisterPrefs(PrefRegistrySimple* registry) {
@@ -118,30 +138,49 @@
   // If the profile hasn't loaded or been created yet, we allow the scenario
   // to start up, but not be finalized.
   Profile* profile = GetProfile();
-  if (!profile)
+  if (!profile) {
+    if (profile_permission == PROFILE_REQUIRED) {
+      RecordDisallowedMetric(
+          TracingFinalizationDisallowedReason::kProfileNotLoaded);
+    }
     return profile_permission != PROFILE_REQUIRED;
+  }
 
 // Safeguard, in case background tracing is responsible for a crash on
 // startup.
 #if !defined(OS_ANDROID)
-  if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED)
+  if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
+    RecordDisallowedMetric(
+        TracingFinalizationDisallowedReason::kLastSessionCrashed);
     return false;
+  }
 #else
   // If the metrics haven't loaded, we allow the scenario to start up, but not
   // be finalized.
-  if (!CrashUploadListAndroid::BrowserCrashMetricsInitialized())
+  if (!CrashUploadListAndroid::BrowserCrashMetricsInitialized()) {
+    if (profile_permission == PROFILE_REQUIRED) {
+      RecordDisallowedMetric(
+          TracingFinalizationDisallowedReason::kCrashMetricsNotLoaded);
+    }
     return profile_permission != PROFILE_REQUIRED;
+  }
 
-  if (CrashUploadListAndroid::DidBrowserCrashRecently())
+  if (CrashUploadListAndroid::DidBrowserCrashRecently()) {
+    RecordDisallowedMetric(
+        TracingFinalizationDisallowedReason::kLastSessionCrashed);
     return false;
+  }
 #endif
 
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
 
 #if !defined(OS_CHROMEOS) && defined(OFFICIAL_BUILD)
-  if (!local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled))
+  if (!local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled)) {
+    RecordDisallowedMetric(
+        TracingFinalizationDisallowedReason::kMetricsReportingDisabled);
     return false;
+  }
 #endif // !OS_CHROMEOS && OFFICIAL_BUILD
 
   if (config.tracing_mode() == content::BackgroundTracingConfig::PREEMPTIVE) {
@@ -151,6 +190,8 @@
       base::Time computed_next_allowed_time =
           last_upload_time + base::TimeDelta::FromDays(kMinDaysUntilNextUpload);
       if (computed_next_allowed_time > base::Time::Now()) {
+        RecordDisallowedMetric(
+            TracingFinalizationDisallowedReason::kTraceUploadedRecently);
         return false;
       }
     }
@@ -178,6 +219,8 @@
     bool requires_anonymized_data) {
   if (requires_anonymized_data &&
       (incognito_launched_ || chrome::IsIncognitoSessionActive())) {
+    RecordDisallowedMetric(
+        TracingFinalizationDisallowedReason::kIncognitoLaunched);
     return false;
   }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f13b00b..986350d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -305,10 +305,6 @@
     "webui/quota_internals/quota_internals_ui.h",
     "webui/signin_internals_ui.cc",
     "webui/signin_internals_ui.h",
-    "webui/supervised_user_internals_message_handler.cc",
-    "webui/supervised_user_internals_message_handler.h",
-    "webui/supervised_user_internals_ui.cc",
-    "webui/supervised_user_internals_ui.h",
     "webui/sync_internals_message_handler.cc",
     "webui/sync_internals_message_handler.h",
     "webui/sync_internals_ui.cc",
@@ -1347,7 +1343,6 @@
       "//chrome/browser/ui/webui/app_management:mojo_bindings",
       "//chrome/common:buildflags",
       "//chrome/common:search_mojom",
-      "//chrome/services/app_service/public/cpp:app_service_proxy",
       "//chrome/services/app_service/public/cpp:app_update",
       "//chrome/services/app_service/public/mojom",
       "//components/feedback/proto",
@@ -1393,6 +1388,7 @@
     }
   }
 
+  # TODO(crbug.com/980869): Remove the section below.
   if (enable_supervised_users && !is_android && !is_chromeos) {
     sources += [
       "startup/supervised_users_deprecated_infobar_delegate.cc",
@@ -1400,6 +1396,15 @@
     ]
   }
 
+  if (enable_supervised_users) {
+    sources += [
+      "webui/supervised_user_internals_message_handler.cc",
+      "webui/supervised_user_internals_message_handler.h",
+      "webui/supervised_user_internals_ui.cc",
+      "webui/supervised_user_internals_ui.h",
+    ]
+  }
+
   if (is_chromeos) {
     assert(enable_extensions)
     assert(toolkit_views)
@@ -2046,22 +2051,22 @@
       "webui/signin/user_manager_screen_handler.h",
       "webui/signin/user_manager_ui.cc",
       "webui/signin/user_manager_ui.h",
-      "webui/welcome/nux/bookmark_handler.cc",
-      "webui/welcome/nux/bookmark_handler.h",
-      "webui/welcome/nux/bookmark_item.cc",
-      "webui/welcome/nux/bookmark_item.h",
-      "webui/welcome/nux/constants.cc",
-      "webui/welcome/nux/constants.h",
-      "webui/welcome/nux/google_apps_handler.cc",
-      "webui/welcome/nux/google_apps_handler.h",
-      "webui/welcome/nux/ntp_background_fetcher.cc",
-      "webui/welcome/nux/ntp_background_fetcher.h",
-      "webui/welcome/nux/ntp_background_handler.cc",
-      "webui/welcome/nux/ntp_background_handler.h",
-      "webui/welcome/nux/set_as_default_handler.cc",
-      "webui/welcome/nux/set_as_default_handler.h",
+      "webui/welcome/bookmark_handler.cc",
+      "webui/welcome/bookmark_handler.h",
+      "webui/welcome/bookmark_item.cc",
+      "webui/welcome/bookmark_item.h",
+      "webui/welcome/constants.cc",
+      "webui/welcome/constants.h",
+      "webui/welcome/google_apps_handler.cc",
+      "webui/welcome/google_apps_handler.h",
+      "webui/welcome/ntp_background_fetcher.cc",
+      "webui/welcome/ntp_background_fetcher.h",
+      "webui/welcome/ntp_background_handler.cc",
+      "webui/welcome/ntp_background_handler.h",
       "webui/welcome/nux_helper.cc",
       "webui/welcome/nux_helper.h",
+      "webui/welcome/set_as_default_handler.cc",
+      "webui/welcome/set_as_default_handler.h",
       "webui/welcome/welcome_handler.cc",
       "webui/welcome/welcome_handler.h",
       "webui/welcome/welcome_ui.cc",
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index f099320..24f5929 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -380,6 +380,9 @@
 
   search_controller_ =
       app_list::CreateSearchController(profile_, current_model_updater_, this);
+  search_ranking_event_logger_ =
+      std::make_unique<app_list::SearchRankingEventLogger>(
+          profile_, search_controller_.get());
 }
 
 app_list::SearchController* AppListClientImpl::search_controller() {
@@ -525,7 +528,7 @@
     const base::string16& trimmed_query,
     const ash::SearchResultIdWithPositionIndices& results,
     int position_index) {
-  search_ranking_event_logger_.Log(trimmed_query, results, position_index);
+  search_ranking_event_logger_->Log(trimmed_query, results, position_index);
 }
 
 ash::ShelfLaunchSource AppListClientImpl::AppListSourceToLaunchSource(
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.h b/chrome/browser/ui/app_list/app_list_client_impl.h
index bd2d157..5eddefc 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.h
+++ b/chrome/browser/ui/app_list/app_list_client_impl.h
@@ -191,7 +191,8 @@
   bool app_list_target_visibility_ = false;
   bool app_list_visible_ = false;
 
-  app_list::SearchRankingEventLogger search_ranking_event_logger_;
+  std::unique_ptr<app_list::SearchRankingEventLogger>
+      search_ranking_event_logger_;
 
   base::WeakPtrFactory<AppListClientImpl> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index bbc0a19f..643588512 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -16,6 +16,7 @@
 #include "base/stl_util.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
@@ -42,7 +43,6 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/sync/model/sync_change_processor.h"
 #include "components/sync/model/sync_data.h"
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc
index 5332eda..25af145 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -6,13 +6,13 @@
 
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "base/bind.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_context_menu.h"
 #include "chrome/browser/ui/app_list/crostini/crostini_app_context_menu.h"
 #include "chrome/browser/ui/app_list/extension_app_context_menu.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 
 // static
 const char AppServiceAppItem::kItemType[] = "AppServiceAppItem";
diff --git a/chrome/browser/ui/app_list/app_service_app_model_builder.cc b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
index 3eb20e5..106cb16 100644
--- a/chrome/browser/ui/app_list/app_service_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/ui/app_list/app_service_app_model_builder.h"
 
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/ui/app_list/app_service_app_item.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "ui/base/l10n/l10n_util.h"
 
 // static
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
index 6801b3a..f128d81a 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/run_loop.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index fc6f36a..3c1f11e2 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -30,6 +30,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
@@ -60,7 +61,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync_sessions/session_sync_service.h"
 #include "extensions/browser/extension_prefs.h"
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 125397eb..9c890ba14 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -7,11 +7,11 @@
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/bind.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_service_app_item.h"
 #include "chrome/browser/ui/app_list/search/internal_app_result.h"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "extensions/common/extension.h"
 
 namespace app_list {
diff --git a/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.cc b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.cc
index 1475d34..dfee341 100644
--- a/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.cc
+++ b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.cc
@@ -2,33 +2,309 @@
 // 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 <vector>
-
-#include "base/logging.h"
 #include "chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.h"
 
-namespace app_list {
+#include <cmath>
 
-SearchRankingEventLogger::SearchRankingEventLogger() {}
+#include "ash/public/cpp/app_list/app_list_types.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/search/omnibox_result.h"
+#include "chromeos/constants/devicetype.h"
+#include "components/omnibox/browser/autocomplete_match_type.h"
+#include "services/metrics/public/cpp/metrics_utils.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace app_list {
+namespace {
+
+using ukm::GetExponentialBucketMinForCounts1000;
+
+// How long to wait for a URL to enter the history service before querying it
+// for a UKM source ID.
+constexpr base::TimeDelta kDelayForHistoryService =
+    base::TimeDelta::FromSeconds(15);
+
+// Chosen so that the bucket at the 24 hour mark is ~60 minutes long. The bucket
+// exponent used for counts that are not seconds is 1.15 (via
+// ukm::GetExponentialBucketMinForCounts1000). The first value skipped by
+// bucketing is 10.
+constexpr float kBucketExponentForSeconds = 1.045;
+
+// Represents the type of a search result. The indices of these values
+// persist to logs, so existing values should not be modified.
+enum class Category {
+  UNKNOWN = 0,
+  FILE = 1,
+  HISTORY = 2,
+  NAV_SUGGEST = 3,
+  SEARCH = 4,
+  BOOKMARK = 5,
+  DOCUMENT = 6,
+  OMNIBOX_DEPRECATED = 7,
+  OMNIBOX_GENERIC = 8
+};
+
+int ExtensionTypeFromFileName(const std::string& file_name) {
+  // This is a limited list of commonly used extensions. The index of an
+  // extension in this list persists to logs, so existing values should not be
+  // modified and new values should only be added to the end. This should be
+  // kept in sync with AppListNonAppImpressionFileExtension in
+  // histograms/enums.xml
+  static const base::NoDestructor<std::vector<std::string>> known_extensions(
+      {".3ga",        ".3gp",    ".aac",     ".alac", ".asf",  ".avi",
+       ".bmp",        ".csv",    ".doc",     ".docx", ".flac", ".gif",
+       ".jpeg",       ".jpg",    ".log",     ".m3u",  ".m3u8", ".m4a",
+       ".m4v",        ".mid",    ".mkv",     ".mov",  ".mp3",  ".mp4",
+       ".mpg",        ".odf",    ".odp",     ".ods",  ".odt",  ".oga",
+       ".ogg",        ".ogv",    ".pdf",     ".png",  ".ppt",  ".pptx",
+       ".ra",         ".ram",    ".rar",     ".rm",   ".rtf",  ".wav",
+       ".webm",       ".webp",   ".wma",     ".wmv",  ".xls",  ".xlsx",
+       ".crdownload", ".crx",    ".dmg",     ".exe",  ".html", ".htm",
+       ".jar",        ".ps",     ".torrent", ".txt",  ".zip",  ".mhtml",
+       ".gdoc",       ".gsheet", ".gslides"});
+
+  size_t found = file_name.find_last_of(".");
+  if (found == std::string::npos)
+    return -1;
+  return std::distance(
+      known_extensions->begin(),
+      std::find(known_extensions->begin(), known_extensions->end(),
+                file_name.substr(found)));
+}
+
+Category CategoryFromResultType(ash::SearchResultType type, int subtype) {
+  if (type == ash::SearchResultType::kLauncher)
+    return Category::FILE;
+
+  if (type == ash::SearchResultType::kOmnibox) {
+    switch (static_cast<AutocompleteMatchType::Type>(subtype)) {
+      case AutocompleteMatchType::Type::HISTORY_URL:
+      case AutocompleteMatchType::Type::HISTORY_TITLE:
+      case AutocompleteMatchType::Type::HISTORY_BODY:
+      case AutocompleteMatchType::Type::HISTORY_KEYWORD:
+        return Category::HISTORY;
+      case AutocompleteMatchType::Type::NAVSUGGEST:
+      case AutocompleteMatchType::Type::NAVSUGGEST_PERSONALIZED:
+        return Category::NAV_SUGGEST;
+      case AutocompleteMatchType::Type::SEARCH_HISTORY:
+      case AutocompleteMatchType::Type::SEARCH_SUGGEST:
+      case AutocompleteMatchType::Type::SEARCH_SUGGEST_ENTITY:
+      case AutocompleteMatchType::Type::SEARCH_SUGGEST_TAIL:
+      case AutocompleteMatchType::Type::SEARCH_SUGGEST_PERSONALIZED:
+      case AutocompleteMatchType::Type::SEARCH_SUGGEST_PROFILE:
+      case AutocompleteMatchType::Type::SEARCH_OTHER_ENGINE:
+        return Category::SEARCH;
+      case AutocompleteMatchType::Type::BOOKMARK_TITLE:
+        return Category::BOOKMARK;
+      case AutocompleteMatchType::Type::DOCUMENT_SUGGESTION:
+        return Category::DOCUMENT;
+      case AutocompleteMatchType::Type::EXTENSION_APP_DEPRECATED:
+      case AutocompleteMatchType::Type::CONTACT_DEPRECATED:
+      case AutocompleteMatchType::Type::PHYSICAL_WEB_DEPRECATED:
+      case AutocompleteMatchType::Type::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
+      case AutocompleteMatchType::Type::TAB_SEARCH_DEPRECATED:
+        return Category::OMNIBOX_DEPRECATED;
+      default:
+        return Category::OMNIBOX_GENERIC;
+    }
+  }
+
+  return Category::UNKNOWN;
+}
+
+int GetExponentialBucketMinForSeconds(int64_t sample) {
+  return ukm::GetExponentialBucketMin(sample, kBucketExponentForSeconds);
+}
+
+}  // namespace
+
+SearchRankingEventLogger::SearchRankingEventLogger(
+    Profile* profile,
+    SearchController* search_controller)
+    : search_controller_(search_controller),
+      ukm_recorder_(ukm::UkmRecorder::Get()),
+      ukm_background_recorder_(
+          ukm::UkmBackgroundRecorderFactory::GetForProfile(profile)),
+      weak_factory_(this) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(search_controller_);
+}
+
 SearchRankingEventLogger::~SearchRankingEventLogger() = default;
 
+SearchRankingEventLogger::ResultState::ResultState() = default;
+SearchRankingEventLogger::ResultState::~ResultState() = default;
+
+SearchRankingEventLogger::ResultInfo::ResultInfo() = default;
+SearchRankingEventLogger::ResultInfo::ResultInfo(
+    const SearchRankingEventLogger::ResultInfo& other) = default;
+SearchRankingEventLogger::ResultInfo::~ResultInfo() = default;
+
+void SearchRankingEventLogger::SetEventRecordedForTesting(
+    base::OnceClosure closure) {
+  event_recorded_for_testing_ = std::move(closure);
+}
+
 void SearchRankingEventLogger::Log(
     const base::string16& trimmed_query,
     const ash::SearchResultIdWithPositionIndices& search_results,
-    int position_index) {
-  if (trimmed_query.empty()) {
-    LogSuggestedZeroStateItems(search_results, position_index);
-  } else {
-    // TODO(crbug.com/972817): Add the logics for query-based events.
+    int launched_index) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (const auto& id_index : search_results) {
+    auto* result = search_controller_->FindSearchResult(id_index.id);
+    if (!result)
+      continue;
+
+    ResultInfo result_info;
+    result_info.index = id_index.position_index;
+    result_info.title = result->title();
+    result_info.type = result->result_type();
+    result_info.subtype = result->result_subtype();
+    result_info.relevance = result->relevance();
+
+    // Omnibox results have associated URLs, so are logged keyed on the URL
+    // after validating that it exists in the history service. Other results
+    // have no associated URL, so use a blank source ID.
+    if (result->result_type() == ash::SearchResultType::kOmnibox) {
+      // The id metadata of an OmnioboxResult is a stripped URL, which does not
+      // correspond to the URL that will be navigated to.
+      result_info.target =
+          static_cast<OmniboxResult*>(result)->DestinationURL().spec();
+
+      // When an omnibox result is launched, we need to retrieve a source ID
+      // using the history service. This may be the first time the URL is used
+      // and so it must be committed to the history service database before we
+      // retrieve it, which happens once the page has loaded. So we delay our
+      // check for long enough that most pages will have loaded.
+      if (launched_index == id_index.position_index) {
+        base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+            FROM_HERE,
+            base::BindOnce(
+                &SearchRankingEventLogger::GetBackgroundSourceIdAndLogEvent,
+                weak_factory_.GetWeakPtr(), next_event_id_, trimmed_query,
+                result_info, launched_index),
+            kDelayForHistoryService);
+      } else {
+        GetBackgroundSourceIdAndLogEvent(next_event_id_, trimmed_query,
+                                         result_info, launched_index);
+      }
+    } else {
+      result_info.target = result->id();
+      LogEvent(next_event_id_, trimmed_query, result_info, launched_index,
+               base::nullopt);
+    }
   }
+
+  ++next_event_id_;
 }
 
-void SearchRankingEventLogger::LogSuggestedZeroStateItems(
-    const ash::SearchResultIdWithPositionIndices& search_results,
-    int position_index) {
-  // TODO(crbug.com/972817): Add the logics to log the suggested items.
+void SearchRankingEventLogger::GetBackgroundSourceIdAndLogEvent(
+    int event_id,
+    const base::string16& trimmed_query,
+    const ResultInfo& result,
+    int launched_index) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ukm_background_recorder_->GetBackgroundSourceIdIfAllowed(
+      url::Origin::Create(GURL(result.target)),
+      base::BindOnce(&SearchRankingEventLogger::LogEvent,
+                     base::Unretained(this), event_id, trimmed_query, result,
+                     launched_index));
+}
+
+void SearchRankingEventLogger::LogEvent(
+    int event_id,
+    const base::string16& trimmed_query,
+    const ResultInfo& result,
+    int launched_index,
+    base::Optional<ukm::SourceId> source_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!source_id)
+    source_id = ukm_recorder_->GetNewSourceID();
+
+  const base::Time now = base::Time::Now();
+  base::Time::Exploded now_exploded;
+  now.LocalExplode(&now_exploded);
+
+  ukm::builders::AppListNonAppImpression event(source_id.value());
+  event.SetEventId(event_id)
+      .SetPosition(result.index)
+      .SetIsLaunched(result.index == launched_index)
+      .SetQueryLength(
+          GetExponentialBucketMinForCounts1000(trimmed_query.size()))
+      // Note this is the search provider's original relevance score, not
+      // tweaked by any search ranking models. Scores are floats in 0 to 1, and
+      // we map this to ints 0 to 100.
+      .SetRelevanceScore(static_cast<int>(100 * result.relevance))
+      .SetCategory(
+          static_cast<int>(CategoryFromResultType(result.type, result.subtype)))
+      .SetHourOfDay(now_exploded.hour)
+      .SetDayOfWeek(now_exploded.day_of_week);
+
+  if (result.type == ash::SearchResultType::kLauncher) {
+    event.SetFileExtension(ExtensionTypeFromFileName(result.target));
+  }
+
+  auto& event_info = id_to_result_state_[result.target];
+
+  if (event_info.last_launch != base::nullopt) {
+    base::Time last_launch = event_info.last_launch.value();
+    base::Time::Exploded last_launch_exploded;
+    last_launch.LocalExplode(&last_launch_exploded);
+
+    event.SetTimeSinceLastLaunch(
+        GetExponentialBucketMinForSeconds((now - last_launch).InSeconds()));
+    event.SetTimeOfLastLaunch(last_launch_exploded.hour);
+
+    // Reset the number of launches this hour to 0 if this is the first launch
+    // today of this event, to account for user sessions spanning multiple days.
+    if (result.index == launched_index &&
+        now - last_launch >= base::TimeDelta::FromHours(23)) {
+      event_info.launches_per_hour[now_exploded.hour] = 0;
+    }
+  }
+
+  event.SetLaunchesThisSession(
+      GetExponentialBucketMinForCounts1000(event_info.launches_this_session));
+
+  const auto& launches = event_info.launches_per_hour;
+  event.SetLaunchesAtHour00(GetExponentialBucketMinForCounts1000(launches[0]));
+  event.SetLaunchesAtHour01(GetExponentialBucketMinForCounts1000(launches[1]));
+  event.SetLaunchesAtHour02(GetExponentialBucketMinForCounts1000(launches[2]));
+  event.SetLaunchesAtHour03(GetExponentialBucketMinForCounts1000(launches[3]));
+  event.SetLaunchesAtHour04(GetExponentialBucketMinForCounts1000(launches[4]));
+  event.SetLaunchesAtHour05(GetExponentialBucketMinForCounts1000(launches[5]));
+  event.SetLaunchesAtHour06(GetExponentialBucketMinForCounts1000(launches[6]));
+  event.SetLaunchesAtHour07(GetExponentialBucketMinForCounts1000(launches[7]));
+  event.SetLaunchesAtHour08(GetExponentialBucketMinForCounts1000(launches[8]));
+  event.SetLaunchesAtHour09(GetExponentialBucketMinForCounts1000(launches[9]));
+  event.SetLaunchesAtHour10(GetExponentialBucketMinForCounts1000(launches[10]));
+  event.SetLaunchesAtHour11(GetExponentialBucketMinForCounts1000(launches[11]));
+  event.SetLaunchesAtHour12(GetExponentialBucketMinForCounts1000(launches[12]));
+  event.SetLaunchesAtHour13(GetExponentialBucketMinForCounts1000(launches[13]));
+  event.SetLaunchesAtHour14(GetExponentialBucketMinForCounts1000(launches[14]));
+  event.SetLaunchesAtHour15(GetExponentialBucketMinForCounts1000(launches[15]));
+  event.SetLaunchesAtHour16(GetExponentialBucketMinForCounts1000(launches[16]));
+  event.SetLaunchesAtHour17(GetExponentialBucketMinForCounts1000(launches[17]));
+  event.SetLaunchesAtHour18(GetExponentialBucketMinForCounts1000(launches[18]));
+  event.SetLaunchesAtHour19(GetExponentialBucketMinForCounts1000(launches[19]));
+  event.SetLaunchesAtHour20(GetExponentialBucketMinForCounts1000(launches[20]));
+  event.SetLaunchesAtHour21(GetExponentialBucketMinForCounts1000(launches[21]));
+  event.SetLaunchesAtHour22(GetExponentialBucketMinForCounts1000(launches[22]));
+  event.SetLaunchesAtHour23(GetExponentialBucketMinForCounts1000(launches[23]));
+
+  event.Record(ukm_recorder_);
+
+  if (result.index == launched_index) {
+    event_info.last_launch = now;
+    event_info.launches_this_session += 1;
+    event_info.launches_per_hour[now_exploded.hour] += 1;
+  }
+
+  if (event_recorded_for_testing_)
+    std::move(event_recorded_for_testing_).Run();
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.h b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.h
index c690681b..7a65eb68 100644
--- a/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.h
+++ b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger.h
@@ -5,19 +5,30 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_LOGGING_SEARCH_RANKING_EVENT_LOGGER_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_LOGGING_SEARCH_RANKING_EVENT_LOGGER_H_
 
+#include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
 #include "base/time/time.h"
+#include "chrome/browser/metrics/ukm_background_recorder_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+
+class ChromeSearchResult;
 
 namespace app_list {
 
+// Performs UKM logging of search ranking events in the launcher's results list.
 class SearchRankingEventLogger {
  public:
-  SearchRankingEventLogger();
+  SearchRankingEventLogger(Profile* profile,
+                           SearchController* search_controller);
   ~SearchRankingEventLogger();
   // Called if a search result item got clicked, or a list of search result has
   // been shown to the user after a certain amount of time. |raw_query| is the
@@ -28,13 +39,80 @@
   // clicked, |position_index| will be -1).
   void Log(const base::string16& trimmed_query,
            const ash::SearchResultIdWithPositionIndices& search_results,
-           int position_index);
+           int launched_index);
+
+  // Sets a testing-only closure to inform tests when a UKM event has been
+  // recorded.
+  void SetEventRecordedForTesting(base::OnceClosure closure);
 
  private:
-  // Logs suggested items either from impressions or from click events.
-  void LogSuggestedZeroStateItems(
-      const ash::SearchResultIdWithPositionIndices& search_results,
-      int position_index);
+  // Stores state necessary for logging a given search result that is
+  // accumulated throughout the session.
+  struct ResultState {
+    ResultState();
+    ~ResultState();
+
+    base::Optional<base::Time> last_launch = base::nullopt;
+    // Initialises all elements to 0.
+    int launches_per_hour[24] = {};
+    int launches_this_session = 0;
+  };
+
+  // Stores the relevant parts of a ChromeSearchResult used for logging.
+  struct ResultInfo {
+   public:
+    ResultInfo();
+    ResultInfo(const ResultInfo& other);
+    ~ResultInfo();
+
+    int index;
+    std::string target;
+    base::string16 title;
+    ash::SearchResultType type;
+    int subtype;
+    float relevance;
+  };
+
+  // Calls the UKM API for a source ID relevant to |result|, and then begins the
+  // logging process by calling LogEvent.
+  void GetBackgroundSourceIdAndLogEvent(int event_id,
+                                        const base::string16& trimmed_query,
+                                        const ResultInfo& result_info,
+                                        int launched_index);
+
+  // Logs the given event to UKM. If |source_id| is nullopt then use a blank
+  // source ID.
+  void LogEvent(int event_id,
+                const base::string16& trimmed_query,
+                const ResultInfo& result_info,
+                int launched_index,
+                base::Optional<ukm::SourceId> source_id);
+
+  SearchController* search_controller_;
+  // Some events do not have an associated URL and so are logged directly with
+  // |ukm_recorder_| using a blank source ID. Other events need to validate the
+  // URL before recording, and use |ukm_background_recorder_|.
+  ukm::UkmRecorder* ukm_recorder_;
+  ukm::UkmBackgroundRecorderService* ukm_background_recorder_;
+
+  // TODO(972817): Zero-state previous query results change their URL based on
+  // the position they should be displayed at in the launcher. Because their
+  // ID changes, we lose information on their result state. If, in future, we
+  // want to rank these results and want more information, we should normalize
+  // their IDs to remove the position information.
+  std::map<std::string, ResultState> id_to_result_state_;
+
+  // The next, unused, event ID.
+  int next_event_id_ = 1;
+
+  // Testing-only closure to inform tests when a UKM event has been recorded.
+  base::OnceClosure event_recorded_for_testing_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<SearchRankingEventLogger> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchRankingEventLogger);
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger_unittest.cc b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger_unittest.cc
new file mode 100644
index 0000000..ecacc78
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/logging/search_ranking_event_logger_unittest.cc
@@ -0,0 +1,355 @@
+// Copyright 2019 The Chromium Authors. 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/logging/search_ranking_event_logger.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_mock_clock_override.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_types.h"
+#include "components/history/core/test/history_service_test_util.h"
+#include "components/history/core/test/test_history_database.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "components/ukm/ukm_recorder_impl.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/mojom/ukm_interface.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace app_list {
+
+namespace {
+
+using ukm::TestUkmRecorder;
+
+using ResultType = ash::SearchResultType;
+using UkmEntry = ukm::builders::AppListNonAppImpression;
+
+std::unique_ptr<KeyedService> BuildHistoryService(
+    content::BrowserContext* context) {
+  TestingProfile* profile = static_cast<TestingProfile*>(context);
+
+  base::FilePath history_path(profile->GetPath().Append("history"));
+
+  // Delete the file before creating the service.
+  if (!base::DeleteFile(history_path, false) ||
+      base::PathExists(history_path)) {
+    ADD_FAILURE() << "failed to delete history db file "
+                  << history_path.value();
+    return nullptr;
+  }
+
+  std::unique_ptr<history::HistoryService> history_service =
+      std::make_unique<history::HistoryService>();
+  if (history_service->Init(
+          history::TestHistoryDatabaseParamsForPath(profile->GetPath()))) {
+    return std::move(history_service);
+  }
+
+  ADD_FAILURE() << "failed to initialize history service";
+  return nullptr;
+}
+
+class TestSearchResult : public ChromeSearchResult {
+ public:
+  TestSearchResult(const std::string& id,
+                   ResultType type,
+                   int subtype,
+                   double relevance)
+      : instance_id_(instantiation_count++) {
+    set_id(id);
+    set_result_subtype(subtype);
+    set_relevance(relevance);
+    SetTitle(base::UTF8ToUTF16(id));
+    SetResultType(type);
+  }
+  ~TestSearchResult() override {}
+
+  // ChromeSearchResult overrides:
+  void Open(int event_flags) override {}
+  void InvokeAction(int action_index, int event_flags) override {}
+  SearchResultType GetSearchResultType() const override {
+    return app_list::SEARCH_RESULT_TYPE_BOUNDARY;
+  }
+
+ private:
+  static int instantiation_count;
+
+  int instance_id_;
+};
+int TestSearchResult::instantiation_count = 0;
+
+class SearchControllerFake : public SearchController {
+ public:
+  explicit SearchControllerFake(Profile* profile)
+      : SearchController(nullptr, nullptr, profile) {}
+
+  ChromeSearchResult* FindSearchResult(const std::string& result_id) override {
+    auto it = results_.find(result_id);
+    CHECK(it != results_.end());
+    return &it->second;
+  }
+
+  void AddSearchResult(const std::string& result_id,
+                       ResultType type,
+                       int subtype,
+                       double relevance) {
+    results_.emplace(
+        std::piecewise_construct, std::forward_as_tuple(result_id),
+        std::forward_as_tuple(result_id, type, subtype, relevance));
+  }
+
+  std::map<std::string, TestSearchResult> results_;
+};
+
+}  // namespace
+
+class SearchRankingEventLoggerTest : public testing::Test {
+ public:
+  SearchRankingEventLoggerTest() {}
+
+  void SetUp() override {
+    ASSERT_TRUE(history_dir_.CreateUniqueTempDir());
+    history_service_ = std::make_unique<history::HistoryService>();
+    history_service_->Init(
+        history::TestHistoryDatabaseParamsForPath(history_dir_.GetPath()));
+
+    TestingProfile::TestingFactories factories;
+
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetProfileName("testuser@gmail.com");
+    profile_builder.AddTestingFactory(
+        HistoryServiceFactory::GetInstance(),
+        base::BindRepeating(&BuildHistoryService));
+    profile_ = profile_builder.Build();
+
+    search_controller_ = std::make_unique<SearchControllerFake>(profile_.get());
+
+    logger_ = std::make_unique<SearchRankingEventLogger>(
+        profile_.get(), search_controller_.get());
+  }
+
+  void AddResultWithHistory(const std::string& result_id,
+                            ResultType type,
+                            int subtype,
+                            double relevance) {
+    search_controller_->AddSearchResult(result_id, type, subtype, relevance);
+    history_service_->AddPage(GURL(result_id), base::Time::Now(),
+                              history::VisitSource::SOURCE_BROWSED);
+    Wait();
+  }
+
+  void AddResultWithoutHistory(const std::string& result_id,
+                               ResultType type,
+                               int subtype,
+                               double relevance) {
+    search_controller_->AddSearchResult(result_id, type, subtype, relevance);
+  }
+
+  std::vector<const ukm::mojom::UkmEntry*> GetUkmEntries() {
+    return recorder_.GetEntriesByName(UkmEntry::kEntryName);
+  }
+
+  void SetLocalTime(const int day, const int hour, const int minute = 10) {
+    // Advance time to 00:00 on the next local Sunday.
+    base::Time::Exploded now;
+    base::Time::Now().LocalExplode(&now);
+    const auto sunday = base::TimeDelta::FromDays(6 - now.day_of_week) +
+                        base::TimeDelta::FromHours(23 - now.hour) +
+                        base::TimeDelta::FromMinutes(60 - now.minute);
+    if (sunday > base::TimeDelta())
+      time_.Advance(sunday);
+
+    base::Time::Now().LocalExplode(&now);
+    CHECK_EQ(now.day_of_week, 0);
+    CHECK_EQ(now.hour, 0);
+    CHECK_EQ(now.minute, 0);
+
+    // Then advance to the given time and day.
+    const auto advance = base::TimeDelta::FromDays(day) +
+                         base::TimeDelta::FromHours(hour) +
+                         base::TimeDelta::FromMinutes(minute);
+    if (advance > base::TimeDelta())
+      time_.Advance(advance);
+  }
+
+  // Wait for separate background task runner in HistoryService to complete
+  // all tasks and then all the tasks on the current one to complete as well.
+  void Wait() {
+    history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
+    thread_bundle_.RunUntilIdle();
+  }
+
+  // Wait for a background UKM event to finish processing. This is necessary if
+  // the logger needs to query UkmBackgroundRecorderService for a Source ID,
+  // which currently happens if the search result is from the Omnibox.
+  //
+  // Warning: this will fail if more than one search result exists within one
+  // call to SearchRankingEventLogger::Log.
+  void WaitForUkmEvent() {
+    base::RunLoop run_loop;
+    logger_->SetEventRecordedForTesting(run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
+  base::ScopedMockClockOverride time_;
+  base::ScopedTempDir history_dir_;
+
+  ukm::TestAutoSetUkmRecorder recorder_;
+
+  std::unique_ptr<Profile> profile_;
+  std::unique_ptr<history::HistoryService> history_service_;
+  std::unique_ptr<SearchControllerFake> search_controller_;
+  std::unique_ptr<SearchRankingEventLogger> logger_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SearchRankingEventLoggerTest);
+};
+
+// TODO(931149): We should add a test to ensure that a result with a URL in the
+// history service has the correct source ID.
+
+// Test a single logging call containing only one result. As there is only one
+// event, we don't test any of the stateful fields that rely on previous
+// launches.
+TEST_F(SearchRankingEventLoggerTest, OneEventWithOneResult) {
+  SetLocalTime(5, 13, 25);
+
+  AddResultWithoutHistory("myfile.txt", ResultType::kLauncher, 2, 0.18);
+
+  logger_->Log(base::UTF8ToUTF16("elevenchars"), {{"myfile.txt", 2}}, 1);
+
+  const auto& entries = GetUkmEntries();
+  ASSERT_EQ(entries.size(), 1ul);
+
+  const auto* entry = entries[0];
+  TestUkmRecorder::ExpectEntryMetric(entry, "EventId", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Position", 2);
+  TestUkmRecorder::ExpectEntryMetric(entry, "IsLaunched", 0);
+  TestUkmRecorder::ExpectEntryMetric(entry, "QueryLength", 11);
+  TestUkmRecorder::ExpectEntryMetric(entry, "RelevanceScore", 18);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Category", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "FileExtension", 57);
+  TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 13);
+  TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek", 5);
+}
+
+// Test a single logging call containing three results.
+TEST_F(SearchRankingEventLoggerTest, OneEventWithManyResults) {
+  SetLocalTime(4, 16, 10);
+
+  // Subtype is unused here.
+  AddResultWithoutHistory("first.avi", ResultType::kLauncher, 0, 0.7);
+  AddResultWithoutHistory("second.mkv", ResultType::kLauncher, 0, 0.6);
+  AddResultWithoutHistory("third.mov", ResultType::kLauncher, 0, 0.5);
+
+  logger_->Log(base::UTF8ToUTF16("ninechars"),
+               {{"first.avi", 0}, {"second.mkv", 1}, {"third.mov", 2}}, 1);
+
+  const auto& entries = GetUkmEntries();
+  ASSERT_EQ(entries.size(), 3ul);
+
+  const auto* entry = entries[0];
+  TestUkmRecorder::ExpectEntryMetric(entry, "EventId", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Position", 0);
+  TestUkmRecorder::ExpectEntryMetric(entry, "IsLaunched", 0);
+  TestUkmRecorder::ExpectEntryMetric(entry, "QueryLength", 9);
+  TestUkmRecorder::ExpectEntryMetric(entry, "RelevanceScore", 70);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Category", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "FileExtension", 5);
+  TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 16);
+  TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek", 4);
+
+  entry = entries[1];
+  TestUkmRecorder::ExpectEntryMetric(entry, "EventId", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Position", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "IsLaunched", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "QueryLength", 9);
+  TestUkmRecorder::ExpectEntryMetric(entry, "RelevanceScore", 60);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Category", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "FileExtension", 20);
+  TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 16);
+  TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek", 4);
+
+  entry = entries[2];
+  TestUkmRecorder::ExpectEntryMetric(entry, "EventId", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Position", 2);
+  TestUkmRecorder::ExpectEntryMetric(entry, "IsLaunched", 0);
+  TestUkmRecorder::ExpectEntryMetric(entry, "QueryLength", 9);
+  TestUkmRecorder::ExpectEntryMetric(entry, "RelevanceScore", 50);
+  TestUkmRecorder::ExpectEntryMetric(entry, "Category", 1);
+  TestUkmRecorder::ExpectEntryMetric(entry, "FileExtension", 21);
+  TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 16);
+  TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek", 4);
+}
+
+// Tests the fields that are affected by previous launches of a given search
+// result.
+TEST_F(SearchRankingEventLoggerTest, StatefulFieldsOfRepeatedEvents) {
+  SetLocalTime(4, 16, 10);
+  AddResultWithoutHistory("file.txt", ResultType::kLauncher, 3, 0.7);
+
+  logger_->Log(base::UTF8ToUTF16("query"), {{"file.txt", 0}}, 0);
+
+  {
+    const auto& entries = GetUkmEntries();
+    ASSERT_EQ(entries.size(), 1ul);
+    TestUkmRecorder::ExpectEntryMetric(entries[0], "EventId", 1);
+    TestUkmRecorder::ExpectEntryMetric(entries[0], "LaunchesThisSession", 0);
+  }
+
+  // Record four more evenets, but only three of them are launches (two for the
+  // purposes of the metrics for the last event).
+  logger_->Log(base::UTF8ToUTF16("query"), {{"file.txt", 1}}, 1);
+  // Not a launch:
+  logger_->Log(base::UTF8ToUTF16("abcd"), {{"file.txt", 0}}, -1);
+  logger_->Log(base::UTF8ToUTF16("abcd"), {{"file.txt", 2}}, 2);
+  logger_->Log(base::UTF8ToUTF16(""), {{"file.txt", 0}}, 0);
+
+  {
+    const auto& entries = GetUkmEntries();
+    ASSERT_EQ(entries.size(), 5ul);
+    TestUkmRecorder::ExpectEntryMetric(entries[4], "EventId", 5);
+    TestUkmRecorder::ExpectEntryMetric(entries[4], "LaunchesThisSession", 3);
+    TestUkmRecorder::ExpectEntryMetric(entries[4], "TimeSinceLastLaunch", 0);
+    TestUkmRecorder::ExpectEntryMetric(entries[4], "TimeOfLastLaunch", 16);
+    TestUkmRecorder::ExpectEntryMetric(entries[4], "LaunchesAtHour16", 3);
+  }
+
+  // Advance time by 24 hours. We expect LaunchesAtHour16 to be reset to 0.
+  time_.Advance(base::TimeDelta::FromHours(24));
+
+  logger_->Log(base::UTF8ToUTF16(""), {{"file.txt", 0}}, 0);
+
+  {
+    const auto& entries = GetUkmEntries();
+    ASSERT_EQ(entries.size(), 6ul);
+    // Bin of 24*60*60 has value 85507, which is the 211th bin overall.
+    TestUkmRecorder::ExpectEntryMetric(entries[5], "TimeSinceLastLaunch",
+                                       85507);
+    TestUkmRecorder::ExpectEntryMetric(entries[5], "TimeOfLastLaunch", 16);
+    TestUkmRecorder::ExpectEntryMetric(entries[5], "LaunchesAtHour16", 0);
+  }
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 801e9c1..2b31171 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -231,6 +231,10 @@
   }
 }
 
+GURL OmniboxResult::DestinationURL() const {
+  return match_.destination_url;
+}
+
 void OmniboxResult::UpdateIcon() {
   BookmarkModel* bookmark_model =
       BookmarkModelFactory::GetForBrowserContext(profile_);
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index 33e821c..677ffb3 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/omnibox/browser/autocomplete_match.h"
+#include "url/gurl.h"
 
 class AppListControllerDelegate;
 class AutocompleteController;
@@ -41,6 +42,9 @@
   void InvokeAction(int action_index, int event_flags) override;
   SearchResultType GetSearchResultType() const override;
 
+  // Returns the URL that will be navigated to by this search result.
+  GURL DestinationURL() const;
+
  private:
   void UpdateIcon();
   void UpdateTitleAndDetails();
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index f641795..543db34 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -52,7 +52,7 @@
   // Takes ownership of |provider| and associates it with given mixer group.
   void AddProvider(size_t group_id, std::unique_ptr<SearchProvider> provider);
 
-  ChromeSearchResult* FindSearchResult(const std::string& result_id);
+  virtual ChromeSearchResult* FindSearchResult(const std::string& result_id);
   ChromeSearchResult* GetResultByTitleForTest(const std::string& title);
 
   // Sends training signal to each |providers_|
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
index bf196d93..96233c5b 100644
--- 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
@@ -237,14 +237,13 @@
 }
 
 JsonConfigConverter::JsonConfigConverter(service_manager::Connector* connector)
-    : connector_(connector) {
-  DCHECK(connector_);
-}
+    : connector_(connector) {}
 JsonConfigConverter::~JsonConfigConverter() {}
 
 void JsonConfigConverter::Convert(const std::string& json_string,
                                   const std::string& model_identifier,
                                   OnConfigLoadedCallback callback) {
+  DCHECK(connector_);
   GetJsonParser().Parse(
       json_string,
       base::BindOnce(&JsonConfigConverter::OnJsonParsed, base::Unretained(this),
diff --git a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
index 355cf01..3e1b4d6 100644
--- a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
@@ -20,7 +20,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
@@ -835,8 +836,8 @@
     return;
   }
 
-  apps::AppServiceProxyImpl* proxy =
-      apps::AppServiceProxyImpl::GetImplForTesting(profile());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile());
   ASSERT_NE(proxy, nullptr);
 
   apps::StubIconLoader stub_icon_loader;
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
index ad5aef6f..19a045d6 100644
--- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
+++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
@@ -88,6 +88,15 @@
   }
 }
 
+void AccessibilityControllerClient::TriggerAccessibilityAlertWithMessage(
+    const std::string& message) {
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  if (!profile)
+    return;
+
+  AutomationManagerAura::GetInstance()->HandleAlert(message);
+}
+
 void AccessibilityControllerClient::PlayEarcon(int32_t sound_key) {
   chromeos::AccessibilityManager::Get()->PlayEarcon(
       sound_key, chromeos::PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED);
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.h b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.h
index 5c20abe..60e70f18 100644
--- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.h
+++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.h
@@ -17,6 +17,8 @@
 
   // ash::AccessibilityControllerClient:
   void TriggerAccessibilityAlert(ash::AccessibilityAlert alert) override;
+  void TriggerAccessibilityAlertWithMessage(
+      const std::string& message) override;
   void PlayEarcon(int sound_key) override;
   base::TimeDelta PlayShutdownSound() override;
   void HandleAccessibilityGesture(ax::mojom::Gesture gesture) override;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index c19dd81..db6a3fb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -38,7 +38,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/arc_apps.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
@@ -324,11 +324,11 @@
     bool flush_app_service_mojo_calls = false;
     if (app_service_proxy_connector_) {
       DCHECK(profile());
-      app_service_proxy_impl_.reset(apps::AppServiceProxyImpl::CreateForTesting(
+      app_service_proxy_.reset(apps::AppServiceProxy::CreateForTesting(
           profile(), app_service_proxy_connector_));
       old_app_service_proxy_for_testing_ =
           AppServiceAppModelBuilder::SetAppServiceProxyForTesting(
-              app_service_proxy_impl_.get());
+              app_service_proxy_.get());
       // Flush the App Service Mojo calls, but only after calling
       // arc_test_.SetUp, as it also pumps the run-loop in general.
       flush_app_service_mojo_calls = true;
@@ -338,7 +338,7 @@
       arc_test_.SetUp(profile());
 
     if (flush_app_service_mojo_calls)
-      app_service_proxy_impl_->FlushMojoCallsForTesting();
+      app_service_proxy_->FlushMojoCallsForTesting();
 
     // Wait until |extension_system| is signaled as started.
     base::RunLoop run_loop;
@@ -481,10 +481,10 @@
 
   void TearDown() override {
     arc_test_.TearDown();
-    if (app_service_proxy_impl_) {
+    if (app_service_proxy_) {
       AppServiceAppModelBuilder::SetAppServiceProxyForTesting(
           old_app_service_proxy_for_testing_);
-      app_service_proxy_impl_.reset(nullptr);
+      app_service_proxy_.reset(nullptr);
     }
     launcher_controller_ = nullptr;
     BrowserWithTestWindowTest::TearDown();
@@ -969,7 +969,7 @@
   app_list::AppListSyncableService* app_list_syncable_service_ = nullptr;
 
   service_manager::Connector* app_service_proxy_connector_ = nullptr;
-  std::unique_ptr<apps::AppServiceProxyImpl> app_service_proxy_impl_;
+  std::unique_ptr<apps::AppServiceProxy> app_service_proxy_;
   apps::AppServiceProxy* old_app_service_proxy_for_testing_ = nullptr;
 
  private:
@@ -1015,8 +1015,8 @@
 
     ChromeLauncherControllerTest::SetUp();
     if (app_service_proxy_connector_) {
-      arc_apps_.reset(apps::ArcApps::CreateForTesting(
-          profile(), app_service_proxy_impl_.get()));
+      arc_apps_.reset(
+          apps::ArcApps::CreateForTesting(profile(), app_service_proxy_.get()));
     }
   }
 
@@ -1489,8 +1489,8 @@
   EXPECT_EQ("App2, Fake App 1, Chrome, App1, Fake App 0, Gmail",
             GetPinnedAppStatus());
 
-  if (app_service_proxy_impl_)
-    app_service_proxy_impl_->FlushMojoCallsForTesting();
+  if (app_service_proxy_)
+    app_service_proxy_->FlushMojoCallsForTesting();
   copy_sync_list = app_list_syncable_service_->GetAllSyncData(syncer::APP_LIST);
 
   ResetLauncherController();
diff --git a/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc b/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc
index bf8f784..d4675a1 100644
--- a/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/launcher_page_switches_interactive_uitest.cc
@@ -12,8 +12,11 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/test/base/perf/drag_event_generator.h"
 #include "chrome/test/base/perf/performance_test.h"
 #include "ui/base/test/ui_controls.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 
 namespace {
 
@@ -116,3 +119,74 @@
 INSTANTIATE_TEST_SUITE_P(,
                          LauncherPageSwitchesTest,
                          /*tablet_mode=*/::testing::Bool());
+
+// The test gets very flaky in tablet-mode, so it's in clamshell mode only for
+// now.
+// TODO(mukai): investigate why and enable this test case with tablet-mode too.
+class LauncherPageDragTest : public UIPerformanceTest {
+ public:
+  LauncherPageDragTest() = default;
+  ~LauncherPageDragTest() override = default;
+
+  // UIPerformanceTest:
+  void SetUpOnMainThread() override {
+    UIPerformanceTest::SetUpOnMainThread();
+
+    test::PopulateDummyAppListItems(100);
+
+    ash::ShellTestApi shell_test_api;
+
+    // Open the fullscreen app; required for page switching.
+    BrowserView* browser_view =
+        BrowserView::GetBrowserViewForBrowser(browser());
+    aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
+    ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
+                              /*control=*/false,
+                              /*shift=*/true,
+                              /*alt=*/false,
+                              /* command = */ false);
+    shell_test_api.WaitForLauncherAnimationState(
+        ash::AppListViewState::kFullscreenAllApps);
+
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      base::RunLoop run_loop;
+      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+                            base::TimeDelta::FromSeconds(5));
+      run_loop.Run();
+    }
+  }
+
+  // UIPerformanceTest:
+  std::vector<std::string> GetUMAHistogramNames() const override {
+    return {
+        "Apps.PaginationTransition.DragScroll.PresentationTime.ClamshellMode",
+        "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
+        "ClamshellMode",
+    };
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LauncherPageDragTest);
+};
+
+IN_PROC_BROWSER_TEST_F(LauncherPageDragTest, Run) {
+  ash::ShellTestApi shell_test_api;
+
+  gfx::Rect display_bounds =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
+          .bounds();
+  gfx::Point start_point = display_bounds.CenterPoint();
+  gfx::Point end_point(start_point);
+  end_point.set_y(10);
+  ui_test_utils::DragEventGenerator generator(
+      std::make_unique<ui_test_utils::InterpolatedProducer>(
+          start_point, end_point, base::TimeDelta::FromMilliseconds(1000)),
+      /*touch=*/true);
+
+  ash::PaginationModel* model = ash::ShellTestApi().GetAppListPaginationModel();
+  ASSERT_TRUE(model);
+  PageSwitchWaiter waiter(model);
+  generator.Wait();
+  waiter.Wait();
+}
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
index 355bacee..173acbd 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
@@ -12,7 +12,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
@@ -23,7 +22,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "content/public/browser/notification_service.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "ui/aura/client/aura_constants.h"
@@ -120,6 +118,8 @@
   DCHECK_EQ(instance_, this);
   instance_ = nullptr;
 
+  BrowserList::RemoveObserver(this);
+
   // This may trigger callbacks to us, delete it early on.
   multi_user_window_manager_.reset();
 
@@ -150,10 +150,7 @@
   DCHECK(account_id_to_app_observer_.find(current_account_id) ==
          account_id_to_app_observer_.end());
 
-  // The BrowserListObserver would have been better to use then the old
-  // notification system, but that observer fires before the window got created.
-  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
-                 content::NotificationService::AllSources());
+  BrowserList::AddObserver(this);
 
   // Add an app window observer & all already running apps.
   Profile* profile =
@@ -184,19 +181,20 @@
     account_id_to_app_observer_[account_id]->OnAppWindowAdded(*it);
 
   // Account all existing browser windows of this user accordingly.
-  BrowserList* browser_list = BrowserList::GetInstance();
-  BrowserList::const_iterator browser_it = browser_list->begin();
-  for (; browser_it != browser_list->end(); ++browser_it) {
-    if ((*browser_it)->profile()->GetOriginalProfile() == profile)
-      AddBrowserWindow(*browser_it);
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (browser->profile()->IsSameProfile(profile))
+      OnBrowserAdded(browser);
   }
 }
 
-void MultiProfileSupport::Observe(int type,
-                                  const content::NotificationSource& source,
-                                  const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_OPENED, type);
-  AddBrowserWindow(content::Source<Browser>(source).ptr());
+void MultiProfileSupport::OnBrowserAdded(Browser* browser) {
+  // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
+  // come here with no valid window.
+  if (!browser->window() || !browser->window()->GetNativeWindow())
+    return;
+  multi_user_window_manager_->SetWindowOwner(
+      browser->window()->GetNativeWindow(),
+      multi_user_util::GetAccountIdFromProfile(browser->profile()));
 }
 
 void MultiProfileSupport::OnWindowOwnerEntryChanged(aura::Window* window,
@@ -233,13 +231,3 @@
   chrome_launcher_controller->ActiveUserChanged(
       multi_user_window_manager_->CurrentAccountId());
 }
-
-void MultiProfileSupport::AddBrowserWindow(Browser* browser) {
-  // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
-  // come here with no valid window.
-  if (!browser->window() || !browser->window()->GetNativeWindow())
-    return;
-  multi_user_window_manager_->SetWindowOwner(
-      browser->window()->GetNativeWindow(),
-      multi_user_util::GetAccountIdFromProfile(browser->profile()));
-}
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support.h b/chrome/browser/ui/ash/multi_user/multi_profile_support.h
index 4f9c5b8..836d2b07 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support.h
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support.h
@@ -11,9 +11,8 @@
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/browser_list_observer.h"
 #include "components/account_id/account_id.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 
 class AppObserver;
 class Browser;
@@ -36,7 +35,7 @@
 // to MultiUserWindowManager as well as all app windows. This class is only
 // created if SessionControllerClient::IsMultiProfileAvailable() returns true.
 class MultiProfileSupport : public ash::MultiUserWindowManagerDelegate,
-                            public content::NotificationObserver {
+                            public BrowserListObserver {
  public:
   // Create the manager and use |active_account_id| as the active user.
   explicit MultiProfileSupport(const AccountId& active_account_id);
@@ -55,10 +54,8 @@
     return multi_user_window_manager_.get();
   }
 
-  // content::NotificationObserver overrides:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override;
 
  private:
   friend class ash::MultiProfileSupportTest;
@@ -70,9 +67,6 @@
                                  bool teleported) override;
   void OnTransitionUserShelfToNewAccount() override;
 
-  // Add a browser window to the system so that the owner can be remembered.
-  void AddBrowserWindow(Browser* browser);
-
   // The single instance of MultiProfileSupport, tracked solely for
   // tests.
   static MultiProfileSupport* instance_;
@@ -82,9 +76,6 @@
   // A list of all known users and their app window observers.
   AccountIdToAppWindowObserver account_id_to_app_observer_;
 
-  // The notification registrar to track the creation of browser windows.
-  content::NotificationRegistrar registrar_;
-
   std::unique_ptr<ash::MultiUserWindowManager> multi_user_window_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiProfileSupport);
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index f209428..5837a67 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -961,9 +961,9 @@
   content::PrepContentsForBeforeUnloadTest(contents);
 
   // Open a second browser window at about:blank.
-  ui_test_utils::BrowserAddedObserver browser_added_observer;
   chrome::NewEmptyWindow(browser()->profile());
-  Browser* second_window = browser_added_observer.WaitForSingleNewBrowser();
+  Browser* second_window = BrowserList::GetInstance()->GetLastActive();
+  EXPECT_NE(second_window, browser());
   ui_test_utils::NavigateToURL(second_window, GURL(url::kAboutBlankURL));
 
   // Tell the application to quit. IDC_EXIT calls AttemptUserExit, which on
@@ -2164,9 +2164,7 @@
   CloseBrowserSynchronously(browser());
   EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
 
-  ui_test_utils::BrowserAddedObserver browser_added_observer;
   chrome::NewEmptyWindow(profile);
-  browser_added_observer.WaitForSingleNewBrowser();
 
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
 }
@@ -2196,9 +2194,7 @@
   EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
 
   // Starting a browser window should work just fine.
-  ui_test_utils::BrowserAddedObserver browser_added_observer;
   CreateBrowser(ProfileManager::GetActiveUserProfile());
-  browser_added_observer.WaitForSingleNewBrowser();
 
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
 }
@@ -2215,9 +2211,7 @@
       session_service->GetBaseSessionServiceForTest();
   ASSERT_FALSE(ProcessedAnyCommands(base_session_service));
 
-  ui_test_utils::BrowserAddedObserver browser_added_observer;
   CreateBrowserForApp("blah", profile);
-  browser_added_observer.WaitForSingleNewBrowser();
 
   ASSERT_FALSE(ProcessedAnyCommands(base_session_service));
 }
@@ -2682,9 +2676,7 @@
                      browser()->tab_strip_model()->GetActiveWebContents());
 
   Profile* profile = ProfileManager::GetActiveUserProfile();
-  ui_test_utils::BrowserAddedObserver browser_added_observer;
   Browser* app_browser = CreateBrowserForApp("blah", profile);
-  browser_added_observer.WaitForSingleNewBrowser();
   auto* app_contents = app_browser->tab_strip_model()->GetActiveWebContents();
   CheckDisplayModeMQ(ASCIIToUTF16("standalone"), app_contents);
 
diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc
index 600ae7f..7de80a8 100644
--- a/chrome/browser/ui/browser_list.cc
+++ b/chrome/browser/ui/browser_list.cc
@@ -110,10 +110,6 @@
   RemoveBrowserFrom(browser, &browser_list->last_active_browsers_);
   browser_list->currently_closing_browsers_.erase(browser);
 
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser),
-      content::NotificationService::NoDetails());
-
   RemoveBrowserFrom(browser, &browser_list->browsers_);
 
   for (BrowserListObserver& observer : observers_.Get())
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 38fe7bc8..3ff9e612 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -340,16 +340,16 @@
   virtual void ShowUpdateChromeDialog() = 0;
 
   // Shows the intent picker bubble. |app_info| contains the app candidates to
-  // display, |enable_stay_in_chrome| allows to enable or disable 'Stay in
-  // Chrome' (used for non-http(s) queries), if |show_persistence_options| is
-  // false, the "remember my choice" checkbox is hidden, the "stay in chrome"
-  // button is hidden, and |callback| helps to continue the flow back to either
+  // display, if |show_stay_in_chrome| is false, the 'Stay in
+  // Chrome' (used for non-http(s) queries) button is hidden, if
+  // |show_remember_selection| is false, the "remember my choice" checkbox is
+  // hidden and |callback| helps to continue the flow back to either
   // AppsNavigationThrottle or ArcExternalProtocolDialog capturing the user's
   // decision and storing UMA metrics.
   virtual void ShowIntentPickerBubble(
       std::vector<apps::IntentPickerAppInfo> app_info,
-      bool enable_stay_in_chrome,
-      bool show_persistence_options,
+      bool show_stay_in_chrome,
+      bool show_remember_selection,
       IntentPickerResponse callback) = 0;
 
   // Shows the Bookmark bubble. |url| is the URL being bookmarked,
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 4c817cbcc..fcc364537 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -124,8 +124,8 @@
   }
 
   const std::string query_string = base::StrCat(
-      {kChromeReleaseNotesURL, "version=", milestone, "&tags=", board_name, ",",
-       region, ",", language, ",", channel_name, ",", user_type});
+      {kChromeReleaseNotesURL, "?version=", milestone, "&tags=", board_name,
+       ",", region, ",", language, ",", channel_name, ",", user_type});
   return query_string;
 }
 #endif
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
index 6984eb0..2db0731 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
@@ -34,6 +34,8 @@
       override {}
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
  private:
   service_manager::Connector* const connector_;
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index bf5e42a..4f3fa80 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -86,7 +86,7 @@
 std::string GetDesktopName(base::Environment* env) {
 #if defined(GOOGLE_CHROME_BUILD)
   return "google-chrome.desktop";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   // Allow $CHROME_DESKTOP to override the built-in value, so that development
   // versions can set themselves as the default without interfering with
   // non-official, packaged versions using the built-in value.
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 9e57eb7..4e2ec6a 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -45,6 +45,8 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/common/buildflags.h"
@@ -108,43 +110,36 @@
 namespace {
 
 // Keeps track on which profiles have been launched.
-class ProfileLaunchObserver : public content::NotificationObserver {
+class ProfileLaunchObserver : public content::NotificationObserver,
+                              public BrowserListObserver {
  public:
-  ProfileLaunchObserver()
-      : profile_to_activate_(NULL),
-        activated_profile_(false) {
+  ProfileLaunchObserver() {
     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                    content::NotificationService::AllSources());
-    registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
-                   content::NotificationService::AllSources());
+    BrowserList::AddObserver(this);
   }
-  ~ProfileLaunchObserver() override {}
 
+  ~ProfileLaunchObserver() override { BrowserList::RemoveObserver(this); }
+
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override {
+    opened_profiles_.insert(browser->profile());
+    MaybeActivateProfile();
+  }
+
+  // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override {
-    switch (type) {
-      case chrome::NOTIFICATION_PROFILE_DESTROYED: {
-        Profile* profile = content::Source<Profile>(source).ptr();
-        launched_profiles_.erase(profile);
-        opened_profiles_.erase(profile);
-        if (profile == profile_to_activate_)
-          profile_to_activate_ = NULL;
-        // If this profile was the last launched one without an opened window,
-        // then we may be ready to activate |profile_to_activate_|.
-        MaybeActivateProfile();
-        break;
-      }
-      case chrome::NOTIFICATION_BROWSER_OPENED: {
-        Browser* browser = content::Source<Browser>(source).ptr();
-        DCHECK(browser);
-        opened_profiles_.insert(browser->profile());
-        MaybeActivateProfile();
-        break;
-      }
-      default:
-        NOTREACHED();
-    }
+    DCHECK_EQ(type, chrome::NOTIFICATION_PROFILE_DESTROYED);
+    Profile* profile = content::Source<Profile>(source).ptr();
+    launched_profiles_.erase(profile);
+    opened_profiles_.erase(profile);
+    if (profile == profile_to_activate_)
+      profile_to_activate_ = nullptr;
+    // If this profile was the last launched one without an opened window,
+    // then we may be ready to activate |profile_to_activate_|.
+    MaybeActivateProfile();
   }
 
   bool HasBeenLaunched(const Profile* profile) const {
@@ -155,7 +150,7 @@
     launched_profiles_.insert(profile);
     if (chrome::FindBrowserWithProfile(profile)) {
       // A browser may get opened before we get initialized (e.g., in tests),
-      // so we never see the NOTIFICATION_BROWSER_OPENED for it.
+      // so we never see the OnBrowserAdded() for it.
       opened_profiles_.insert(profile);
     }
   }
@@ -191,10 +186,8 @@
         base::BindOnce(&ProfileLaunchObserver::ActivateProfile,
                        base::Unretained(this)));
     // Avoid posting more than once before ActivateProfile gets called.
-    registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_OPENED,
-                      content::NotificationService::AllSources());
-    registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
-                      content::NotificationService::AllSources());
+    registrar_.RemoveAll();
+    BrowserList::RemoveObserver(this);
   }
 
   void ActivateProfile() {
@@ -207,7 +200,7 @@
       if (browser)
         browser->window()->Activate();
       // No need try to activate this profile again.
-      profile_to_activate_ = NULL;
+      profile_to_activate_ = nullptr;
     }
     // Assign true here, even if no browser was actually activated, so that
     // the test can stop waiting, and fail gracefully when needed.
@@ -223,12 +216,12 @@
   // be activated on top of it.
   std::set<const Profile*> opened_profiles_;
   content::NotificationRegistrar registrar_;
-  // This is NULL until the profile to activate has been chosen. This value,
+  // This is null until the profile to activate has been chosen. This value
   // should only be set once all profiles have been launched, otherwise,
   // activation may not happen after the launch of newer profiles.
-  Profile* profile_to_activate_;
+  Profile* profile_to_activate_ = nullptr;
   // Set once we attempted to activate a profile. We only get one shot at this.
-  bool activated_profile_;
+  bool activated_profile_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileLaunchObserver);
 };
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 542fcd9e..57f74b8b 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1025,36 +1025,6 @@
   ASSERT_EQ(1u, chrome::GetBrowserCount(profile2));
 }
 
-class SupervisedUserBrowserCreatorTest : public InProcessBrowserTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(switches::kSupervisedUserId,
-                                    supervised_users::kChildAccountSUID);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(SupervisedUserBrowserCreatorTest,
-                       StartupSupervisedUserProfile) {
-  StartupBrowserCreator browser_creator;
-
-  // Do a simple non-process-startup browser launch.
-  base::CommandLine dummy(base::CommandLine::NO_PROGRAM);
-  StartupBrowserCreatorImpl launch(base::FilePath(), dummy, &browser_creator,
-                                   chrome::startup::IS_FIRST_RUN);
-  content::WindowedNotificationObserver observer(
-      content::NOTIFICATION_LOAD_STOP,
-      content::NotificationService::AllSources());
-  ASSERT_TRUE(launch.Launch(browser()->profile(), std::vector<GURL>(), false));
-
-  // This should have created a new browser window.
-  Browser* new_browser = FindOneOtherBrowser(browser());
-  ASSERT_TRUE(new_browser);
-
-  TabStripModel* tab_strip = new_browser->tab_strip_model();
-
-  EXPECT_EQ(1, tab_strip->count());
-}
-
 #endif  // !defined(OS_CHROMEOS)
 
 // These tests are not applicable to Chrome OS as neither master_preferences nor
diff --git a/chrome/browser/ui/tabs/tab_group_data.h b/chrome/browser/ui/tabs/tab_group_data.h
index a246f01..11f87437 100644
--- a/chrome/browser/ui/tabs/tab_group_data.h
+++ b/chrome/browser/ui/tabs/tab_group_data.h
@@ -15,6 +15,8 @@
  public:
   // Construct a TabGroupData with placeholder name and random color.
   TabGroupData();
+  TabGroupData(const TabGroupData& other) = default;
+  TabGroupData(TabGroupData&& other) = default;
   ~TabGroupData() = default;
 
   base::string16 title() const { return title_; }
@@ -30,8 +32,6 @@
   base::string16 title_;
   SkColor color_;
   int tab_count_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabGroupData);
 };
 
 #endif  // CHROME_BROWSER_UI_TABS_TAB_GROUP_DATA_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 07be43c..e81c3861 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -780,7 +780,7 @@
 
 const TabGroupData* TabStripModel::GetDataForGroup(TabGroupId group) const {
   DCHECK(base::Contains(group_data_, group));
-  return group_data_.at(group).get();
+  return &group_data_.at(group);
 }
 
 base::Optional<TabGroupId> TabStripModel::GetTabGroupForTab(int index) const {
@@ -1488,7 +1488,7 @@
   }
   data->set_group(group);
   if (group.has_value())
-    group_data_[group.value()]->TabAdded();
+    group_data_[group.value()].TabAdded();
 
   // TODO(gbillock): Ask the modal dialog manager whether the WebContents should
   // be blocked, or just let the modal dialog manager make the blocking call
@@ -1759,7 +1759,7 @@
       contents_data_.cbegin(), contents_data_.cend(),
       [new_group](const auto& datum) { return datum->group() == new_group; }));
 
-  group_data_[new_group] = std::make_unique<TabGroupData>();
+  group_data_.emplace(new_group, TabGroupData());
 
   // Find a destination for the first tab that's not inside another group. We
   // will stack the rest of the tabs up to its right.
@@ -1843,7 +1843,7 @@
     MoveWebContentsAtImpl(index, new_index, false);
   contents_data_[new_index]->set_group(new_group);
   if (new_group.has_value())
-    group_data_[new_group.value()]->TabAdded();
+    group_data_[new_group.value()].TabAdded();
 
   NotifyGroupChange(new_index, old_group, new_group);
 }
@@ -1870,7 +1870,7 @@
     return base::nullopt;
 
   contents_data_[index]->set_group(base::nullopt);
-  TabGroupData* group_data = group_data_[group.value()].get();
+  TabGroupData* group_data = &group_data_[group.value()];
   group_data->TabRemoved();
   // Delete the group if we just ungrouped the last tab in that group.
   if (group_data->empty())
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 056cba0..a8b49ffa 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -34,7 +34,6 @@
 #endif
 
 class Profile;
-class TabGroupData;
 class TabStripModelDelegate;
 
 namespace content {
@@ -325,7 +324,8 @@
   // https://crbug.com/915956).
   base::Optional<TabGroupId> GetTabGroupForTab(int index) const;
 
-  // Returns the TabGroupData instance for the given |group|.
+  // Returns the TabGroupData instance for the given |group|. The returned
+  // pointer is valid until all tabs in |group| are destroyed.
   const TabGroupData* GetDataForGroup(TabGroupId group) const;
 
   // Returns a list of tab groups that contain at least one tab in this strip.
@@ -668,7 +668,7 @@
 
   // The data for tab groups hosted within this TabStripModel, indexed by the
   // group ID.
-  std::map<TabGroupId, std::unique_ptr<TabGroupData>> group_data_;
+  std::map<TabGroupId, TabGroupData> group_data_;
 
   TabStripModelDelegate* delegate_;
 
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index f270fc7..c63acd4 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1327,11 +1327,11 @@
 
 void BrowserView::ShowIntentPickerBubble(
     std::vector<IntentPickerBubbleView::AppInfo> app_info,
-    bool enable_stay_in_chrome,
-    bool show_persistence_options,
+    bool show_stay_in_chrome,
+    bool show_remember_selection,
     IntentPickerResponse callback) {
-  toolbar_->ShowIntentPickerBubble(std::move(app_info), enable_stay_in_chrome,
-                                   show_persistence_options,
+  toolbar_->ShowIntentPickerBubble(std::move(app_info), show_stay_in_chrome,
+                                   show_remember_selection,
                                    std::move(callback));
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 1b4c73e..fd80be425 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -366,8 +366,8 @@
   void ShowUpdateChromeDialog() override;
   void ShowIntentPickerBubble(
       std::vector<IntentPickerBubbleView::AppInfo> app_info,
-      bool enable_stay_in_chrome,
-      bool show_persistence_options,
+      bool show_stay_in_chrome,
+      bool show_remember_selection,
       IntentPickerResponse callback) override;
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index d0aa8a9..f79efbd 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -120,8 +120,8 @@
     views::View* anchor_view,
     content::WebContents* web_contents,
     std::vector<AppInfo> app_info,
-    bool enable_stay_in_chrome,
-    bool show_persistence_options,
+    bool show_stay_in_chrome,
+    bool show_remember_selection,
     IntentPickerResponse intent_picker_cb) {
   if (intent_picker_bubble_) {
     intent_picker_bubble_->Initialize();
@@ -140,7 +140,7 @@
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   intent_picker_bubble_ = new IntentPickerBubbleView(
       std::move(app_info), std::move(intent_picker_cb), web_contents,
-      enable_stay_in_chrome, show_persistence_options);
+      show_stay_in_chrome, show_remember_selection);
   intent_picker_bubble_->set_margins(gfx::Insets());
 
   if (anchor_view) {
@@ -184,13 +184,13 @@
 // static
 std::unique_ptr<IntentPickerBubbleView>
 IntentPickerBubbleView::CreateBubbleView(std::vector<AppInfo> app_info,
-                                         bool enable_stay_in_chrome,
-                                         bool show_persistence_options,
+                                         bool show_stay_in_chrome,
+                                         bool show_remember_selection,
                                          IntentPickerResponse intent_picker_cb,
                                          content::WebContents* web_contents) {
   std::unique_ptr<IntentPickerBubbleView> bubble(new IntentPickerBubbleView(
       std::move(app_info), std::move(intent_picker_cb), web_contents,
-      enable_stay_in_chrome, show_persistence_options));
+      show_stay_in_chrome, show_remember_selection));
   bubble->Initialize();
   return bubble;
 }
@@ -242,7 +242,8 @@
 }
 
 int IntentPickerBubbleView::GetDialogButtons() const {
-  if (show_persistence_options_)
+  // The cancel button here refers to the 'Stay in Chrome' button
+  if (show_stay_in_chrome_)
     return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
   return ui::DIALOG_BUTTON_OK;
 }
@@ -251,13 +252,6 @@
   return l10n_util::GetStringUTF16(IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH);
 }
 
-bool IntentPickerBubbleView::IsDialogButtonEnabled(
-    ui::DialogButton button) const {
-  if (!enable_stay_in_chrome_ && button == ui::DIALOG_BUTTON_CANCEL)
-    return false;
-  return true;
-}
-
 base::string16 IntentPickerBubbleView::GetDialogButtonLabel(
     ui::DialogButton button) const {
   return l10n_util::GetStringUTF16(
@@ -270,16 +264,16 @@
     std::vector<AppInfo> app_info,
     IntentPickerResponse intent_picker_cb,
     content::WebContents* web_contents,
-    bool enable_stay_in_chrome,
-    bool show_persistence_options)
+    bool show_stay_in_chrome,
+    bool show_remember_selection)
     : LocationBarBubbleDelegateView(nullptr /* anchor_view */,
                                     gfx::Point(),
                                     web_contents),
       intent_picker_cb_(std::move(intent_picker_cb)),
       selected_app_tag_(0),
       app_info_(std::move(app_info)),
-      enable_stay_in_chrome_(enable_stay_in_chrome),
-      show_persistence_options_(show_persistence_options) {
+      show_stay_in_chrome_(show_stay_in_chrome),
+      show_remember_selection_(show_remember_selection) {
   chrome::RecordDialogCreation(chrome::DialogIdentifier::INTENT_PICKER);
 }
 
@@ -388,8 +382,7 @@
   scroll_view_ = layout->AddView(std::move(scroll_view));
   layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId, 0);
 
-  // Add Show remember selection checkbox if there are apps besides pwas present
-  if (show_persistence_options_) {
+  if (show_remember_selection_) {
     layout->AddView(CreateHorizontalSeparator());
 
     // This second ColumnSet has a padding column in order to manipulate the
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.h b/chrome/browser/ui/views/intent_picker_bubble_view.h
index 6aae126..327e0fd3 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.h
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.h
@@ -64,13 +64,13 @@
   static views::Widget* ShowBubble(views::View* anchor_view,
                                    content::WebContents* web_contents,
                                    std::vector<AppInfo> app_info,
-                                   bool enable_stay_in_chrome,
-                                   bool show_persistence_options,
+                                   bool show_stay_in_chrome,
+                                   bool show_remember_selection,
                                    IntentPickerResponse intent_picker_cb);
   static std::unique_ptr<IntentPickerBubbleView> CreateBubbleView(
       std::vector<AppInfo> app_info,
-      bool enable_stay_in_chrome,
-      bool show_persistence_options,
+      bool show_stay_in_chrome,
+      bool show_remember_selection,
       IntentPickerResponse intent_picker_cb,
       content::WebContents* web_contents);
   static IntentPickerBubbleView* intent_picker_bubble() {
@@ -90,7 +90,6 @@
  protected:
   // LocationBarBubbleDelegateView overrides:
   base::string16 GetWindowTitle() const override;
-  bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void CloseBubble() override;
 
@@ -108,8 +107,8 @@
   IntentPickerBubbleView(std::vector<AppInfo> app_info,
                          IntentPickerResponse intent_picker_cb,
                          content::WebContents* web_contents,
-                         bool enable_stay_in_chrome,
-                         bool show_persistence_options);
+                         bool show_stay_in_chrome,
+                         bool show_remember_selection);
 
   // views::BubbleDialogDelegateView overrides:
   void OnWidgetDestroying(views::Widget* widget) override;
@@ -174,12 +173,11 @@
 
   views::Checkbox* remember_selection_checkbox_ = nullptr;
 
-  // Tells whether or not 'Stay in Chrome' should be enabled as an option.
-  const bool enable_stay_in_chrome_;
+  // Tells whether 'Stay in Chrome' button should be shown or hidden.
+  const bool show_stay_in_chrome_;
 
-  // Whether 'Remember selection' checkbox and "Stay in chrome" button should be
-  // shown or hidden.
-  const bool show_persistence_options_;
+  // Whether 'Remember my choice' checkbox should be shown or hidden.
+  const bool show_remember_selection_;
 
   DISALLOW_COPY_AND_ASSIGN(IntentPickerBubbleView);
 };
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
index 9389e367..a272f6e 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
@@ -40,7 +40,7 @@
   }
 
  protected:
-  void CreateBubbleView(bool use_icons, bool enable_stay_in_chrome) {
+  void CreateBubbleView(bool use_icons, bool show_stay_in_chrome) {
     // Pushing a couple of fake apps just to check they are created on the UI.
     app_info_.emplace_back(apps::mojom::AppType::kArc, gfx::Image(),
                            "package_1", "dank app 1");
@@ -73,8 +73,8 @@
     }
 
     bubble_ = IntentPickerBubbleView::CreateBubbleView(
-        std::move(app_info), enable_stay_in_chrome,
-        /*show_persistence_options=*/true,
+        std::move(app_info), show_stay_in_chrome,
+        /*show_remember_selection=*/true,
         base::Bind(&IntentPickerBubbleViewTest::OnBubbleClosed,
                    base::Unretained(this)),
         web_contents);
@@ -102,7 +102,7 @@
 
 // Verifies that we didn't set up an image for any LabelButton.
 TEST_F(IntentPickerBubbleViewTest, NullIcons) {
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
   size_t size = bubble_->GetScrollViewSize();
   for (size_t i = 0; i < size; ++i) {
     gfx::ImageSkia image = bubble_->GetAppImageForTesting(i);
@@ -112,7 +112,7 @@
 
 // Verifies that all the icons contain a non-null icon.
 TEST_F(IntentPickerBubbleViewTest, NonNullIcons) {
-  CreateBubbleView(/*use_icons=*/true, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
   size_t size = bubble_->GetScrollViewSize();
   for (size_t i = 0; i < size; ++i) {
     gfx::ImageSkia image = bubble_->GetAppImageForTesting(i);
@@ -125,7 +125,7 @@
 // shown to the user on the picker UI, so there could be a difference
 // represented by |chrome_package_repetitions|.
 TEST_F(IntentPickerBubbleViewTest, LabelsPtrVectorSize) {
-  CreateBubbleView(/*use_icons=*/true, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
   size_t size = app_info_.size();
   size_t chrome_package_repetitions = 0;
   for (const AppInfo& app_info : app_info_) {
@@ -138,7 +138,7 @@
 
 // Verifies the InkDrop state when creating a new bubble.
 TEST_F(IntentPickerBubbleViewTest, VerifyStartingInkDrop) {
-  CreateBubbleView(/*use_icons=*/true, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
   size_t size = bubble_->GetScrollViewSize();
   for (size_t i = 0; i < size; ++i) {
     EXPECT_EQ(bubble_->GetInkDropStateForTesting(i),
@@ -149,7 +149,7 @@
 // Press each button at a time and make sure it goes to ACTIVATED state,
 // followed by HIDDEN state after selecting other button.
 TEST_F(IntentPickerBubbleViewTest, InkDropStateTransition) {
-  CreateBubbleView(/*use_icons=*/true, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
   const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                              ui::EventTimeForNow(), 0, 0);
   size_t size = bubble_->GetScrollViewSize();
@@ -165,7 +165,7 @@
 // Arbitrary press the first button twice, check that the InkDropState remains
 // the same.
 TEST_F(IntentPickerBubbleViewTest, PressButtonTwice) {
-  CreateBubbleView(/*use_icons=*/true, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
   const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                              ui::EventTimeForNow(), 0, 0);
   EXPECT_EQ(bubble_->GetInkDropStateForTesting(0), views::InkDropState::HIDDEN);
@@ -180,7 +180,7 @@
 // Check that none of the app candidates within the picker corresponds to the
 // Chrome browser.
 TEST_F(IntentPickerBubbleViewTest, ChromeNotInCandidates) {
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
   size_t size = bubble_->GetScrollViewSize();
   for (size_t i = 0; i < size; ++i) {
     EXPECT_FALSE(arc::ArcIntentHelperBridge::IsIntentHelperPackage(
@@ -188,21 +188,11 @@
   }
 }
 
-// Check that 'Stay in Chrome' remains enabled/disabled accordingly. For this
-// UI, DIALOG_BUTTON_CANCEL maps to 'Stay in Chrome'.
-TEST_F(IntentPickerBubbleViewTest, StayInChromeTest) {
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/false);
-  EXPECT_EQ(bubble_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL), false);
-
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/true);
-  EXPECT_EQ(bubble_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL), true);
-}
-
 // Check that a non nullptr WebContents() has been created and observed.
 TEST_F(IntentPickerBubbleViewTest, WebContentsTiedToBubble) {
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/false);
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false);
   EXPECT_TRUE(bubble_->web_contents());
 
-  CreateBubbleView(/*use_icons=*/false, /*enable_stay_in_chrome=*/true);
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
   EXPECT_TRUE(bubble_->web_contents());
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index dda1742c..45f5a7d 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -1313,7 +1313,10 @@
   // Also revert if the text has been edited but currently exactly matches
   // the permanent text. An example of this scenario is someone typing on the
   // new tab page and then deleting everything using backspace/delete.
+  //
+  // This should never exit keyword mode.
   if (GetWidget() && GetWidget()->IsActive() && popup_closes_on_blur &&
+      !model()->is_keyword_selected() &&
       ((!model()->user_input_in_progress() &&
         text() != model()->GetPermanentDisplayText()) ||
        (model()->user_input_in_progress() &&
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 c14ec2c3..430da08 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -576,6 +576,23 @@
   EXPECT_TRUE(omnibox_view()->model()->keyword().empty());
 }
 
+TEST_F(OmniboxViewViewsTest, BlurNeverExitsKeywordMode) {
+  location_bar_model()->set_url(GURL());
+  omnibox_view()->model()->ResetDisplayTexts();
+  omnibox_view()->RevertAll();
+
+  // Enter keyword mode, but with no user text.
+  omnibox_view()->model()->EnterKeywordModeForDefaultSearchProvider(
+      OmniboxEventProto::KEYBOARD_SHORTCUT);
+  EXPECT_TRUE(omnibox_view()->text().empty());
+  EXPECT_FALSE(omnibox_view()->model()->keyword().empty());
+
+  // Expect that on blur, stay in keyword mode.
+  omnibox_textfield()->OnBlur();
+  EXPECT_TRUE(omnibox_view()->text().empty());
+  EXPECT_FALSE(omnibox_view()->model()->keyword().empty());
+}
+
 TEST_F(OmniboxViewViewsTest, PasteAndGoToUrlOrSearchCommand) {
   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
   ui::ClipboardType clipboard_type = ui::ClipboardType::kCopyPaste;
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc
index b6bef992..215f27d 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/passwords/password_items_view.h"
 
 #include <numeric>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -125,7 +126,7 @@
   });
   bool display_arrow = !usernames.empty();
   auto combobox = std::make_unique<views::EditableCombobox>(
-      std::make_unique<ui::SimpleComboboxModel>(usernames),
+      std::make_unique<ui::SimpleComboboxModel>(std::move(usernames)),
       /*filter_on_edit=*/false, /*show_on_empty=*/true,
       views::EditableCombobox::Type::kRegular, views::style::CONTEXT_BUTTON,
       views::style::STYLE_PRIMARY, display_arrow);
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc
index fb92ff9..3b21bee 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.cc
+++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -187,7 +187,7 @@
   });
   bool display_arrow = !passwords.empty();
   auto combobox = std::make_unique<views::EditableCombobox>(
-      std::make_unique<ui::SimpleComboboxModel>(passwords),
+      std::make_unique<ui::SimpleComboboxModel>(std::move(passwords)),
       /*filter_on_edit=*/false, /*show_on_empty=*/true,
       views::EditableCombobox::Type::kPassword, views::style::CONTEXT_BUTTON,
       STYLE_PRIMARY_MONOSPACED, display_arrow);
diff --git a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.cc b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.cc
index 159fa3a7..f968b096 100644
--- a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.cc
+++ b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.cc
@@ -22,7 +22,6 @@
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/link.h"
-#include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 
@@ -110,12 +109,6 @@
   controller_->OnHelpTextClicked();
 }
 
-void ClickToCallDialogView::StyledLabelLinkClicked(views::StyledLabel* label,
-                                                   const gfx::Range& range,
-                                                   int event_flags) {
-  controller_->OnHelpTextClicked();
-}
-
 void ClickToCallDialogView::Init() {
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
@@ -170,9 +163,14 @@
 }
 
 void ClickToCallDialogView::InitListView() {
+  LogClickToCallDialogShown(
+      devices_.empty()
+          ? SharingClickToCallDialogType::kDialogWithoutDevicesWithApp
+          : SharingClickToCallDialogType::kDialogWithDevicesMaybeApps);
+
   int tag = 0;
   // Devices:
-  LogClickToCallDevicesToShow(devices_.size());
+  LogClickToCallDevicesToShow(kSharingClickToCallUiDialog, devices_.size());
   for (const auto& device : devices_) {
     auto dialog_button = std::make_unique<HoverButton>(
         this, CreateDeviceIcon(device.device_type()),
@@ -183,7 +181,7 @@
   }
 
   // Apps:
-  LogClickToCallAppsToShow(apps_.size());
+  LogClickToCallAppsToShow(kSharingClickToCallUiDialog, apps_.size());
   for (const auto& app : apps_) {
     auto dialog_button =
         std::make_unique<HoverButton>(this, CreateIconView(app.icon), app.name,
@@ -195,6 +193,8 @@
 }
 
 void ClickToCallDialogView::InitEmptyView() {
+  LogClickToCallDialogShown(SharingClickToCallDialogType::kEducationalDialog);
+
   auto label = std::make_unique<views::Label>(
       l10n_util::GetStringUTF16(
           IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_HELP_TEXT_NO_DEVICES),
@@ -225,17 +225,13 @@
 }
 
 void ClickToCallDialogView::InitErrorView() {
-  const base::string16 link = l10n_util::GetStringUTF16(
-      IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TROUBLESHOOT_LINK);
-  size_t offset;
-  const base::string16 text = l10n_util::GetStringFUTF16(
-      IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE, link, &offset);
-  auto label = std::make_unique<views::StyledLabel>(text, this);
+  LogClickToCallDialogShown(SharingClickToCallDialogType::kErrorDialog);
 
-  views::StyledLabel::RangeStyleInfo link_style =
-      views::StyledLabel::RangeStyleInfo::CreateForLink();
-  label->AddStyleRange(gfx::Range(offset, offset + link.length()), link_style);
-
+  auto label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(
+          IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE),
+      views::style::CONTEXT_LABEL, ChromeTextStyle::STYLE_SECONDARY);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   AddChildView(std::move(label));
 }
 
@@ -262,5 +258,5 @@
 }
 
 void ClickToCallDialogView::WindowClosing() {
-  controller_->OnDialogClosed();
+  controller_->OnDialogClosed(this);
 }
diff --git a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.h b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.h
index 7dd4985..7e0a139 100644
--- a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.h
+++ b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_dialog_view.h
@@ -14,11 +14,9 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/link_listener.h"
-#include "ui/views/controls/styled_label_listener.h"
 
 namespace views {
 class Link;
-class StyledLabel;
 class View;
 }  // namespace views
 
@@ -28,7 +26,6 @@
 class ClickToCallDialogView : public ClickToCallDialog,
                               public views::ButtonListener,
                               public views::LinkListener,
-                              public views::StyledLabelListener,
                               public LocationBarBubbleDelegateView {
  public:
   // Bubble will be anchored to |anchor_view|.
@@ -49,11 +46,6 @@
   // views::LinkListener:
   void LinkClicked(views::Link* source, int event_flags) override;
 
-  // views::StyledLabelListener:
-  void StyledLabelLinkClicked(views::StyledLabel* label,
-                              const gfx::Range& range,
-                              int event_flags) override;
-
   // views::DialogDelegate:
   int GetDialogButtons() const override;
   std::unique_ptr<views::View> CreateFootnoteView() override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 53845f7..f4d0f272 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -401,8 +401,8 @@
 
 void ToolbarView::ShowIntentPickerBubble(
     std::vector<IntentPickerBubbleView::AppInfo> app_info,
-    bool enable_stay_in_chrome,
-    bool show_persistence_options,
+    bool show_stay_in_chrome,
+    bool show_remember_selection,
     IntentPickerResponse callback) {
   PageActionIconView* intent_picker_view =
       location_bar()
@@ -413,7 +413,7 @@
       IntentPickerTabHelper::SetShouldShowIcon(GetWebContents(), true);
     IntentPickerBubbleView::ShowBubble(
         intent_picker_view, GetWebContents(), std::move(app_info),
-        enable_stay_in_chrome, show_persistence_options, std::move(callback));
+        show_stay_in_chrome, show_remember_selection, std::move(callback));
   }
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index ed67a74..307dc295 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -117,8 +117,8 @@
 
   void ShowIntentPickerBubble(
       std::vector<IntentPickerBubbleView::AppInfo> app_info,
-      bool enable_stay_in_chrome,
-      bool show_persistence_options,
+      bool show_stay_in_chrome,
+      bool show_remember_selection,
       IntentPickerResponse callback);
 
   // Shows a bookmark bubble and anchors it appropriately.
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 04a95253..8df423bb 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
@@ -10,11 +10,11 @@
 #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.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.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/mojom/types.mojom.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 6a3bd9f..389bc5d 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -153,7 +153,7 @@
 std::unique_ptr<base::DictionaryValue> HistoryEntryToValue(
     const BrowsingHistoryService::HistoryEntry& entry,
     BookmarkModel* bookmark_model,
-    SupervisedUserService* supervised_user_service,
+    Profile* profile,
     const syncer::DeviceInfoTracker* tracker,
     base::Clock* clock) {
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
@@ -223,6 +223,11 @@
   result->SetString("deviceType", device_type);
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+  SupervisedUserService* supervised_user_service = nullptr;
+  if (profile->IsSupervised()) {
+    supervised_user_service =
+        SupervisedUserServiceFactory::GetForProfile(profile);
+  }
   if (supervised_user_service) {
     const SupervisedUserURLFilter* url_filter =
         supervised_user_service->GetURLFilter();
@@ -375,12 +380,6 @@
   Profile* profile = Profile::FromWebUI(web_ui());
   BookmarkModel* bookmark_model =
       BookmarkModelFactory::GetForBrowserContext(profile);
-  SupervisedUserService* supervised_user_service = NULL;
-#if defined(ENABLE_SUPERVISED_USERS)
-  if (profile->IsSupervised())
-    supervised_user_service =
-        SupervisedUserServiceFactory::GetForProfile(profile);
-#endif
 
   const syncer::DeviceInfoTracker* tracker =
       DeviceInfoSyncServiceFactory::GetForProfile(profile)
@@ -390,8 +389,8 @@
   DCHECK(tracker);
   base::ListValue results_value;
   for (const BrowsingHistoryService::HistoryEntry& entry : results) {
-    std::unique_ptr<base::Value> value(HistoryEntryToValue(
-        entry, bookmark_model, supervised_user_service, tracker, clock_));
+    std::unique_ptr<base::Value> value(
+        HistoryEntryToValue(entry, bookmark_model, profile, tracker, clock_));
     results_value.Append(std::move(value));
   }
 
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 734a3e5..8f6d49bf 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -58,7 +58,6 @@
 #include "chrome/browser/ui/webui/settings/settings_ui.h"
 #include "chrome/browser/ui/webui/settings_utils.h"
 #include "chrome/browser/ui/webui/signin_internals_ui.h"
-#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
 #include "chrome/browser/ui/webui/sync_internals_ui.h"
 #include "chrome/browser/ui/webui/translate_internals/translate_internals_ui.h"
 #include "chrome/browser/ui/webui/ukm/ukm_internals_ui.h"
@@ -236,6 +235,10 @@
 #include "chrome/browser/ui/webui/reset_password/reset_password_ui.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
+#endif
+
 using content::WebUI;
 using content::WebUIController;
 using ui::WebDialogUI;
@@ -403,8 +406,6 @@
     return &NewWebUI<SignInInternalsUI>;
   if (url.host_piece() == chrome::kChromeUISuggestionsHost)
     return &NewWebUI<suggestions::SuggestionsUI>;
-  if (url.host_piece() == chrome::kChromeUISupervisedUserInternalsHost)
-    return &NewWebUI<SupervisedUserInternalsUI>;
   if (url.host_piece() == chrome::kChromeUISupervisedUserPassphrasePageHost)
     return &NewWebUI<ConstrainedWebDialogUI>;
   if (url.host_piece() == chrome::kChromeUISyncInternalsHost)
@@ -718,6 +719,11 @@
   }
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+  if (url.host_piece() == chrome::kChromeUISupervisedUserInternalsHost)
+    return &NewWebUI<SupervisedUserInternalsUI>;
+#endif
+
   return NULL;
 }
 
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 450ea8da..0acab1a 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
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/stl_util.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
@@ -18,7 +19,6 @@
 #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"
-#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "components/signin/public/identity_manager/access_token_fetcher.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index 8230958ad3..2e65bdf 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -22,6 +22,7 @@
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
 #include "components/arc/arc_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_ui.h"
@@ -118,6 +119,12 @@
 void AssistantOptInDialog::Show(
     ash::FlowType type,
     ash::AssistantSetup::StartAssistantOptInFlowCallback callback) {
+  // Check session state here to prevent timing issue -- session state might
+  // have changed during the mojom calls to launch the opt-in dalog.
+  if (session_manager::SessionManager::Get()->session_state() !=
+      session_manager::SessionState::ACTIVE) {
+    return;
+  }
   if (g_dialog) {
     g_dialog->Focus();
     std::move(callback).Run(false);
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
index 981076b..36ab5f6 100644
--- a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
@@ -23,6 +23,9 @@
     {"finish", IDS_CELLULAR_SETUP_FINISH_LABEL},
     {"tryAgain", IDS_CELLULAR_SETUP_TRY_AGAIN_LABEL},
     {"simDetectPageTitle", IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_TITLE},
+    {"simDetectPageErrorTitle", IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_ERROR_TITLE},
+    {"simDetectPageErrorMessage",
+     IDS_CELLULAR_SETUP_SIM_DETECT_PAGE_ERROR_MESSAGE},
     {"provisioningPageTitle", IDS_CELLULAR_SETUP_PROVISIONING_PAGE_TITLE},
     {"finalPageTitle", IDS_CELLULAR_SETUP_FINAL_PAGE_TITLE},
     {"finalPageMessage", IDS_CELLULAR_SETUP_FINAL_PAGE_MESSAGE},
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
index adca3a6..e46a6ea 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
@@ -9,8 +9,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -22,17 +20,15 @@
 namespace chromeos {
 
 ConfirmPasswordChangeHandler::ConfirmPasswordChangeHandler() {
-  auto* in_session_password_change_manager =
-      g_browser_process->platform_part()->in_session_password_change_manager();
-  CHECK(in_session_password_change_manager);
-  in_session_password_change_manager->AddObserver(this);
+  if (InSessionPasswordChangeManager::IsInitialized()) {
+    InSessionPasswordChangeManager::Get()->AddObserver(this);
+  }
 }
 
 ConfirmPasswordChangeHandler::~ConfirmPasswordChangeHandler() {
-  auto* in_session_password_change_manager =
-      g_browser_process->platform_part()->in_session_password_change_manager();
-  CHECK(in_session_password_change_manager);
-  in_session_password_change_manager->RemoveObserver(this);
+  if (InSessionPasswordChangeManager::IsInitialized()) {
+    InSessionPasswordChangeManager::Get()->RemoveObserver(this);
+  }
 }
 
 void ConfirmPasswordChangeHandler::OnEvent(
@@ -48,11 +44,8 @@
     const base::ListValue* params) {
   const std::string old_password = params->GetList()[0].GetString();
   const std::string new_password = params->GetList()[1].GetString();
-  auto* in_session_password_change_manager =
-      g_browser_process->platform_part()->in_session_password_change_manager();
-  CHECK(in_session_password_change_manager);
-  in_session_password_change_manager->ChangePassword(old_password,
-                                                     new_password);
+  InSessionPasswordChangeManager::Get()->ChangePassword(old_password,
+                                                        new_password);
 }
 
 void ConfirmPasswordChangeHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
index 01f87b4..fa57cf5d 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
@@ -9,8 +9,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/login/auth/chrome_cryptohome_authenticator.h"
 #include "chrome/browser/chromeos/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -60,11 +58,8 @@
                                        ? new_passwords.GetList()[0].GetString()
                                        : "";
 
-  auto* in_session_password_change_manager =
-      g_browser_process->platform_part()->in_session_password_change_manager();
-  CHECK(in_session_password_change_manager);
-  in_session_password_change_manager->OnSamlPasswordChanged(old_password,
-                                                            new_password);
+  InSessionPasswordChangeManager::Get()->OnSamlPasswordChanged(old_password,
+                                                               new_password);
 }
 
 void PasswordChangeHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/urgent_password_expiry_notification_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/urgent_password_expiry_notification_handler.cc
index fa3506f..627e157 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/urgent_password_expiry_notification_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/urgent_password_expiry_notification_handler.cc
@@ -9,8 +9,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/chromeos/login/saml/password_expiry_notification.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -30,10 +28,7 @@
 
 void UrgentPasswordExpiryNotificationHandler::HandleContinue(
     const base::ListValue* params) {
-  auto* in_session_password_change_manager =
-      g_browser_process->platform_part()->in_session_password_change_manager();
-  CHECK(in_session_password_change_manager);
-  in_session_password_change_manager->StartInSessionPasswordChange();
+  InSessionPasswordChangeManager::Get()->StartInSessionPasswordChange();
 }
 
 void UrgentPasswordExpiryNotificationHandler::HandleGetTitleText(
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 54b787d4..eca0146a 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -1305,7 +1305,10 @@
 void SigninScreenHandler::HandleFocusPod(const AccountId& account_id,
                                          bool is_large_pod) {
   proximity_auth::ScreenlockBridge::Get()->SetFocusedUser(account_id);
-  if (delegate_)
+  const bool is_same_pod_focused =
+      focused_pod_account_id_ && *focused_pod_account_id_ == account_id;
+
+  if (delegate_ && !is_same_pod_focused)
     delegate_->CheckUserStatus(account_id);
   if (!test_focus_pod_callback_.is_null())
     test_focus_pod_callback_.Run();
@@ -1317,21 +1320,26 @@
   // |user| may be nullptr in kiosk mode or unit tests.
   if (user && user->is_logged_in() && !user->is_active()) {
     SessionControllerClientImpl::DoSwitchActiveUser(account_id);
-  } else {
-    lock_screen_utils::SetUserInputMethod(account_id.GetUserEmail(),
-                                          ime_state_.get());
-    lock_screen_utils::SetKeyboardSettings(account_id);
-    if (LoginDisplayHost::default_host() && is_large_pod)
-      LoginDisplayHost::default_host()->LoadWallpaper(account_id);
+    return;
+  }
 
-    bool use_24hour_clock = false;
-    if (user_manager::known_user::GetBooleanPref(
-            account_id, prefs::kUse24HourClock, &use_24hour_clock)) {
-      g_browser_process->platform_part()
-          ->GetSystemClock()
-          ->SetLastFocusedPodHourClockType(
-              use_24hour_clock ? base::k24HourClock : base::k12HourClock);
-    }
+  if (LoginDisplayHost::default_host() && is_large_pod)
+    LoginDisplayHost::default_host()->LoadWallpaper(account_id);
+
+  if (is_same_pod_focused)
+    return;
+
+  lock_screen_utils::SetUserInputMethod(account_id.GetUserEmail(),
+                                        ime_state_.get());
+  lock_screen_utils::SetKeyboardSettings(account_id);
+
+  bool use_24hour_clock = false;
+  if (user_manager::known_user::GetBooleanPref(
+          account_id, prefs::kUse24HourClock, &use_24hour_clock)) {
+    g_browser_process->platform_part()
+        ->GetSystemClock()
+        ->SetLastFocusedPodHourClockType(use_24hour_clock ? base::k24HourClock
+                                                          : base::k12HourClock);
   }
 }
 
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index 4fbf410..1a5607b 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
 #include "chrome/browser/ssl/mitm_software_blocking_page.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
-#include "chrome/browser/supervised_user/supervised_user_interstitial.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/url_constants.h"
 #include "components/grit/components_resources.h"
diff --git a/chrome/browser/ui/webui/omnibox/omnibox.mojom b/chrome/browser/ui/webui/omnibox/omnibox.mojom
index de8253d..0f5dbb70 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox.mojom
+++ b/chrome/browser/ui/webui/omnibox/omnibox.mojom
@@ -59,6 +59,7 @@
   string type;
   string host;
   bool is_typed_host;
+  string input_text;
   array<AutocompleteMatch> combined_results;
   array<AutocompleteResultsForProvider> results_by_provider;
 };
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
index 97def4a..0c1f86f6 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -254,6 +254,7 @@
   if (!LookupIsTypedHost(host, &is_typed_host))
     is_typed_host = false;
   response->is_typed_host = is_typed_host;
+  response->input_text = base::UTF16ToUTF8(input_.text());
 
   {
     // Copy to an ACMatches to make conversion easier. Since this isn't
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_ui.cc b/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
index 7ad3bb9..af3ee72 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/omnibox/omnibox_page_handler.h"
+#include "chrome/browser/ui/webui/version_ui.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "content/public/browser/web_ui.h"
@@ -19,6 +20,11 @@
   // Set up the chrome://omnibox/ source.
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUIOmniboxHost);
+
+  // Expose version information to client because it is useful in output.
+  VersionUI::AddVersionDetailStrings(source);
+  source->SetJsonPath("strings.js");
+
   source->AddResourcePath("omnibox.css", IDR_OMNIBOX_CSS);
   source->AddResourcePath("omnibox_input.css", IDR_OMNIBOX_INPUT_CSS);
   source->AddResourcePath("output_results_group.css",
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
index c544f08..fdf98ee 100644
--- a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
@@ -58,6 +58,11 @@
   CHECK_EQ(1U, args->GetSize());
   const std::string& callback_id = args->GetList()[0].GetString();
 
+  if (!KerberosCredentialsManager::Get().IsKerberosEnabled()) {
+    ResolveJavascriptCallback(base::Value(callback_id), base::Value());
+    return;
+  }
+
   KerberosCredentialsManager::Get().ListAccounts(
       base::BindOnce(&KerberosAccountsHandler::OnListAccounts,
                      weak_factory_.GetWeakPtr(), callback_id));
@@ -103,7 +108,7 @@
     accounts.GetList().push_back(std::move(account_dict));
   }
 
-  ResolveJavascriptCallback(base::Value(callback_id), accounts);
+  ResolveJavascriptCallback(base::Value(callback_id), std::move(accounts));
 }
 
 void KerberosAccountsHandler::HandleAddKerberosAccount(
@@ -122,6 +127,12 @@
   const std::string& config = args->GetList()[4].GetString();
   const bool allow_existing = args->GetList()[5].GetBool();
 
+  if (!KerberosCredentialsManager::Get().IsKerberosEnabled()) {
+    ResolveJavascriptCallback(base::Value(callback_id),
+                              base::Value(kerberos::ERROR_KERBEROS_DISABLED));
+    return;
+  }
+
   KerberosCredentialsManager::Get().AddAccountAndAuthenticate(
       principal_name, false /* is_managed */, password, remember_password,
       config, allow_existing,
@@ -144,6 +155,12 @@
   const std::string& callback_id = args->GetList()[0].GetString();
   const std::string& principal_name = args->GetList()[1].GetString();
 
+  if (!KerberosCredentialsManager::Get().IsKerberosEnabled()) {
+    ResolveJavascriptCallback(base::Value(callback_id),
+                              base::Value(kerberos::ERROR_KERBEROS_DISABLED));
+    return;
+  }
+
   KerberosCredentialsManager::Get().RemoveAccount(
       principal_name, base::BindOnce(&KerberosAccountsHandler::OnRemoveAccount,
                                      weak_factory_.GetWeakPtr(), callback_id));
@@ -163,6 +180,12 @@
   const std::string& callback_id = args->GetList()[0].GetString();
   const std::string& krb5conf = args->GetList()[1].GetString();
 
+  if (!KerberosCredentialsManager::Get().IsKerberosEnabled()) {
+    ResolveJavascriptCallback(base::Value(callback_id),
+                              base::Value(kerberos::ERROR_KERBEROS_DISABLED));
+    return;
+  }
+
   KerberosCredentialsManager::Get().ValidateConfig(
       krb5conf, base::BindOnce(&KerberosAccountsHandler::OnValidateConfig,
                                weak_factory_.GetWeakPtr(), callback_id));
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 851bdac..b76ceac4 100644
--- a/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/values.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
@@ -15,7 +16,6 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.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/services/app_service/public/mojom/types.mojom.h"
 #include "components/arc/arc_util.h"
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 5429506..9dc2fca 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -615,7 +615,7 @@
   html_source->AddString(
       "androidAppsSubtext",
       l10n_util::GetStringFUTF16(
-          IDS_SETTINGS_ANDROID_APPS_SUBTEXT,
+          IDS_SETTINGS_ANDROID_APPS_SUBTEXT, ui::GetChromeOSDeviceName(),
           GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL)));
 }
 
@@ -2486,6 +2486,7 @@
     {"osSearchPageTitle", is_assistant_allowed
                               ? IDS_SETTINGS_SEARCH_AND_ASSISTANT
                               : IDS_SETTINGS_SEARCH},
+    {"osSearchEngineLabel", IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL},
     {"searchGoogleAssistant", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT},
     {"searchGoogleAssistantEnabled",
      IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ENABLED},
@@ -2500,6 +2501,9 @@
       base::ASCIIToUTF16(chrome::kOmniboxLearnMoreURL));
   html_source->AddString("searchExplanation", search_explanation_text);
 #if defined(OS_CHROMEOS)
+  html_source->AddString(
+      "osSearchEngineTooltip",
+      ui::SubstituteChromeOSDeviceType(IDS_OS_SETTINGS_SEARCH_ENGINE_TOOLTIP));
   html_source->AddBoolean("isAssistantAllowed", is_assistant_allowed);
 #endif
 }
@@ -3062,10 +3066,6 @@
       {"multideviceAndroidMessagesItemTitle",
        IDS_SETTINGS_MULTIDEVICE_ANDROID_MESSAGES},
       {"multideviceForgetDevice", IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE},
-      {"multideviceForgetDeviceSummary",
-       IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE_EXPLANATION},
-      {"multideviceForgetDeviceDialogMessage",
-       IDS_SETTINGS_MULTIDEVICE_FORGET_DEVICE_DIALOG_MESSAGE},
       {"multideviceSmartLockOptions",
        IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOCK},
   };
@@ -3073,6 +3073,14 @@
                           base::size(kLocalizedStrings));
 
   html_source->AddString(
+      "multideviceForgetDeviceSummary",
+      ui::SubstituteChromeOSDeviceType(
+          IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE_EXPLANATION));
+  html_source->AddString(
+      "multideviceForgetDeviceDialogMessage",
+      ui::SubstituteChromeOSDeviceType(
+          IDS_SETTINGS_MULTIDEVICE_FORGET_DEVICE_DIALOG_MESSAGE));
+  html_source->AddString(
       "multideviceVerificationText",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_MULTIDEVICE_VERIFICATION_TEXT,
@@ -3083,7 +3091,7 @@
   html_source->AddString(
       "multideviceSetupSummary",
       l10n_util::GetStringFUTF16(
-          IDS_SETTINGS_MULTIDEVICE_SETUP_SUMMARY,
+          IDS_SETTINGS_MULTIDEVICE_SETUP_SUMMARY, ui::GetChromeOSDeviceName(),
           base::UTF8ToUTF16(
               chromeos::multidevice_setup::
                   GetBoardSpecificBetterTogetherSuiteLearnMoreUrl()
@@ -3100,6 +3108,7 @@
       "multideviceAndroidMessagesItemSummary",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_MULTIDEVICE_ANDROID_MESSAGES_SUMMARY,
+          ui::GetChromeOSDeviceName(),
           base::UTF8ToUTF16(chromeos::multidevice_setup::
                                 GetBoardSpecificMessagesLearnMoreUrl()
                                     .spec())));
@@ -3107,6 +3116,7 @@
       "multideviceSmartLockItemSummary",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_MULTIDEVICE_SMART_LOCK_SUMMARY,
+          ui::GetChromeOSDeviceName(),
           GetHelpUrlWithBoard(chrome::kEasyUnlockLearnMoreUrl)));
 }
 #endif
diff --git a/chrome/browser/ui/webui/version_ui.cc b/chrome/browser/ui/webui/version_ui.cc
index 8127a54..e0759744e 100644
--- a/chrome/browser/ui/webui/version_ui.cc
+++ b/chrome/browser/ui/webui/version_ui.cc
@@ -55,8 +55,7 @@
 WebUIDataSource* CreateVersionUIDataSource() {
   WebUIDataSource* html_source =
       WebUIDataSource::Create(chrome::kChromeUIVersionHost);
-
-  // Localized strings.
+  // These localized strings are used to label version details.
   static constexpr LocalizedString kStrings[] = {
     {version_ui::kTitle, IDS_VERSION_UI_TITLE},
     {version_ui::kApplicationLabel, IDS_PRODUCT_NAME},
@@ -82,6 +81,42 @@
   };
   AddLocalizedStringsBulk(html_source, kStrings, base::size(kStrings));
 
+  VersionUI::AddVersionDetailStrings(html_source);
+
+  html_source->SetJsonPath("strings.js");
+  html_source->AddResourcePath(version_ui::kVersionJS, IDR_VERSION_UI_JS);
+  html_source->AddResourcePath(version_ui::kAboutVersionCSS,
+                               IDR_VERSION_UI_CSS);
+  html_source->SetDefaultResource(IDR_VERSION_UI_HTML);
+  return html_source;
+}
+
+}  // namespace
+
+VersionUI::VersionUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  Profile* profile = Profile::FromWebUI(web_ui);
+
+#if defined(OS_CHROMEOS)
+  web_ui->AddMessageHandler(std::make_unique<VersionHandlerChromeOS>());
+#elif defined(OS_WIN)
+  web_ui->AddMessageHandler(std::make_unique<VersionHandlerWindows>());
+#else
+  web_ui->AddMessageHandler(std::make_unique<VersionHandler>());
+#endif
+
+#if !defined(OS_ANDROID)
+  // Set up the chrome://theme/ source.
+  content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
+#endif
+
+  WebUIDataSource::Add(profile, CreateVersionUIDataSource());
+}
+
+VersionUI::~VersionUI() {}
+
+// static
+void VersionUI::AddVersionDetailStrings(content::WebUIDataSource* html_source) {
   html_source->AddLocalizedString(version_ui::kOfficial,
                                   version_info::IsOfficialBuild()
                                       ? IDS_VERSION_UI_OFFICIAL
@@ -150,35 +185,4 @@
 #endif  // defined(OS_WIN)
 
   html_source->AddString(version_ui::kSanitizer, version_info::GetSanitizerList());
-
-  html_source->SetJsonPath("strings.js");
-  html_source->AddResourcePath(version_ui::kVersionJS, IDR_VERSION_UI_JS);
-  html_source->AddResourcePath(version_ui::kAboutVersionCSS,
-                               IDR_VERSION_UI_CSS);
-  html_source->SetDefaultResource(IDR_VERSION_UI_HTML);
-  return html_source;
 }
-
-}  // namespace
-
-VersionUI::VersionUI(content::WebUI* web_ui)
-    : content::WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-
-#if defined(OS_CHROMEOS)
-  web_ui->AddMessageHandler(std::make_unique<VersionHandlerChromeOS>());
-#elif defined(OS_WIN)
-  web_ui->AddMessageHandler(std::make_unique<VersionHandlerWindows>());
-#else
-  web_ui->AddMessageHandler(std::make_unique<VersionHandler>());
-#endif
-
-#if !defined(OS_ANDROID)
-  // Set up the chrome://theme/ source.
-  content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
-#endif
-
-  WebUIDataSource::Add(profile, CreateVersionUIDataSource());
-}
-
-VersionUI::~VersionUI() {}
diff --git a/chrome/browser/ui/webui/version_ui.h b/chrome/browser/ui/webui/version_ui.h
index 9de2301..1b0cc1a 100644
--- a/chrome/browser/ui/webui/version_ui.h
+++ b/chrome/browser/ui/webui/version_ui.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
 
 // The WebUI handler for chrome://version.
 class VersionUI : public content::WebUIController {
@@ -14,6 +15,10 @@
   explicit VersionUI(content::WebUI* web_ui);
   ~VersionUI() override;
 
+  // Loads a data source with many named details comprising version info.
+  // The keys are from version_ui_constants.
+  static void AddVersionDetailStrings(content::WebUIDataSource* html_source);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(VersionUI);
 };
diff --git a/chrome/browser/ui/webui/welcome/nux/DEPS b/chrome/browser/ui/webui/welcome/DEPS
similarity index 100%
rename from chrome/browser/ui/webui/welcome/nux/DEPS
rename to chrome/browser/ui/webui/welcome/DEPS
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc b/chrome/browser/ui/webui/welcome/bookmark_handler.cc
similarity index 95%
rename from chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc
rename to chrome/browser/ui/webui/welcome/bookmark_handler.cc
index e09ec04f..1899ba5 100644
--- a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.cc
+++ b/chrome/browser/ui/webui/welcome/bookmark_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/ui/webui/welcome/nux/bookmark_handler.h"
+#include "chrome/browser/ui/webui/welcome/bookmark_handler.h"
 
 #include "base/bind.h"
 #include "chrome/grit/browser_resources.h"
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.h b/chrome/browser/ui/webui/welcome/bookmark_handler.h
similarity index 80%
rename from chrome/browser/ui/webui/welcome/nux/bookmark_handler.h
rename to chrome/browser/ui/webui/welcome/bookmark_handler.h
index b9240c9..927d252 100644
--- a/chrome/browser/ui/webui/welcome/nux/bookmark_handler.h
+++ b/chrome/browser/ui/webui/welcome/bookmark_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_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_HANDLER_H_
 
 #include "base/macros.h"
 #include "base/values.h"
@@ -34,4 +34,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_HANDLER_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_item.cc b/chrome/browser/ui/webui/welcome/bookmark_item.cc
similarity index 94%
rename from chrome/browser/ui/webui/welcome/nux/bookmark_item.cc
rename to chrome/browser/ui/webui/welcome/bookmark_item.cc
index ba59a85..3cebd7c 100644
--- a/chrome/browser/ui/webui/welcome/nux/bookmark_item.cc
+++ b/chrome/browser/ui/webui/welcome/bookmark_item.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/ui/webui/welcome/nux/bookmark_item.h"
+#include "chrome/browser/ui/webui/welcome/bookmark_item.h"
 
 #include "base/strings/string16.h"
 #include "base/values.h"
diff --git a/chrome/browser/ui/webui/welcome/nux/bookmark_item.h b/chrome/browser/ui/webui/welcome/bookmark_item.h
similarity index 79%
rename from chrome/browser/ui/webui/welcome/nux/bookmark_item.h
rename to chrome/browser/ui/webui/welcome/bookmark_item.h
index 838735a9..e7bfa51c 100644
--- a/chrome/browser/ui/webui/welcome/nux/bookmark_item.h
+++ b/chrome/browser/ui/webui/welcome/bookmark_item.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_UI_WEBUI_WELCOME_NUX_BOOKMARK_ITEM_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_ITEM_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_ITEM_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_ITEM_H_
 
 #include <stddef.h>
 #include <string>
@@ -31,4 +31,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_BOOKMARK_ITEM_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_BOOKMARK_ITEM_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/constants.cc b/chrome/browser/ui/webui/welcome/constants.cc
similarity index 94%
rename from chrome/browser/ui/webui/welcome/nux/constants.cc
rename to chrome/browser/ui/webui/welcome/constants.cc
index ac31489..d6e5606 100644
--- a/chrome/browser/ui/webui/welcome/nux/constants.cc
+++ b/chrome/browser/ui/webui/welcome/constants.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/ui/webui/welcome/nux/constants.h"
+#include "chrome/browser/ui/webui/welcome/constants.h"
 
 #include "base/feature_list.h"
 
diff --git a/chrome/browser/ui/webui/welcome/nux/constants.h b/chrome/browser/ui/webui/welcome/constants.h
similarity index 79%
rename from chrome/browser/ui/webui/welcome/nux/constants.h
rename to chrome/browser/ui/webui/welcome/constants.h
index de397b2..d0e4b96 100644
--- a/chrome/browser/ui/webui/welcome/nux/constants.h
+++ b/chrome/browser/ui/webui/welcome/constants.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_UI_WEBUI_WELCOME_NUX_CONSTANTS_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_CONSTANTS_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_CONSTANTS_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_CONSTANTS_H_
 
 #include <string>
 #include "base/metrics/field_trial_params.h"
@@ -25,4 +25,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_CONSTANTS_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_CONSTANTS_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc b/chrome/browser/ui/webui/welcome/google_apps_handler.cc
similarity index 96%
rename from chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
rename to chrome/browser/ui/webui/welcome/google_apps_handler.cc
index b76a818..74e30884a 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
+++ b/chrome/browser/ui/webui/welcome/google_apps_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/ui/webui/welcome/nux/google_apps_handler.h"
+#include "chrome/browser/ui/webui/welcome/google_apps_handler.h"
 
 #include "base/bind.h"
 #include "base/metrics/field_trial_params.h"
@@ -10,7 +10,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/welcome/nux/bookmark_item.h"
+#include "chrome/browser/ui/webui/welcome/bookmark_item.h"
 #include "chrome/browser/ui/webui/welcome/nux_helper.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/generated_resources.h"
@@ -109,7 +109,7 @@
   int appId;
   args->GetInteger(0, &appId);
 
-  const BookmarkItem* selectedApp = NULL;
+  const BookmarkItem* selectedApp = nullptr;
   for (const auto& google_app : google_apps_) {
     if (google_app.id == appId) {
       selectedApp = &google_app;
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h b/chrome/browser/ui/webui/welcome/google_apps_handler.h
similarity index 79%
rename from chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
rename to chrome/browser/ui/webui/welcome/google_apps_handler.h
index f35e22d4..caaaef1 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
+++ b/chrome/browser/ui/webui/welcome/google_apps_handler.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 CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_GOOGLE_APPS_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_GOOGLE_APPS_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_GOOGLE_APPS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_GOOGLE_APPS_HANDLER_H_
 
 #include <vector>
 
 #include "base/macros.h"
 #include "base/values.h"
-#include "chrome/browser/ui/webui/welcome/nux/bookmark_item.h"
+#include "chrome/browser/ui/webui/welcome/bookmark_item.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace nux {
@@ -45,4 +45,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_GOOGLE_APPS_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_GOOGLE_APPS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.cc b/chrome/browser/ui/webui/welcome/ntp_background_fetcher.cc
similarity index 97%
rename from chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.cc
rename to chrome/browser/ui/webui/welcome/ntp_background_fetcher.cc
index 658de1d..161936419 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.cc
+++ b/chrome/browser/ui/webui/welcome/ntp_background_fetcher.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/ui/webui/welcome/nux/ntp_background_fetcher.h"
+#include "chrome/browser/ui/webui/welcome/ntp_background_fetcher.h"
 
 #include <utility>
 
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.h b/chrome/browser/ui/webui/welcome/ntp_background_fetcher.h
similarity index 79%
rename from chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.h
rename to chrome/browser/ui/webui/welcome/ntp_background_fetcher.h
index 1e4892a..b9b8602 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.h
+++ b/chrome/browser/ui/webui/welcome/ntp_background_fetcher.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_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_FETCHER_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_FETCHER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_FETCHER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_FETCHER_H_
 
 #include <memory>
 #include <string>
@@ -37,4 +37,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_FETCHER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_FETCHER_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc b/chrome/browser/ui/webui/welcome/ntp_background_handler.cc
similarity index 92%
rename from chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
rename to chrome/browser/ui/webui/welcome/ntp_background_handler.cc
index bf02622..8109841c 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
+++ b/chrome/browser/ui/webui/welcome/ntp_background_handler.cc
@@ -2,12 +2,13 @@
 // 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/welcome/nux/ntp_background_handler.h"
+#include "chrome/browser/ui/webui/welcome/ntp_background_handler.h"
 
 #include <memory>
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/onboarding_ntp_backgrounds.h"
@@ -73,7 +74,7 @@
   element->SetString("title",
                      l10n_util::GetStringUTF8(
                          IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_EARTH_TITLE));
-  element->SetString("imageUrl", kUrlPrefix + std::to_string(id));
+  element->SetString("imageUrl", kUrlPrefix + base::NumberToString(id));
   element->SetString("thumbnailClass", "earth");
   list_value.Append(std::move(element));
 
@@ -83,7 +84,7 @@
   element->SetString(
       "title", l10n_util::GetStringUTF8(
                    IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_CITYSCAPE_TITLE));
-  element->SetString("imageUrl", kUrlPrefix + std::to_string(id));
+  element->SetString("imageUrl", kUrlPrefix + base::NumberToString(id));
   element->SetString("thumbnailClass", "cityscape");
   list_value.Append(std::move(element));
 
@@ -93,7 +94,7 @@
   element->SetString(
       "title", l10n_util::GetStringUTF8(
                    IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_LANDSCAPE_TITLE));
-  element->SetString("imageUrl", kUrlPrefix + std::to_string(id));
+  element->SetString("imageUrl", kUrlPrefix + base::NumberToString(id));
   element->SetString("thumbnailClass", "landscape");
   list_value.Append(std::move(element));
 
@@ -103,7 +104,7 @@
   element->SetString("title",
                      l10n_util::GetStringUTF8(
                          IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_ART_TITLE));
-  element->SetString("imageUrl", kUrlPrefix + std::to_string(id));
+  element->SetString("imageUrl", kUrlPrefix + base::NumberToString(id));
   element->SetString("thumbnailClass", "art");
   list_value.Append(std::move(element));
 
@@ -114,7 +115,7 @@
       "title",
       l10n_util::GetStringUTF8(
           IDS_ONBOARDING_WELCOME_NTP_BACKGROUND_GEOMETRIC_SHAPES_TITLE));
-  element->SetString("imageUrl", kUrlPrefix + std::to_string(id));
+  element->SetString("imageUrl", kUrlPrefix + base::NumberToString(id));
   element->SetString("thumbnailClass", "geometric-shapes");
   list_value.Append(std::move(element));
 
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h b/chrome/browser/ui/webui/welcome/ntp_background_handler.h
similarity index 77%
rename from chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h
rename to chrome/browser/ui/webui/welcome/ntp_background_handler.h
index d2c23fa..cb863dcb 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h
+++ b/chrome/browser/ui/webui/welcome/ntp_background_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_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_HANDLER_H_
 
 #include "content/public/browser/web_ui_message_handler.h"
 
@@ -28,4 +28,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_NTP_BACKGROUND_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NTP_BACKGROUND_HANDLER_H_
diff --git a/chrome/browser/ui/webui/welcome/nux/README b/chrome/browser/ui/webui/welcome/nux/README
deleted file mode 100644
index b2d6888..0000000
--- a/chrome/browser/ui/webui/welcome/nux/README
+++ /dev/null
@@ -1,3 +0,0 @@
-New User Experience Experiment (NUX). Code here will prompt new users to go
-through an onboarding experience to customize their new browser install to
-their common usages.
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc
index 34c8a24..ed36410 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.cc
+++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/search.h"
-#include "chrome/browser/ui/webui/welcome/nux/constants.h"
+#include "chrome/browser/ui/webui/welcome/constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/common/policy_map.h"
diff --git a/chrome/browser/ui/webui/welcome/nux/set_as_default_handler.cc b/chrome/browser/ui/webui/welcome/set_as_default_handler.cc
similarity index 85%
rename from chrome/browser/ui/webui/welcome/nux/set_as_default_handler.cc
rename to chrome/browser/ui/webui/welcome/set_as_default_handler.cc
index 3d4f3387..9166e53 100644
--- a/chrome/browser/ui/webui/welcome/nux/set_as_default_handler.cc
+++ b/chrome/browser/ui/webui/welcome/set_as_default_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/ui/webui/welcome/nux/set_as_default_handler.h"
+#include "chrome/browser/ui/webui/welcome/set_as_default_handler.h"
 
 namespace nux {
 
diff --git a/chrome/browser/ui/webui/welcome/nux/set_as_default_handler.h b/chrome/browser/ui/webui/welcome/set_as_default_handler.h
similarity index 72%
rename from chrome/browser/ui/webui/welcome/nux/set_as_default_handler.h
rename to chrome/browser/ui/webui/welcome/set_as_default_handler.h
index 5f70e699..1d89f292 100644
--- a/chrome/browser/ui/webui/welcome/nux/set_as_default_handler.h
+++ b/chrome/browser/ui/webui/welcome/set_as_default_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_UI_WEBUI_WELCOME_NUX_SET_AS_DEFAULT_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_SET_AS_DEFAULT_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_SET_AS_DEFAULT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_SET_AS_DEFAULT_HANDLER_H_
 
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
@@ -23,4 +23,4 @@
 
 }  // namespace nux
 
-#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_SET_AS_DEFAULT_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_WELCOME_SET_AS_DEFAULT_HANDLER_H_
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 4ae52e2..460e976 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -8,12 +8,12 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/ui/webui/localized_string.h"
-#include "chrome/browser/ui/webui/welcome/nux/bookmark_handler.h"
-#include "chrome/browser/ui/webui/welcome/nux/constants.h"
-#include "chrome/browser/ui/webui/welcome/nux/google_apps_handler.h"
-#include "chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h"
-#include "chrome/browser/ui/webui/welcome/nux/set_as_default_handler.h"
+#include "chrome/browser/ui/webui/welcome/bookmark_handler.h"
+#include "chrome/browser/ui/webui/welcome/constants.h"
+#include "chrome/browser/ui/webui/welcome/google_apps_handler.h"
+#include "chrome/browser/ui/webui/welcome/ntp_background_handler.h"
 #include "chrome/browser/ui/webui/welcome/nux_helper.h"
+#include "chrome/browser/ui/webui/welcome/set_as_default_handler.h"
 #include "chrome/browser/ui/webui/welcome/welcome_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.h b/chrome/browser/ui/webui/welcome/welcome_ui.h
index ed54ca23..376a447 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.h
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.h
@@ -10,7 +10,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.h"
+#include "chrome/browser/ui/webui/welcome/ntp_background_fetcher.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/vr/service/xr_device_impl.cc b/chrome/browser/vr/service/xr_device_impl.cc
index 5a464fd..64e4c74 100644
--- a/chrome/browser/vr/service/xr_device_impl.cc
+++ b/chrome/browser/vr/service/xr_device_impl.cc
@@ -307,10 +307,20 @@
                        "id", session_runtime_id);
 
   auto runtime_options = GetRuntimeOptions(options.get());
-  runtime_options->render_process_id =
-      render_frame_host_ ? render_frame_host_->GetProcess()->GetID() : -1;
-  runtime_options->render_frame_id =
-      render_frame_host_ ? render_frame_host_->GetRoutingID() : -1;
+
+#if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARCORE)
+  if (session_runtime_id == device::mojom::XRDeviceId::ARCORE_DEVICE_ID) {
+    if (!render_frame_host_) {
+      std::move(callback).Run(
+          device::mojom::RequestSessionResult::NewFailureReason(
+              device::mojom::RequestSessionError::INVALID_CLIENT));
+      return;
+    }
+    runtime_options->render_process_id =
+        render_frame_host_->GetProcess()->GetID();
+    runtime_options->render_frame_id = render_frame_host_->GetRoutingID();
+  }
+#endif
 
   if (runtime_options->immersive) {
     GetSessionMetricsHelper()->ReportRequestPresent(*runtime_options);
diff --git a/chrome/browser/vr/test/webxr_vr_browser_test.cc b/chrome/browser/vr/test/webxr_vr_browser_test.cc
index d0d957558..863b159 100644
--- a/chrome/browser/vr/test/webxr_vr_browser_test.cc
+++ b/chrome/browser/vr/test/webxr_vr_browser_test.cc
@@ -37,6 +37,15 @@
   PollJavaScriptBooleanOrFail(
       "sessionInfos[sessionTypes.IMMERSIVE].currentSession != null",
       kPollTimeoutLong, web_contents);
+
+#if defined(OS_WIN)
+  // For WMR, creating a session may take foreground from us, and Windows may
+  // not return it when the session terminates. This means subsequent requests
+  // to enter an immersive session may fail. The fix for testing is to call
+  // SetForegroundWindow manually. In real code, we'll have foreground if there
+  // was a user gesture to enter VR.
+  SetForegroundWindow(hwnd_);
+#endif
 }
 
 void WebXrVrBrowserTestBase::EndSession(content::WebContents* web_contents) {
diff --git a/chrome/browser/vr/test/xr_browser_test.cc b/chrome/browser/vr/test/xr_browser_test.cc
index 54ddc20e..2cac9c3 100644
--- a/chrome/browser/vr/test/xr_browser_test.cc
+++ b/chrome/browser/vr/test/xr_browser_test.cc
@@ -220,6 +220,13 @@
                                     kPollTimeoutMedium,
                                     GetCurrentWebContents()))
       << "Timed out waiting for JavaScript test initialization.";
+
+#if defined(OS_WIN)
+  // Now that the browser is opened and has focus, keep track of this window so
+  // that we can restore the proper focus after entering each session. This is
+  // required for WMR tests that create multiple sessions to work properly.
+  hwnd_ = GetForegroundWindow();
+#endif
 }
 
 void XrBrowserTestBase::RunJavaScriptOrFail(
diff --git a/chrome/browser/vr/test/xr_browser_test.h b/chrome/browser/vr/test/xr_browser_test.h
index 5b4b89b..ad29ac4c 100644
--- a/chrome/browser/vr/test/xr_browser_test.h
+++ b/chrome/browser/vr/test/xr_browser_test.h
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/vr/test/conditional_skipping.h"
 #include "chrome/common/chrome_features.h"
@@ -23,6 +24,10 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 namespace vr {
 
 // Base browser test class for running XR-related tests.
@@ -228,6 +233,10 @@
   std::vector<XrTestRequirement> runtime_requirements_;
   std::unordered_set<std::string> ignored_requirements_;
 
+#if defined(OS_WIN)
+  HWND hwnd_;
+#endif
+
  private:
   void LogJavaScriptFailure();
   Browser* browser_ = nullptr;
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index a26e26a..f488a4e 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -34,18 +34,14 @@
   base::flat_set<AuthenticatorTransport> candidate_transports(
       transport_availability.available_transports);
 
-  // As an exception, we can tell in advance if using Touch Id will succeed. If
-  // yes, always auto-select that transport over all other considerations for
-  // GetAssertion operations; and de-select it if it will not work.
+  // For GetAssertion requests, auto advance to Touch ID if the authenticator
+  // has a matching credential for the (possibly empty) allow list.
   if (transport_availability.request_type ==
           device::FidoRequestHandlerBase::RequestType::kGetAssertion &&
-      !transport_availability.has_empty_allow_list &&
       base::Contains(candidate_transports,
-                     device::FidoTransportProtocol::kInternal)) {
-    // For GetAssertion requests, auto advance to Touch ID if the keychain
-    // contains one of the allowedCredentials.
-    if (transport_availability.has_recognized_mac_touch_id_credential)
-      return device::FidoTransportProtocol::kInternal;
+                     device::FidoTransportProtocol::kInternal) &&
+      transport_availability.has_recognized_mac_touch_id_credential) {
+    return device::FidoTransportProtocol::kInternal;
   }
 
   // If caBLE is listed as one of the allowed transports, it indicates that the
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 76a6bc64..8075a59 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -28,9 +28,11 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "device/fido/features.h"
 #include "device/fido/fido_authenticator.h"
 
 #if defined(OS_MACOSX)
+#include "device/fido/mac/authenticator.h"
 #include "device/fido/mac/credential_metadata.h"
 #endif
 
@@ -304,19 +306,11 @@
 }  // namespace
 
 // static
-content::AuthenticatorRequestClientDelegate::TouchIdAuthenticatorConfig
+ChromeAuthenticatorRequestDelegate::TouchIdAuthenticatorConfig
 ChromeAuthenticatorRequestDelegate::TouchIdAuthenticatorConfigForProfile(
     Profile* profile) {
-  return content::AuthenticatorRequestClientDelegate::
-      TouchIdAuthenticatorConfig{kTouchIdKeychainAccessGroup,
-                                 TouchIdMetadataSecret(profile)};
-}
-
-base::Optional<
-    content::AuthenticatorRequestClientDelegate::TouchIdAuthenticatorConfig>
-ChromeAuthenticatorRequestDelegate::GetTouchIdAuthenticatorConfig() {
-  return TouchIdAuthenticatorConfigForProfile(
-      Profile::FromBrowserContext(browser_context()));
+  return TouchIdAuthenticatorConfig{kTouchIdKeychainAccessGroup,
+                                    TouchIdMetadataSecret(profile)};
 }
 #endif
 
@@ -357,15 +351,39 @@
   return !disable_ui_;
 }
 
-bool ChromeAuthenticatorRequestDelegate::ShouldDisablePlatformAuthenticators() {
+bool ChromeAuthenticatorRequestDelegate::
+    IsUserVerifyingPlatformAuthenticatorAvailable() {
 #if defined(OS_MACOSX)
   // Touch ID is available in Incognito, but not in Guest mode.
-  return Profile::FromBrowserContext(browser_context())->IsGuestSession();
-#else  // Windows, Android
-  return browser_context()->IsOffTheRecord();
-#endif
+  if (Profile::FromBrowserContext(browser_context())->IsGuestSession())
+    return false;
+
+  return device::fido::mac::TouchIdAuthenticator::IsAvailable(
+      TouchIdAuthenticatorConfigForProfile(
+          Profile::FromBrowserContext(browser_context())));
+#elif defined(OS_WIN)
+  if (browser_context()->IsOffTheRecord())
+    return false;
+
+  return base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi) &&
+         device::WinWebAuthnApiAuthenticator::
+             IsUserVerifyingPlatformAuthenticatorAvailable();
+#else
+  return false;
+#endif  // defined(OS_MACOSX) || defined(OS_WIN)
 }
 
+#if defined(OS_MACOSX)
+base::Optional<ChromeAuthenticatorRequestDelegate::TouchIdAuthenticatorConfig>
+ChromeAuthenticatorRequestDelegate::GetTouchIdAuthenticatorConfig() {
+  if (!IsUserVerifyingPlatformAuthenticatorAvailable())
+    return base::nullopt;
+
+  return TouchIdAuthenticatorConfigForProfile(
+      Profile::FromBrowserContext(browser_context()));
+}
+#endif  // defined(OS_MACOSX)
+
 void ChromeAuthenticatorRequestDelegate::OnTransportAvailabilityEnumerated(
     device::FidoRequestHandlerBase::TransportAvailabilityInfo data) {
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index a5635db..9af9b98 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -87,7 +87,7 @@
       device::FidoTransportProtocol transport) override;
   void DisableUI() override;
   bool IsWebAuthnUIEnabled() override;
-  bool ShouldDisablePlatformAuthenticators() override;
+  bool IsUserVerifyingPlatformAuthenticatorAvailable() override;
 
   // device::FidoRequestHandlerBase::Observer:
   void OnTransportAvailabilityEnumerated(
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
index 2b130e2..1260be5 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
@@ -8,9 +8,11 @@
 
 #include "build/build_config.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/authenticator_request_client_delegate.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/test/web_contents_tester.h"
 #include "device/fido/fido_device_authenticator.h"
+#include "device/fido/fido_discovery_factory.h"
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,10 +20,21 @@
 #include "device/fido/win/authenticator.h"
 #include "device/fido/win/fake_webauthn_api.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
-#endif
+#endif  // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+#include "device/fido/mac/authenticator_config.h"
+#include "device/fido/mac/scoped_touch_id_test_environment.h"
+#endif  // defined(OS_MACOSX)
 
 class ChromeAuthenticatorRequestDelegateTest
-    : public ChromeRenderViewHostTestHarness {};
+    : public ChromeRenderViewHostTestHarness {
+ protected:
+#if defined(OS_MACOSX)
+  API_AVAILABLE(macos(10.12.2))
+  device::fido::mac::ScopedTouchIdTestEnvironment touch_id_test_environment_;
+#endif  // defined(OS_MACOSX)
+};
 
 static constexpr char kRelyingPartyID[] = "example.com";
 
@@ -76,10 +89,7 @@
 #if defined(OS_MACOSX)
 std::string TouchIdMetadataSecret(
     ChromeAuthenticatorRequestDelegate* delegate) {
-  base::Optional<
-      content::AuthenticatorRequestClientDelegate::TouchIdAuthenticatorConfig>
-      config = delegate->GetTouchIdAuthenticatorConfig();
-  return config->metadata_secret;
+  return delegate->GetTouchIdAuthenticatorConfig()->metadata_secret;
 }
 
 TEST_F(ChromeAuthenticatorRequestDelegateTest, TouchIdMetadataSecret) {
@@ -114,9 +124,48 @@
   // Ensure this second secret is actually valid.
   EXPECT_EQ(32u, TouchIdMetadataSecret(&delegate2).size());
 }
-#endif
+
+TEST_F(ChromeAuthenticatorRequestDelegateTest, IsUVPAA) {
+  if (__builtin_available(macOS 10.12.2, *)) {
+    for (const bool touch_id_available : {false, true}) {
+      SCOPED_TRACE(::testing::Message()
+                   << "touch_id_available=" << touch_id_available);
+      touch_id_test_environment_.SetTouchIdAvailable(touch_id_available);
+
+      std::unique_ptr<content::AuthenticatorRequestClientDelegate> delegate =
+          std::make_unique<ChromeAuthenticatorRequestDelegate>(main_rfh(),
+                                                               kRelyingPartyID);
+      EXPECT_EQ(touch_id_available,
+                delegate->IsUserVerifyingPlatformAuthenticatorAvailable());
+    }
+  }
+}
+
+#endif  // defined(OS_MACOSX)
 
 #if defined(OS_WIN)
+TEST_F(ChromeAuthenticatorRequestDelegateTest, WinIsUVPAA) {
+  device::ScopedFakeWinWebAuthnApi win_webauthn_api =
+      device::ScopedFakeWinWebAuthnApi::MakeUnavailable();
+
+  for (const bool enable_win_webauthn_api : {false, true}) {
+    SCOPED_TRACE(enable_win_webauthn_api ? "enable_win_webauthn_api"
+                                         : "!enable_win_webauthn_api");
+    for (const bool is_uvpaa : {false, true}) {
+      SCOPED_TRACE(is_uvpaa ? "is_uvpaa" : "!is_uvpaa");
+
+      win_webauthn_api.set_available(enable_win_webauthn_api);
+      win_webauthn_api.set_is_uvpaa(is_uvpaa);
+
+      std::unique_ptr<content::AuthenticatorRequestClientDelegate> delegate =
+          std::make_unique<ChromeAuthenticatorRequestDelegate>(main_rfh(),
+                                                               kRelyingPartyID);
+      EXPECT_EQ(enable_win_webauthn_api && is_uvpaa,
+                delegate->IsUserVerifyingPlatformAuthenticatorAvailable());
+    }
+  }
+}
+
 // Tests that ShouldReturnAttestation() returns with true if |authenticator|
 // is the Windows native WebAuthn API with WEBAUTHN_API_VERSION_2 or higher,
 // where Windows prompts for attestation in its own native UI.
diff --git a/chrome/chrome_cleaner/components/recovery_component_unittest.cc b/chrome/chrome_cleaner/components/recovery_component_unittest.cc
index 71ff18a..d4a3412 100644
--- a/chrome/chrome_cleaner/components/recovery_component_unittest.cc
+++ b/chrome/chrome_cleaner/components/recovery_component_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/test_simple_task_runner.h"
 #include "chrome/chrome_cleaner/http/mock_http_agent_factory.h"
 #include "chrome/chrome_cleaner/test/test_pup_data.h"
@@ -66,8 +66,9 @@
  protected:
   RecoveryComponentTest() : task_runner_(new base::TestSimpleTaskRunner) {}
 
-  // A message loop is needed for the current task runner to be available.
-  base::MessageLoopForUI ui_message_loop_;
+  // Needed for the current task runner to be available.
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
 
   // The recover component under test. This declaration must be after the
   // |ui_message_loop_| because the |RecoveryComponent| constructor needs
diff --git a/chrome/chrome_cleaner/ui/BUILD.gn b/chrome/chrome_cleaner/ui/BUILD.gn
index 46cb9af..3cb30dd 100644
--- a/chrome/chrome_cleaner/ui/BUILD.gn
+++ b/chrome/chrome_cleaner/ui/BUILD.gn
@@ -38,6 +38,7 @@
   deps = [
     ":cleaner_ui",
     "//base",
+    "//base/test:test_support",
     "//chrome/chrome_cleaner/constants:uws_id",
     "//chrome/chrome_cleaner/ipc:mock_chrome_prompt_ipc",
     "//chrome/chrome_cleaner/test:test_pup_data",
diff --git a/chrome/chrome_cleaner/ui/chrome_proxy_main_dialog_unittest.cc b/chrome/chrome_cleaner/ui/chrome_proxy_main_dialog_unittest.cc
index d3fa1f5..716f4a5 100644
--- a/chrome/chrome_cleaner/ui/chrome_proxy_main_dialog_unittest.cc
+++ b/chrome/chrome_cleaner/ui/chrome_proxy_main_dialog_unittest.cc
@@ -9,8 +9,8 @@
 
 #include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "chrome/chrome_cleaner/ipc/mock_chrome_prompt_ipc.h"
 #include "chrome/chrome_cleaner/test/test_file_util.h"
 #include "chrome/chrome_cleaner/test/test_pup_data.h"
@@ -48,7 +48,8 @@
 }
 
 TEST_F(ChromeProxyMainDialogTest, NoPUPsFound) {
-  base::MessageLoopForUI ui_message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   base::RunLoop run_loop;
   EXPECT_CALL(delegate_, OnClose())
@@ -93,7 +94,8 @@
       prompt_acceptance == PromptAcceptance::ACCEPTED_WITHOUT_LOGS;
   bool logs_allowed = prompt_acceptance == PromptAcceptance::ACCEPTED_WITH_LOGS;
 
-  base::MessageLoopForUI ui_message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   EXPECT_CALL(mock_settings_,
               set_logs_allowed_in_cleanup_mode(Eq(logs_allowed)))
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index d856e29..1d56a19497 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -543,6 +543,7 @@
     ":buildflags",
     ":version_header",
     "//base",
+    "//build:branding_buildflags",
     "//ppapi/buildflags",
   ]
 
@@ -598,6 +599,7 @@
     ":buildflags",
     ":chrome_features",
     ":non_code_constants",
+    "//build:branding_buildflags",
     "//content/public/common:result_codes",
     "//rlz/buildflags",
   ]
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index dd163bb..81d60e3 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/common/chrome_constants.h"
 
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_version.h"
 
@@ -13,7 +14,7 @@
 #define CHROMIUM_PRODUCT_STRING "Chromium"
 #if defined(GOOGLE_CHROME_BUILD)
 #define PRODUCT_STRING "Google Chrome"
-#elif defined(CHROMIUM_BUILD)
+#elif BUILDFLAG(CHROMIUM_BRANDING)
 #define PRODUCT_STRING "Chromium"
 #else
 #error Unknown branding
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index b3cff585..4ef4b7e 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -469,8 +469,8 @@
       break;
     }
 #endif
-#if defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(CHROMIUM_BUILD)) || \
-    defined(OS_MACOSX)
+#if defined(OS_CHROMEOS) || \
+    (defined(OS_LINUX) && BUILDFLAG(CHROMIUM_BRANDING)) || defined(OS_MACOSX)
     case chrome::DIR_USER_EXTERNAL_EXTENSIONS: {
       if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
         return false;
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 34e42c5..d592642 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_COMMON_CHROME_PATHS_H__
 #define CHROME_COMMON_CHROME_PATHS_H__
 
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -19,40 +20,40 @@
 enum {
   PATH_START = 1000,
 
-  DIR_APP = PATH_START,         // Directory where dlls and data reside.
-  DIR_LOGS,                     // Directory where logs should be written.
-  DIR_USER_DATA,                // Directory where user data can be written.
-  DIR_CRASH_DUMPS,              // Directory where crash dumps are written.
+  DIR_APP = PATH_START,  // Directory where dlls and data reside.
+  DIR_LOGS,              // Directory where logs should be written.
+  DIR_USER_DATA,         // Directory where user data can be written.
+  DIR_CRASH_DUMPS,       // Directory where crash dumps are written.
 #if defined(OS_WIN)
-  DIR_WATCHER_DATA,             // Directory where the Chrome watcher stores
-                                // data.
-  DIR_ROAMING_USER_DATA,        // Directory where user data is stored that
-                                // needs to be roamed between computers.
+  DIR_WATCHER_DATA,       // Directory where the Chrome watcher stores
+                          // data.
+  DIR_ROAMING_USER_DATA,  // Directory where user data is stored that
+                          // needs to be roamed between computers.
 #endif
-  DIR_RESOURCES,                // Directory containing separate file resources
-                                // used by Chrome at runtime.
-  DIR_INSPECTOR_DEBUG,          // Directory where non-bundled and non-minified
-                                // web inspector is located.
-  DIR_APP_DICTIONARIES,         // Directory where the global dictionaries are.
-  DIR_USER_DOCUMENTS,           // Directory for a user's "My Documents".
-  DIR_USER_MUSIC,               // Directory for a user's music.
-  DIR_USER_PICTURES,            // Directory for a user's pictures.
-  DIR_USER_VIDEOS,              // Directory for a user's videos.
-  DIR_DEFAULT_DOWNLOADS_SAFE,   // Directory for a user's
-                                // "My Documents/Downloads", (Windows) or
-                                // "Downloads". (Linux)
-  DIR_DEFAULT_DOWNLOADS,        // Directory for a user's downloads.
-  DIR_INTERNAL_PLUGINS,         // Directory where internal plugins reside.
-  DIR_COMPONENTS,               // Directory where built-in implementations of
-                                // component-updated libraries or data reside.
+  DIR_RESOURCES,               // Directory containing separate file resources
+                               // used by Chrome at runtime.
+  DIR_INSPECTOR_DEBUG,         // Directory where non-bundled and non-minified
+                               // web inspector is located.
+  DIR_APP_DICTIONARIES,        // Directory where the global dictionaries are.
+  DIR_USER_DOCUMENTS,          // Directory for a user's "My Documents".
+  DIR_USER_MUSIC,              // Directory for a user's music.
+  DIR_USER_PICTURES,           // Directory for a user's pictures.
+  DIR_USER_VIDEOS,             // Directory for a user's videos.
+  DIR_DEFAULT_DOWNLOADS_SAFE,  // Directory for a user's
+                               // "My Documents/Downloads", (Windows) or
+                               // "Downloads". (Linux)
+  DIR_DEFAULT_DOWNLOADS,       // Directory for a user's downloads.
+  DIR_INTERNAL_PLUGINS,        // Directory where internal plugins reside.
+  DIR_COMPONENTS,              // Directory where built-in implementations of
+                               // component-updated libraries or data reside.
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
-  DIR_POLICY_FILES,             // Directory for system-wide read-only
-                                // policy files that allow sys-admins
-                                // to set policies for chrome. This directory
-                                // contains subdirectories.
+  DIR_POLICY_FILES,  // Directory for system-wide read-only
+                     // policy files that allow sys-admins
+                     // to set policies for chrome. This directory
+                     // contains subdirectories.
 #endif
-#if defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(CHROMIUM_BUILD)) || \
-    defined(OS_MACOSX)
+#if defined(OS_CHROMEOS) || \
+    (defined(OS_LINUX) && BUILDFLAG(CHROMIUM_BRANDING)) || defined(OS_MACOSX)
   DIR_USER_EXTERNAL_EXTENSIONS,  // Directory for per-user external extensions
                                  // on Chrome Mac and Chromium Linux.
                                  // On Chrome OS, this path is used for OEM
@@ -66,39 +67,39 @@
                                        // describe extensions which are to be
                                        // installed when chrome is run.
 #endif
-  DIR_EXTERNAL_EXTENSIONS,      // Directory where installer places .crx files.
+  DIR_EXTERNAL_EXTENSIONS,  // Directory where installer places .crx files.
 
-  DIR_DEFAULT_APPS,             // Directory where installer places .crx files
-                                // to be installed when chrome is first run.
-  DIR_PEPPER_FLASH_PLUGIN,      // Directory to the bundled Pepper Flash plugin,
-                                // containing the plugin and the manifest.
+  DIR_DEFAULT_APPS,         // Directory where installer places .crx files
+                            // to be installed when chrome is first run.
+  DIR_PEPPER_FLASH_PLUGIN,  // Directory to the bundled Pepper Flash plugin,
+                            // containing the plugin and the manifest.
   DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,  // Base directory of the Pepper
                                               // Flash plugins downloaded by the
                                               // component updater.
-  FILE_RESOURCE_MODULE,         // Full path and filename of the module that
-                                // contains embedded resources (version,
-                                // strings, images, etc.).
-  FILE_LOCAL_STATE,             // Path and filename to the file in which
-                                // machine/installation-specific state is saved.
-  FILE_RECORDED_SCRIPT,         // Full path to the script.log file that
-                                // contains recorded browser events for
-                                // playback.
-  FILE_PEPPER_FLASH_PLUGIN,     // Full path to the bundled Pepper Flash plugin
-                                // file.
+  FILE_RESOURCE_MODULE,      // Full path and filename of the module that
+                             // contains embedded resources (version,
+                             // strings, images, etc.).
+  FILE_LOCAL_STATE,          // Path and filename to the file in which
+                             // machine/installation-specific state is saved.
+  FILE_RECORDED_SCRIPT,      // Full path to the script.log file that
+                             // contains recorded browser events for
+                             // playback.
+  FILE_PEPPER_FLASH_PLUGIN,  // Full path to the bundled Pepper Flash plugin
+                             // file.
   FILE_PEPPER_FLASH_SYSTEM_PLUGIN,  // Full path to the system version of the
                                     // Pepper Flash plugin, downloadable from
                                     // Adobe website. Querying this path might
                                     // succeed no matter the file exists or not.
-  DIR_PNACL_BASE,               // Full path to the base dir for PNaCl.
-  DIR_PNACL_COMPONENT,          // Full path to the latest PNaCl version
-                                // (subdir of DIR_PNACL_BASE).
-  FILE_WIDEVINE_CDM,            // Full path to the Widevine CDM.
-  FILE_RESOURCES_PACK,          // Full path to the .pak file containing
-                                // binary data (e.g., html files and images
-                                // used by internal pages).
+  DIR_PNACL_BASE,                   // Full path to the base dir for PNaCl.
+  DIR_PNACL_COMPONENT,              // Full path to the latest PNaCl version
+                                    // (subdir of DIR_PNACL_BASE).
+  FILE_WIDEVINE_CDM,                // Full path to the Widevine CDM.
+  FILE_RESOURCES_PACK,              // Full path to the .pak file containing
+                                    // binary data (e.g., html files and images
+                                    // used by internal pages).
 #if defined(OS_CHROMEOS)
-  DIR_CHROMEOS_WALLPAPERS,      // Directory where downloaded chromeos
-                                // wallpapers reside.
+  DIR_CHROMEOS_WALLPAPERS,            // Directory where downloaded chromeos
+                                      // wallpapers reside.
   DIR_CHROMEOS_WALLPAPER_THUMBNAILS,  // Directory where downloaded chromeos
                                       // wallpaper thumbnails reside.
   DIR_CHROMEOS_CUSTOM_WALLPAPERS,     // Directory where custom wallpapers
@@ -108,23 +109,23 @@
                                              // supervised user whitelists are
                                              // installed.
 #if defined(OS_LINUX) || defined(OS_MACOSX)
-  DIR_NATIVE_MESSAGING,         // System directory where native messaging host
-                                // manifest files are stored.
-  DIR_USER_NATIVE_MESSAGING,    // Directory with Native Messaging Hosts
-                                // installed per-user.
+  DIR_NATIVE_MESSAGING,       // System directory where native messaging host
+                              // manifest files are stored.
+  DIR_USER_NATIVE_MESSAGING,  // Directory with Native Messaging Hosts
+                              // installed per-user.
 #endif
 #if !defined(OS_ANDROID)
-  DIR_GLOBAL_GCM_STORE,         // Directory where the global GCM instance
-                                // stores its data.
+  DIR_GLOBAL_GCM_STORE,  // Directory where the global GCM instance
+                         // stores its data.
 #endif
 
   // Valid only in development environment; TODO(darin): move these
-  DIR_GEN_TEST_DATA,            // Directory where generated test data resides.
-  DIR_TEST_DATA,                // Directory where unit test data resides.
-  DIR_TEST_TOOLS,               // Directory where unit test tools reside.
+  DIR_GEN_TEST_DATA,  // Directory where generated test data resides.
+  DIR_TEST_DATA,      // Directory where unit test data resides.
+  DIR_TEST_TOOLS,     // Directory where unit test tools reside.
 #if defined(OS_LINUX)
-  FILE_COMPONENT_FLASH_HINT,    // A file in a known location that points to
-                                // the component updated flash plugin.
+  FILE_COMPONENT_FLASH_HINT,  // A file in a known location that points to
+                              // the component updated flash plugin.
 #endif  // defined(OS_LINUX)
 #if defined(OS_CHROMEOS)
   FILE_CHROME_OS_COMPONENT_FLASH,  // The location of component updated Flash on
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index 8fb7199..87b6f10 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -47,7 +47,6 @@
   schema_include_rules = chrome_extensions_api_schema_include_rules
 
   sources += [
-    "action.json",
     "app.json",
     "commands.json",
     "declarative_content.json",
diff --git a/chrome/common/extensions/api/action.json b/chrome/common/extensions/api/action.json
index a277a49b..49c4b2e8 100644
--- a/chrome/common/extensions/api/action.json
+++ b/chrome/common/extensions/api/action.json
@@ -6,6 +6,9 @@
   {
     "namespace": "action",
     "description": "Use actions to put icons in the main Google Chrome toolbar, to the right of the address bar. Actions can be set to take action on all pages (default_state: enabled) or only the current page (default_state: disabled). If an action is default disabled, the action appears grayed out when inactive. In addition to its <a href='action#icon'>icon</a>, an action can also have a <a href='action#tooltip'>tooltip</a>, a <a href='action#badge'>badge</a>, and a <a href='action#popups'>popup</a>.",
+    "compiler_options": {
+      "implemented_in": "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+    },
     "functions": [{
       "name": "setTitle",
       "type": "function",
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index 1f734e4..9c0c100 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -124,11 +124,12 @@
 }
 
 uncompiled_sources_ = [
-  "page_action.json",
+  "action.json",
   "browser_action.json",
   "browsing_data.json",
   "extension.json",
   "idltest.idl",
+  "page_action.json",
   "top_sites.json",
 ]
 
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index 628c4e2c..80d1116 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -57,6 +57,7 @@
   enable_simple_browser_service_out_of_process =
       is_chromeos && (is_debug || dcheck_always_on)
 
+  # TODO(crbug.com/980869): Change this to is_chromeos || is_android.
   enable_supervised_users = !is_chromecast
 
   # Indicates if Wayland display server support is enabled.
diff --git a/chrome/common/service_process_util_linux.cc b/chrome/common/service_process_util_linux.cc
index 06f6e1d7..b324b31 100644
--- a/chrome/common/service_process_util_linux.cc
+++ b/chrome/common/service_process_util_linux.cc
@@ -28,7 +28,7 @@
 std::string GetBaseDesktopName() {
 #if defined(GOOGLE_CHROME_BUILD)
   return "google-chrome-service.desktop";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   return "chromium-service.desktop";
 #endif
 }
@@ -72,7 +72,7 @@
   DCHECK(autorun_command_line_.get());
 #if defined(GOOGLE_CHROME_BUILD)
   std::string app_name = "Google Chrome Service";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   std::string app_name = "Chromium Service";
 #endif
   return AutoStart::AddApplication(
diff --git a/chrome/common/service_process_util_unittest.cc b/chrome/common/service_process_util_unittest.cc
index 1c1cde0..01e26a5 100644
--- a/chrome/common/service_process_util_unittest.cc
+++ b/chrome/common/service_process_util_unittest.cc
@@ -142,7 +142,7 @@
 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
 #if defined(GOOGLE_CHROME_BUILD)
   std::string base_desktop_name = "google-chrome-service.desktop";
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
   std::string base_desktop_name = "chromium-service.desktop";
 #endif
   std::string exec_value;
diff --git a/chrome/credential_provider/eventlog/gcp_eventlog_messages.mc b/chrome/credential_provider/eventlog/gcp_eventlog_messages.mc
index 0dfd0b3a..60ddd632 100644
--- a/chrome/credential_provider/eventlog/gcp_eventlog_messages.mc
+++ b/chrome/credential_provider/eventlog/gcp_eventlog_messages.mc
Binary files differ
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs b/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
index 3e5d411..382666ab 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
@@ -62,7 +62,7 @@
 				{

 					NoRemove Application

 					{

-						ForceRemove GCP

+						ForceRemove GCPW

 						{

 							val CategoryCount = d '1'

 							val CategoryMessageFile = s '%EVENTLOG_PATH%'

diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
index c284b32..495975f 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
@@ -103,7 +103,7 @@
                            true,    // Enable thread id.
                            true,    // Enable timestamp.
                            false);  // Enable tickcount.
-      logging::SetEventSource("GCP", GCP_CATEGORY, MSG_LOG_MESSAGE);
+      logging::SetEventSource("GCPW", GCPW_CATEGORY, MSG_LOG_MESSAGE);
 
       base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
 
diff --git a/chrome/credential_provider/setup/setup.cc b/chrome/credential_provider/setup/setup.cc
index e573ced..3c6d36b6 100644
--- a/chrome/credential_provider/setup/setup.cc
+++ b/chrome/credential_provider/setup/setup.cc
@@ -105,7 +105,7 @@
     }
   }
 
-  logging::SetEventSource("GCP", GCP_CATEGORY, MSG_LOG_MESSAGE);
+  logging::SetEventSource("GCPW", GCPW_CATEGORY, MSG_LOG_MESSAGE);
 
   // Make sure the process exits cleanly on unexpected errors.
   base::EnableTerminationOnHeapCorruption();
diff --git a/chrome/credential_provider/test/gcp_setup_unittests.cc b/chrome/credential_provider/test/gcp_setup_unittests.cc
index 16ef99a..394790f 100644
--- a/chrome/credential_provider/test/gcp_setup_unittests.cc
+++ b/chrome/credential_provider/test/gcp_setup_unittests.cc
@@ -158,7 +158,7 @@
   // Make sure eventlog source is registered.
   base::win::RegKey el_key(
       HKEY_LOCAL_MACHINE,
-      L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\GCP",
+      L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\GCPW",
       KEY_READ);
   EXPECT_EQ(registered, el_key.Valid());
 
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 33295f1..f71bd8a2 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -47,6 +47,10 @@
 
 namespace {
 
+// DowngradeVersion holds the version from which Chrome was downgraded. In case
+// of multiple downgrades (e.g., 75->74->73), it retains the highest version
+// installed prior to any downgrades. DowngradeVersion is deleted on upgrade
+// once Chrome reaches the version from which it was downgraded.
 const wchar_t kRegDowngradeVersion[] = L"DowngradeVersion";
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -531,7 +535,7 @@
 }
 
 // static
-base::Version InstallUtil::GetDowngradeVersion() {
+base::Optional<base::Version> InstallUtil::GetDowngradeVersion() {
   RegKey key;
   base::string16 downgrade_version;
   if (key.Open(install_static::IsSystemInstall() ? HKEY_LOCAL_MACHINE
@@ -541,9 +545,12 @@
       key.ReadValue(kRegDowngradeVersion, &downgrade_version) !=
           ERROR_SUCCESS ||
       downgrade_version.empty()) {
-    return base::Version();
+    return base::nullopt;
   }
-  return base::Version(base::UTF16ToASCII(downgrade_version));
+  base::Version version(base::UTF16ToASCII(downgrade_version));
+  if (!version.IsValid())
+    return base::nullopt;
+  return version;
 }
 
 // static
@@ -553,18 +560,21 @@
     const base::Version& new_version,
     WorkItemList* list) {
   DCHECK(list);
-  const base::Version downgrade_version = GetDowngradeVersion();
-  if (!current_version ||
-      (*current_version <= new_version &&
-       ((!downgrade_version.IsValid() || downgrade_version <= new_version)))) {
+  const auto downgrade_version = GetDowngradeVersion();
+  if (current_version && new_version < *current_version) {
+    // This is a downgrade. Write the value if this is the first one (i.e., no
+    // previous value exists). Otherwise, leave any existing value in place.
+    if (!downgrade_version) {
+      list->AddSetRegValueWorkItem(
+          root, install_static::GetClientStateKeyPath(), KEY_WOW64_32KEY,
+          kRegDowngradeVersion,
+          base::ASCIIToUTF16(current_version->GetString()), true);
+    }
+  } else if (!current_version || new_version >= downgrade_version) {
+    // This is a new install or an upgrade to/past a previous DowngradeVersion.
     list->AddDeleteRegValueWorkItem(root,
                                     install_static::GetClientStateKeyPath(),
                                     KEY_WOW64_32KEY, kRegDowngradeVersion);
-  } else if (*current_version > new_version && !downgrade_version.IsValid()) {
-    list->AddSetRegValueWorkItem(
-        root, install_static::GetClientStateKeyPath(), KEY_WOW64_32KEY,
-        kRegDowngradeVersion, base::ASCIIToUTF16(current_version->GetString()),
-        true);
   }
 }
 
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
index 79c14995..941308f 100644
--- a/chrome/installer/util/install_util.h
+++ b/chrome/installer/util/install_util.h
@@ -16,6 +16,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/version.h"
@@ -163,9 +164,8 @@
   static base::string16 GetCurrentDate();
 
   // Returns the highest Chrome version that was installed prior to a downgrade,
-  // or an invalid Version if Chrome was not previously downgraded from a newer
-  // version.
-  static base::Version GetDowngradeVersion();
+  // or no value if Chrome was not previously downgraded from a newer version.
+  static base::Optional<base::Version> GetDowngradeVersion();
 
   // Adds or removes downgrade version registry value. This function should only
   // be used for Chrome install.
diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc
index c55d180..eaa87e6 100644
--- a/chrome/installer/util/install_util_unittest.cc
+++ b/chrome/installer/util/install_util_unittest.cc
@@ -439,14 +439,14 @@
   base::Version lower_new_version_1("1.1.1.0");
   base::Version lower_new_version_2("1.1.0.0");
 
-  ASSERT_FALSE(InstallUtil::GetDowngradeVersion().IsValid());
+  ASSERT_FALSE(InstallUtil::GetDowngradeVersion());
 
   // Upgrade should not create the value.
   list.reset(WorkItem::CreateWorkItemList());
   InstallUtil::AddUpdateDowngradeVersionItem(kRoot, &current_version,
                                              higer_new_version, list.get());
   ASSERT_TRUE(list->Do());
-  ASSERT_FALSE(InstallUtil::GetDowngradeVersion().IsValid());
+  ASSERT_FALSE(InstallUtil::GetDowngradeVersion());
 
   // Downgrade should create the value.
   list.reset(WorkItem::CreateWorkItemList());
@@ -501,7 +501,7 @@
   InstallUtil::AddUpdateDowngradeVersionItem(kRoot, &lower_new_version_1,
                                              higer_new_version, list.get());
   ASSERT_TRUE(list->Do());
-  ASSERT_FALSE(InstallUtil::GetDowngradeVersion().IsValid());
+  ASSERT_FALSE(InstallUtil::GetDowngradeVersion());
 
   // Fresh install should delete the value if it exists.
   list.reset(WorkItem::CreateWorkItemList());
@@ -513,7 +513,7 @@
   InstallUtil::AddUpdateDowngradeVersionItem(kRoot, nullptr,
                                              lower_new_version_1, list.get());
   ASSERT_TRUE(list->Do());
-  ASSERT_FALSE(InstallUtil::GetDowngradeVersion().IsValid());
+  ASSERT_FALSE(InstallUtil::GetDowngradeVersion());
 }
 
 TEST(DeleteRegistryKeyTest, DeleteAccessRightIsEnoughToDelete) {
diff --git a/chrome/installer/util/logging_installer.cc b/chrome/installer/util/logging_installer.cc
index 78dfa44c..9473692 100644
--- a/chrome/installer/util/logging_installer.cc
+++ b/chrome/installer/util/logging_installer.cc
@@ -117,7 +117,7 @@
   static const base::FilePath::CharType kLogFilename[] =
 #if defined(GOOGLE_CHROME_BUILD)
       FILE_PATH_LITERAL("chrome_installer.log");
-#else  // CHROMIUM_BUILD
+#else  // BUILDFLAG(CHROMIUM_BRANDING)
       FILE_PATH_LITERAL("chromium_installer.log");
 #endif
 
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index ddd3dd6..f4473b2 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -1264,8 +1264,6 @@
   if (!search_box)
     return;
   search_box->RevertThemeChanges();
-  content::RenderThread::Get()->RecordAction(
-      base::UserMetricsAction("ChromeColors_MenuCancel"));
 }
 
 // static
@@ -1274,8 +1272,6 @@
   if (!search_box)
     return;
   search_box->ConfirmThemeChanges();
-  content::RenderThread::Get()->RecordAction(
-      base::UserMetricsAction("ChromeColors_MenuDone"));
 }
 
 v8::Local<v8::Value> NewTabPageBindings::GetColorsInfo(v8::Isolate* isolate) {
diff --git a/chrome/services/app_service/public/cpp/BUILD.gn b/chrome/services/app_service/public/cpp/BUILD.gn
index 3f42f44a..f31b9d5 100644
--- a/chrome/services/app_service/public/cpp/BUILD.gn
+++ b/chrome/services/app_service/public/cpp/BUILD.gn
@@ -2,18 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("app_service_proxy") {
-  sources = [
-    "app_service_proxy.cc",
-    "app_service_proxy.h",
-  ]
-
-  deps = [
-    ":app_update",
-    ":icon_loader",
-  ]
-}
-
 source_set("app_update") {
   sources = [
     "app_registry_cache.cc",
diff --git a/chrome/services/app_service/public/cpp/app_service_proxy.cc b/chrome/services/app_service/public/cpp/app_service_proxy.cc
deleted file mode 100644
index 6d2fe0d..0000000
--- a/chrome/services/app_service/public/cpp/app_service_proxy.cc
+++ /dev/null
@@ -1,52 +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/services/app_service/public/cpp/app_service_proxy.h"
-
-#include <utility>
-
-namespace apps {
-
-AppServiceProxy::AppServiceProxy() = default;
-
-AppServiceProxy::~AppServiceProxy() = default;
-
-apps::mojom::AppServicePtr& AppServiceProxy::AppService() {
-  return app_service_;
-}
-
-apps::AppRegistryCache& AppServiceProxy::AppRegistryCache() {
-  return cache_;
-}
-
-apps::mojom::IconKeyPtr AppServiceProxy::GetIconKey(const std::string& app_id) {
-  return apps::mojom::IconKey::New();
-}
-
-std::unique_ptr<apps::IconLoader::Releaser>
-AppServiceProxy::LoadIconFromIconKey(
-    apps::mojom::AppType app_type,
-    const std::string& app_id,
-    apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
-    int32_t size_hint_in_dip,
-    bool allow_placeholder_icon,
-    apps::mojom::Publisher::LoadIconCallback callback) {
-  std::move(callback).Run(apps::mojom::IconValue::New());
-  return nullptr;
-}
-
-void AppServiceProxy::Launch(const std::string& app_id,
-                             int32_t event_flags,
-                             apps::mojom::LaunchSource launch_source,
-                             int64_t display_id) {}
-
-void AppServiceProxy::SetPermission(const std::string& app_id,
-                                    apps::mojom::PermissionPtr permission) {}
-
-void AppServiceProxy::Uninstall(const std::string& app_id) {}
-
-void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {}
-
-}  // namespace apps
diff --git a/chrome/services/app_service/public/cpp/app_service_proxy.h b/chrome/services/app_service/public/cpp/app_service_proxy.h
deleted file mode 100644
index 38adb8b1..0000000
--- a/chrome/services/app_service/public/cpp/app_service_proxy.h
+++ /dev/null
@@ -1,62 +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_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
-#define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
-#include "chrome/services/app_service/public/cpp/icon_loader.h"
-#include "chrome/services/app_service/public/mojom/app_service.mojom.h"
-
-namespace apps {
-
-// Abstract superclass (with default no-op methods) for the App Service proxy.
-// See chrome/services/app_service/README.md.
-//
-// Most code, outside of tests, should use an AppServiceProxyImpl.
-class AppServiceProxy : public apps::IconLoader {
- public:
-  AppServiceProxy();
-  ~AppServiceProxy() override;
-
-  apps::mojom::AppServicePtr& AppService();
-  apps::AppRegistryCache& AppRegistryCache();
-
-  // apps::IconLoader overrides.
-  apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
-  std::unique_ptr<IconLoader::Releaser> LoadIconFromIconKey(
-      apps::mojom::AppType app_type,
-      const std::string& app_id,
-      apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
-      int32_t size_hint_in_dip,
-      bool allow_placeholder_icon,
-      apps::mojom::Publisher::LoadIconCallback callback) override;
-
-  virtual void Launch(const std::string& app_id,
-                      int32_t event_flags,
-                      apps::mojom::LaunchSource launch_source,
-                      int64_t display_id);
-
-  virtual void SetPermission(const std::string& app_id,
-                             apps::mojom::PermissionPtr permission);
-
-  virtual void Uninstall(const std::string& app_id);
-
-  virtual void OpenNativeSettings(const std::string& app_id);
-
- protected:
-  apps::mojom::AppServicePtr app_service_;
-  apps::AppRegistryCache cache_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AppServiceProxy);
-};
-
-}  // namespace apps
-
-#endif  // CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_SERVICE_PROXY_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 512ebddb..f60af13 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -782,6 +782,7 @@
       "../browser/autofill/autofill_server_browsertest.cc",
       "../browser/autofill/content_autofill_driver_browsertest.cc",
       "../browser/autofill/form_structure_browsertest.cc",
+      "../browser/availability/availability_prober_browsertest.cc",
       "../browser/background_fetch/background_fetch_browsertest.cc",
       "../browser/background_sync/background_sync_browsertest.cc",
       "../browser/background_sync/background_sync_metrics_browsertest.cc",
@@ -1024,7 +1025,6 @@
       "../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",
@@ -2777,6 +2777,7 @@
     "../browser/autocomplete/chrome_autocomplete_scheme_classifier_unittest.cc",
     "../browser/autocomplete/search_provider_unittest.cc",
     "../browser/autocomplete/shortcuts_provider_extension_unittest.cc",
+    "../browser/availability/availability_prober_unittest.cc",
     "../browser/background_fetch/background_fetch_delegate_impl_unittest.cc",
     "../browser/background_fetch/background_fetch_permission_context_unittest.cc",
     "../browser/background_sync/background_sync_controller_impl_unittest.cc",
@@ -2813,7 +2814,6 @@
     "../browser/component_updater/optimization_hints_component_installer_unittest.cc",
     "../browser/component_updater/origin_trials_component_installer_unittest.cc",
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
-    "../browser/component_updater/supervised_user_whitelist_installer_unittest.cc",
     "../browser/component_updater/sw_reporter_installer_win_unittest.cc",
     "../browser/content_index/content_index_provider_unittest.cc",
     "../browser/content_settings/content_settings_default_provider_unittest.cc",
@@ -3066,7 +3066,6 @@
     "../browser/previews/previews_lite_page_predictor_unittest.cc",
     "../browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc",
     "../browser/previews/previews_offline_helper_unittest.cc",
-    "../browser/previews/previews_prober_unittest.cc",
     "../browser/previews/previews_service_unittest.cc",
     "../browser/previews/previews_top_host_provider_unittest.cc",
     "../browser/previews/previews_ui_tab_helper_unittest.cc",
@@ -3832,7 +3831,7 @@
 
   if (!is_android) {
     sources += [
-      "../browser/apps/app_service/app_service_proxy_impl_unittest.cc",
+      "../browser/apps/app_service/app_service_proxy_unittest.cc",
       "../browser/apps/intent_helper/apps_navigation_throttle_unittest.cc",
       "../browser/apps/intent_helper/page_transition_util_unittest.cc",
       "../browser/devtools/devtools_file_system_indexer_unittest.cc",
@@ -4432,7 +4431,6 @@
   }
   if (!is_chromeos && is_linux) {
     sources += [
-      "../browser/password_manager/native_backend_kwallet_x_unittest.cc",
       "../browser/shell_integration_linux_unittest.cc",
       "../browser/ui/input_method/input_method_engine_unittest.cc",
     ]
@@ -4567,6 +4565,11 @@
       "//components/safe_browsing/renderer:websocket_sb_handshake_throttle_unittest",
       "//components/safe_browsing/triggers:ad_redirect_trigger",
     ]
+
+    # TODO(drubery): Once b/138388200 has been addressed, enable this on Windows.
+    if (!is_win) {
+      sources += [ "../browser/safe_browsing/download_protection/multipart_uploader_unittest.cc" ]
+    }
   } else if (safe_browsing_mode == 2) {
     sources += [ "../browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc" ]
     deps += []
@@ -4670,21 +4673,6 @@
   } else {
     sources -= [ "../browser/password_manager/password_store_x_unittest.cc" ]
   }
-  if (use_gnome_keyring && current_cpu == "x64") {
-    # Only add this test for 64 bit builds because otherwise we need the 32
-    # bit library on 64 bit systems when running this test.
-    sources +=
-        [ "../browser/password_manager/native_backend_gnome_x_unittest.cc" ]
-    configs += [
-      "//components/os_crypt:gnome_keyring",
-      "//components/os_crypt:gnome_keyring_direct",
-    ]
-  }
-  if (is_linux && !is_chromeos && !use_ozone) {
-    sources +=
-        [ "../browser/password_manager/native_backend_libsecret_unittest.cc" ]
-    deps += [ "//third_party/libsecret" ]
-  }
   if (is_linux && use_aura) {
     deps += [ "//ui/aura:test_support" ]
     if (use_dbus) {
@@ -4941,6 +4929,7 @@
   }
   if (enable_supervised_users) {
     sources += [
+      "../browser/component_updater/supervised_user_whitelist_installer_unittest.cc",
       "../browser/content_settings/content_settings_supervised_provider_unittest.cc",
       "../browser/supervised_user/child_accounts/child_account_service_unittest.cc",
       "../browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc",
@@ -4980,6 +4969,7 @@
       "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
       "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
+      "../browser/ui/app_list/search/logging/search_ranking_event_logger_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc",
diff --git a/chrome/test/android/DEPS b/chrome/test/android/DEPS
index c470c190..56d481ae 100644
--- a/chrome/test/android/DEPS
+++ b/chrome/test/android/DEPS
@@ -2,5 +2,4 @@
   # This test code needs to depend on sync related classes and test tools.
   "+components/sync/android/java/src/org/chromium/components/sync/signin",
   "+components/sync/test/android/javatests/src/org/chromium/components/sync/test/util",
-  "+components/signin/core/browser/android/java/src/org/chromium/components/signin",
 ]
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleCardController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleCardController.java
index 56534220..1fa0419 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleCardController.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleCardController.java
@@ -105,14 +105,14 @@
                                 return null;
                             } else {
                                 if (headline == null)
-                                    throw UiLocationException.newInstance(
-                                            "Headline not found", headlineLocator, articleCard);
+                                    throw new UiLocationException(
+                                            "Headline not found.", headlineLocator, articleCard);
                                 else if (publisher == null)
-                                    throw UiLocationException.newInstance(
-                                            "Publisher not found", publisherLocator, articleCard);
+                                    throw new UiLocationException(
+                                            "Publisher not found.", publisherLocator, articleCard);
                                 else
-                                    throw UiLocationException.newInstance(
-                                            "Age not found", ageLocator, articleCard);
+                                    throw new UiLocationException(
+                                            "Age not found.", ageLocator, articleCard);
                             }
                         }
                     });
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java
index f7df404..2eaa52c 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java
@@ -86,8 +86,8 @@
                             // called again if we throw an exception.  This
                             // gives a chance for the title UI of the tile to
                             // load.
-                            throw UiLocationException.newInstance(
-                                    "Title is null", LOCATOR_TILE_TITLES, root);
+                            throw new UiLocationException(
+                                    "Title not found.", LOCATOR_TILE_TITLES, root);
                         } else {
                             // This is the last attempt.  It is possible that
                             // no complete tiles are found on the screen, just
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
index a257204..c3ec853 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
@@ -55,8 +55,9 @@
         if (matcher.matches()) {
             return Integer.valueOf(matcher.group(1));
         } else {
-            throw UiLocationException.newInstance(
-                    "Could not match " + text + " to " + PATTERN_NUMBER_OF_OPEN_TABS);
+            throw new UiLocationException(
+                    "Could not get number of open tabs in Tab Switcher button.",
+                    LOCATOR_TAB_SWITCHER_BUTTON);
         }
     }
     public NewTabPageController clickNewTab() {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
index 983603d..079a789 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
@@ -40,7 +40,7 @@
                 return instance;
             }
         }
-        throw UiLocationException.newInstance("Could not detect current Page");
+        throw new UiLocationException("Could not detect current Page.");
     }
 
     /** Launch the chrome application */
@@ -71,8 +71,8 @@
         if (controller instanceof NewTabPageController) {
             return (NewTabPageController) controller;
         } else {
-            throw UiLocationException.newInstance(
-                    "Could not navigate to new tab page from " + controller.getClass().getName());
+            throw new UiLocationException("Could not navigate to new tab page from "
+                    + controller.getClass().getName() + ".");
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java
index 3f784f7..0e3944b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java
@@ -342,15 +342,14 @@
         int iterationsLeft = MAX_SWIPES;
         while (!mLocatorHelper.isOnScreen(locator) && iterationsLeft-- > 0) {
             if (mLocatorHelper.isOnScreen(stopLocator))
-                throw UiLocationException.newInstance(
-                        "Did not find locator while swiping to " + stopLocator, locator, null);
+                throw new UiLocationException(
+                        "Did not find locator while swiping to " + stopLocator + ".", locator);
             swipeVertically(fractionOfScreen);
             Utils.sleep(UiLocatorHelper.UI_CHECK_INTERVAL_MS);
         }
         if (!mLocatorHelper.isOnScreen(locator)) {
-            throw UiLocationException.newInstance(
-                    "Did not find locator after swiping for " + MAX_SWIPES + " times", locator,
-                    null);
+            throw new UiLocationException(
+                    "Did not find locator after swiping for " + MAX_SWIPES + " times.", locator);
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocationException.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocationException.java
index a26c213..02676227 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocationException.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocationException.java
@@ -8,53 +8,40 @@
 
 /**
  * Exception class that represents an unexpected failure when trying to find
- * a UI element.
+ * a UI node.
  */
 public class UiLocationException extends IllegalStateException {
-    private final UiObject2 mUiObject2;
-    private final IUi2Locator mIUi2Locator;
-
-    public UiObject2 getUiObject2() {
-        return mUiObject2;
-    }
-
-    public IUi2Locator getIUi2Locator() {
-        return mIUi2Locator;
-    }
-
-    private UiLocationException(
-            String message, Throwable cause, UiObject2 mUiObject2, IUi2Locator mIUi2Locator) {
-        super(message, cause);
-        this.mUiObject2 = mUiObject2;
-        this.mIUi2Locator = mIUi2Locator;
-    }
-
     /**
-     * Creates a UiLocationException exception.
+     * Creates a UiLocationException exception when the locator used is not
+     * known.
      *
      * @param msg The error message.
-     * @return    UiLocationException with msg.
      */
-    public static UiLocationException newInstance(String msg) {
-        return newInstance(msg, null, null);
+    public UiLocationException(String msg) {
+        super(msg);
     }
 
     /**
-     * Creates a UiLocationException exception.
+     * Creates a UiLocationException exception when the locator failed to find
+     * any nodes in the root node.
      *
      * @param msg     The error message.
      * @param locator The locator that failed to find any nodes.
      * @param root    The root that the locator searched under, or null if all the nodes were
      *                searched.
-     * @return        UiLocationException with msg, locator, and root.
      */
-    public static UiLocationException newInstance(String msg, IUi2Locator locator, UiObject2 root) {
-        if (root == null) {
-            return new UiLocationException(
-                    msg + " Locator: " + locator + " on device", null, root, locator);
-        } else {
-            return new UiLocationException(
-                    msg + " Locator: " + locator + " in " + root.toString(), null, root, locator);
-        }
+    public UiLocationException(String msg, IUi2Locator locator, UiObject2 root) {
+        this(msg + " (Locator=" + locator + " in root=" + root + ")");
+    }
+
+    /**
+     * Creates a UiLocationException exception when the locator failed to find
+     * any nodes on the device.
+     *
+     * @param msg     The error message.
+     * @param locator The locator that failed to find any nodes.
+     */
+    public UiLocationException(String msg, IUi2Locator locator) {
+        this(msg + " (Locator=" + locator + " on device)");
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
index 658221f..40f2f16 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
@@ -42,7 +42,7 @@
         if (object2.isCheckable()) {
             return object2.isChecked();
         } else {
-            throw UiLocationException.newInstance("Item is not checkable", null, object2);
+            throw new UiLocationException("Item in " + object2 + " is not checkable.");
         }
     };
 
@@ -288,9 +288,9 @@
             }
             Utils.sleep(UI_CHECK_INTERVAL_MS);
         } while (true);
-        throw UiLocationException.newInstance("Could not find any objects after " + elapsedTime
-                        + " ms and " + attempts + " attempts",
-                locator, null);
+        throw new UiLocationException("Could not find any objects after " + elapsedTime + " ms and "
+                        + attempts + " attempts.",
+                locator);
     }
 
     /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
index bdf4aff..b3a5e4d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
@@ -5,8 +5,6 @@
 package org.chromium.chrome.test.util;
 
 import android.app.Activity;
-import android.app.DialogFragment;
-import android.app.Fragment;
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
 import android.content.Context;
@@ -40,33 +38,6 @@
     private static final long CONDITION_POLL_INTERVAL_MS = 100;
 
     /**
-     * Waits for a particular fragment to be present on a given activity.
-     */
-    private static class FragmentPresentCriteria extends Criteria {
-
-        private final Activity mActivity;
-        private final String mFragmentTag;
-
-        public FragmentPresentCriteria(Activity activity, String fragmentTag) {
-            super(String.format("Could not locate the fragment with tag '%s'", fragmentTag));
-            mActivity = activity;
-            mFragmentTag = fragmentTag;
-        }
-
-        @Override
-        public boolean isSatisfied() {
-            Fragment fragment = mActivity.getFragmentManager().findFragmentByTag(mFragmentTag);
-            if (fragment == null) return false;
-            if (fragment instanceof DialogFragment) {
-                DialogFragment dialogFragment = (DialogFragment) fragment;
-                return dialogFragment.getDialog() != null
-                        && dialogFragment.getDialog().isShowing();
-            }
-            return fragment.getView() != null;
-        }
-    }
-
-    /**
      * Captures an activity of a particular type by launching an intent explicitly targeting the
      * activity.
      *
@@ -179,24 +150,6 @@
      * @param activity The activity that owns the fragment.
      * @param fragmentTag The tag of the fragment to be loaded.
      */
-    @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-    public static <T> T waitForFragment(Activity activity, String fragmentTag) {
-        CriteriaHelper.pollInstrumentationThread(new FragmentPresentCriteria(activity, fragmentTag),
-                ACTIVITY_START_TIMEOUT_MS, CONDITION_POLL_INTERVAL_MS);
-        return (T) activity.getFragmentManager().findFragmentByTag(fragmentTag);
-    }
-
-    /**
-     * TODO(crbug.com/967022): This method is a duplicate of {@link #waitForFragment(Activity,
-     * String)}, but with Support Library classes replacing deprecated Framework classes. When
-     * Preference Support Library migration is complete remove {@link #waitForFragment(Activity,
-     * String)} and {@link FragmentPresentCriteria} in favor of this method.
-     *
-     * Waits for a fragment to be registered by the specified activity.
-     *
-     * @param activity The activity that owns the fragment.
-     * @param fragmentTag The tag of the fragment to be loaded.
-     */
     @SuppressWarnings("unchecked")
     public static <T extends android.support.v4.app.Fragment> T waitForFragmentCompat(
             AppCompatActivity activity, String fragmentTag) {
@@ -226,35 +179,6 @@
      * quickly and we can miss the time that a fragment is visible. This method allows you to get a
      * reference to any fragment that was attached to the activity at any point.
      *
-     * @param <T> A subclass of android.app.Fragment
-     * @param activity An instance or subclass of Preferences
-     * @param fragmentClass The class object for T
-     * @return A reference to the requested fragment or null.
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends Fragment> T waitForFragmentToAttach(
-            final Preferences activity, final Class<T> fragmentClass) {
-        CriteriaHelper.pollInstrumentationThread(
-                new Criteria("Could not find fragment " + fragmentClass) {
-                    @Override
-                    public boolean isSatisfied() {
-                        return fragmentClass.isInstance(activity.getMainFragment());
-                    }
-                }, ACTIVITY_START_TIMEOUT_MS, CONDITION_POLL_INTERVAL_MS);
-        return (T) activity.getMainFragment();
-    }
-
-    /**
-     * TODO(crbug.com/967022): This method is a duplicate of {@link
-     * #waitForFragmentToAttach(Preferences, Class)}, but with Support Library classes replacing
-     * deprecated Framework classes. When Preference Support Library migration is complete remove
-     * {@link #waitForFragmentToAttach(Preferences, Class)} in favor of this method.
-     *
-     * Waits until the specified fragment has been attached to the specified activity. Note that
-     * we don't guarantee that the fragment is visible. Some UI operations can happen too
-     * quickly and we can miss the time that a fragment is visible. This method allows you to get a
-     * reference to any fragment that was attached to the activity at any point.
-     *
      * @param <T> A subclass of {@link android.support.v4.app.Fragment}.
      * @param activity An instance or subclass of {@link Preferences}.
      * @param fragmentClass The class object for {@link T}.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeSigninUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeSigninUtils.java
index f432de2..26abd7b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeSigninUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeSigninUtils.java
@@ -17,7 +17,6 @@
 
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.ChromeSigninController;
-import org.chromium.components.signin.CoreAccountInfo;
 import org.chromium.components.signin.test.util.AccountHolder;
 import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
 
@@ -86,11 +85,9 @@
      * Removes all fake accounts from the OS.
      */
     public void removeAllFakeAccountsFromOs() throws Exception {
-        for (CoreAccountInfo accountInfo : mFakeAccountManagerDelegate.getAccountsSyncNoThrow()) {
+        for (Account acct : mFakeAccountManagerDelegate.getAccountsSyncNoThrow()) {
             mFakeAccountManagerDelegate.removeAccountHolderBlocking(
-                    AccountHolder.builder(accountInfo.getAccount())
-                            .accountId(accountInfo.getId())
-                            .build());
+                    AccountHolder.builder(acct).build());
         }
     }
 
@@ -101,8 +98,8 @@
      * @return {@code true} if fake account is on OS, false otherwise.
      */
     public boolean isExistingFakeAccountOnOs(String username) {
-        for (CoreAccountInfo accountInfo : mFakeAccountManagerDelegate.getAccountsSyncNoThrow()) {
-            if (username.equals(accountInfo.getName())) {
+        for (Account acct : mFakeAccountManagerDelegate.getAccountsSyncNoThrow()) {
+            if (username.equals(acct.name)) {
                 return true;
             }
         }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
index c493222d..b7a2052 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
@@ -14,7 +14,6 @@
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.ChromeSigninController;
-import org.chromium.components.signin.CoreAccountInfo;
 import org.chromium.components.signin.OAuth2TokenService;
 import org.chromium.components.signin.test.util.AccountHolder;
 import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
@@ -104,12 +103,13 @@
     }
 
     private static void seedAccounts() {
-        List<CoreAccountInfo> accountInfos = sAccountManager.getAccountsSyncNoThrow();
-        String[] accountNames = new String[accountInfos.size()];
-        String[] accountIds = new String[accountInfos.size()];
-        for (int i = 0; i < accountInfos.size(); i++) {
-            accountNames[i] = accountInfos.get(i).getName();
-            accountIds[i] = accountInfos.get(i).getId().getGaiaIdAsString();
+        AccountIdProvider accountIdProvider = AccountIdProvider.getInstance();
+        Account[] accounts = sAccountManager.getAccountsSyncNoThrow();
+        String[] accountNames = new String[accounts.length];
+        String[] accountIds = new String[accounts.length];
+        for (int i = 0; i < accounts.length; i++) {
+            accountNames[i] = accounts[i].name;
+            accountIds[i] = accountIdProvider.getAccountId(accounts[i].name);
         }
         IdentityServicesProvider.getAccountTrackerService().syncForceRefreshForTest(
                 accountIds, accountNames);
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 0386c5a..2cfff5c 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -57,6 +57,7 @@
 #include "chrome/test/base/chrome_test_suite.h"
 #include "chrome/test/base/test_launcher_utils.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/google/core/common/google_util.h"
 #include "components/os_crypt/os_crypt_mocker.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -319,10 +320,8 @@
 }
 
 void InProcessBrowserTest::CloseBrowserSynchronously(Browser* browser) {
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser));
   CloseBrowserAsynchronously(browser);
-  observer.Wait();
+  ui_test_utils::WaitForBrowserToClose(browser);
 }
 
 void InProcessBrowserTest::CloseBrowserAsynchronously(Browser* browser) {
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 423ba03..5d6159af 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -127,8 +127,8 @@
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override {}
 #if !defined(OS_ANDROID)
   void ShowIntentPickerBubble(std::vector<apps::IntentPickerAppInfo> app_info,
-                              bool enable_stay_in_chrome,
-                              bool show_persistence_options,
+                              bool show_stay_in_chrome,
+                              bool show_remember_selection,
                               IntentPickerResponse callback) override {}
 #endif  //  !define(OS_ANDROID)
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 6e020e9..1022469 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -370,10 +370,12 @@
   return nullptr;
 }
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 component_updater::SupervisedUserWhitelistInstaller*
 TestingBrowserProcess::supervised_user_whitelist_installer() {
   return nullptr;
 }
+#endif
 
 MediaFileSystemRegistry* TestingBrowserProcess::media_file_system_registry() {
 #if defined(OS_ANDROID)
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 7cfbf01c..a2df097 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -121,8 +121,10 @@
 #endif
 
   component_updater::ComponentUpdateService* component_updater() override;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   component_updater::SupervisedUserWhitelistInstaller*
   supervised_user_whitelist_installer() override;
+#endif
   MediaFileSystemRegistry* media_file_system_registry() override;
 
   WebRtcLogUploader* webrtc_log_uploader() override;
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 34beb1d..08fc569 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -437,8 +437,10 @@
     user_prefs::UserPrefs::Set(this, prefs_.get());
   else if (IsOffTheRecord())
     CreateIncognitoPrefService();
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   else if (!supervised_user_id_.empty())
     CreatePrefServiceForSupervisedUser();
+#endif
   else
     CreateTestingPrefService();
 
@@ -821,6 +823,7 @@
   RegisterUserProfilePrefs(testing_prefs_->registry());
 }
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 void TestingProfile::CreatePrefServiceForSupervisedUser() {
   DCHECK(!prefs_.get());
   DCHECK(!supervised_user_id_.empty());
@@ -839,6 +842,7 @@
   RegisterUserProfilePrefs(registry.get());
   user_prefs::UserPrefs::Set(this, prefs_.get());
 }
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 void TestingProfile::CreateIncognitoPrefService() {
   DCHECK(original_profile_);
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 8a9c81e0..6dd27277 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -16,6 +16,7 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/buildflags.h"
 #include "components/domain_reliability/clear_mode.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/simple_factory_key.h"
@@ -419,9 +420,11 @@
   // Creates a TestingPrefService and associates it with the TestingProfile.
   void CreateTestingPrefService();
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // Creates a pref service that uses SupervisedUserPrefStore and associates
   // it with the TestingProfile.
   void CreatePrefServiceForSupervisedUser();
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
   // Initializes |prefs_| for an incognito profile, derived from
   // |original_profile_|.
diff --git a/chrome/test/data/BUILD.gn b/chrome/test/data/BUILD.gn
index f976695..befb2de 100644
--- a/chrome/test/data/BUILD.gn
+++ b/chrome/test/data/BUILD.gn
@@ -29,7 +29,4 @@
   sources = [
     "webui/web_ui_test.mojom",
   ]
-
-  # TODO(https://crbug.com/968369): Change to use new names.
-  use_old_js_lite_bindings_names = true
 }
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeDisabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1
similarity index 100%
rename from chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1
rename to chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeDisabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeDisabled-bookmark_manager_one_folder.Nexus_5-19.png.sha1
similarity index 100%
rename from chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1
rename to chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeDisabled-bookmark_manager_one_folder.Nexus_5-19.png.sha1
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..2608ec14
--- /dev/null
+++ b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+82717411fb282589ba37ac01f0e5d81a3c12babc
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_one_folder.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_one_folder.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..c5c2a7e1
--- /dev/null
+++ b/chrome/test/data/android/render_tests/BookmarkReorderTest.NightModeEnabled-bookmark_manager_one_folder.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+b2b67ed9e0c888c4076d7fdf5de394743def87f7
diff --git a/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeDisabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeDisabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
index c3c9603f..6e078d2e 100644
--- a/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeDisabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
+++ b/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeDisabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
@@ -1 +1 @@
-329f6dd0d04005e2f71bdafb3bb322574746c7a2
\ No newline at end of file
+cfaf40f75d21ecfba91edcf14a796a950270cb69
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeEnabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeEnabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
index d33a04f2..69defdb 100644
--- a/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeEnabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
+++ b/chrome/test/data/android/render_tests/RevampedContextMenuRenderTest.NightModeEnabled-revamped_context_menu_with_image_link.Nexus_5-19.png.sha1
@@ -1 +1 @@
-e1e15f28009525bc57b03b5860b80e69061cb753
\ No newline at end of file
+b3b76225f96fb6ed97b73198f0dc1c77804dab38
\ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js b/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
index c9365be..c3e0264 100644
--- a/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
+++ b/chrome/test/data/extensions/api_test/certificate_provider/request_pin/operated.js
@@ -64,6 +64,9 @@
     case 'RequestWithZeroAttempts':
       requestPin({signRequestId: SIGN_REQUEST_ID, attemptsLeft: 0});
       break;
+    case 'RequestWithNegativeAttempts':
+      requestPin({signRequestId: SIGN_REQUEST_ID, attemptsLeft: -1});
+      break;
     case 'Stop':
       stopPinRequest({signRequestId: SIGN_REQUEST_ID});
       break;
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
index b954084a..f4d763a 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
@@ -207,7 +207,7 @@
     page = document.createElement('settings-cups-printers');
     // Enable feature flag to show the new saved printers list.
     // TODO(jimmyxgong): Remove this line when the feature flag is removed.
-    page.enableUpdatedUI_ = true;
+    page.enableUpdatedUi_ = true;
     document.body.appendChild(page);
     assertTrue(!!page);
 
@@ -386,7 +386,7 @@
     page = document.createElement('settings-cups-printers');
     // Enable feature flag to show the new saved printers list.
     // TODO(jimmyxgong): Remove this line when the feature flag is removed.
-    page.enableUpdatedUI_ = true;
+    page.enableUpdatedUi_ = true;
     document.body.appendChild(page);
     assertTrue(!!page);
 
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
index fb689a2d..a774a6c 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
@@ -168,7 +168,7 @@
       });
 
   test(
-      'AndroidMessages set up button is disabled when prohibited by policy',
+      'AndroidMessages toggle is disabled when prohibited by policy',
       function() {
         // Verify that setup button is disabled when prohibited by policy.
         multideviceSubpage.pageContentData =
@@ -181,9 +181,7 @@
 
         let setUpButton =
             multideviceSubpage.$$('#messagesItem > [slot=feature-controller]');
-        assertTrue(!!setUpButton);
-        assertTrue(setUpButton.tagName.includes('BUTTON'));
-        assertTrue(setUpButton.disabled);
+        assertFalse(!!setUpButton);
 
         // Verify that setup button is not disabled when feature is enabled.
         setAndroidSmsPairingComplete(false);
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index 2ad9f784..9cb97ac7 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -23,8 +23,8 @@
         lifetimeBrowserProxy = new settings.TestLifetimeBrowserProxy();
         settings.LifetimeBrowserProxyImpl.instance_ = lifetimeBrowserProxy;
 
-        resetPageBrowserProxy = new reset_page.TestResetBrowserProxy();
-        settings.ResetOsProxyImpl.instance_ = resetPageBrowserProxy;
+        resetPageBrowserProxy = new reset_page.TestOsResetBrowserProxy();
+        settings.OsResetBrowserProxyImpl.instance_ = resetPageBrowserProxy;
 
         PolymerTest.clearBody();
         resetPage = document.createElement('os-settings-reset-page');
diff --git a/chrome/test/data/webui/settings/chromeos/os_search_page_test.js b/chrome/test/data/webui/settings/chromeos/os_search_page_test.js
new file mode 100644
index 0000000..f2fa3a1d
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_search_page_test.js
@@ -0,0 +1,122 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('os_settings_search_page', function() {
+  function generateSearchEngineInfo() {
+    const searchEngines0 =
+        settings_search.createSampleSearchEngine(true, false, false);
+    searchEngines0.default = true;
+    const searchEngines1 =
+        settings_search.createSampleSearchEngine(true, false, false);
+    searchEngines1.default = false;
+    const searchEngines2 =
+        settings_search.createSampleSearchEngine(true, false, false);
+    searchEngines2.default = false;
+
+    return {
+      defaults: [searchEngines0, searchEngines1, searchEngines2],
+      others: [],
+      extensions: [],
+    };
+  }
+
+  suite('OSSearchPageTests', function() {
+    /** @type {?SettingsSearchPageElement} */
+    let page = null;
+
+    let browserProxy = null;
+
+    setup(function() {
+      browserProxy = new settings_search.TestSearchEnginesBrowserProxy();
+      browserProxy.setSearchEnginesInfo(generateSearchEngineInfo());
+      settings.SearchEnginesBrowserProxyImpl.instance_ = browserProxy;
+      PolymerTest.clearBody();
+      page = document.createElement('os-settings-search-page');
+      page.prefs = {
+        default_search_provider_data: {
+          template_url_data: {},
+        },
+      };
+      document.body.appendChild(page);
+    });
+
+    teardown(function() {
+      page.remove();
+    });
+
+    // Tests that the page is querying and displaying search engine info on
+    // startup.
+    test('Initialization', async () => {
+      const selectElement = page.$$('select');
+      assertTrue(!!selectElement);
+      assertTrue(!!page.$$('#help-icon'));
+
+      await browserProxy.whenCalled('getSearchEnginesList');
+      Polymer.dom.flush();
+      assertEquals(0, selectElement.selectedIndex);
+
+      // Simulate a user initiated change of the default search engine.
+      selectElement.selectedIndex = 1;
+      selectElement.dispatchEvent(new CustomEvent('change'));
+      await browserProxy.whenCalled('setDefaultSearchEngine');
+      assertEquals(1, selectElement.selectedIndex);
+
+      // Simulate a change that happened in browser settings.
+      const searchEnginesInfo = generateSearchEngineInfo();
+      searchEnginesInfo.defaults[0].default = false;
+      searchEnginesInfo.defaults[1].default = false;
+      searchEnginesInfo.defaults[2].default = true;
+
+      browserProxy.resetResolver('setDefaultSearchEngine');
+      cr.webUIListenerCallback('search-engines-changed', searchEnginesInfo);
+      Polymer.dom.flush();
+      assertEquals(2, selectElement.selectedIndex);
+
+      browserProxy.whenCalled('setDefaultSearchEngine').then(function() {
+        // Since the change happened in a different tab, there should be
+        // no new call to |setDefaultSearchEngine|.
+        assertNotReached('Should not call setDefaultSearchEngine again');
+      });
+    });
+
+    test('ControlledByExtension', async () => {
+      await browserProxy.whenCalled('getSearchEnginesList');
+      const selectElement = page.$$('select');
+      assertFalse(selectElement.disabled);
+      assertFalse(!!page.$$('extension-controlled-indicator'));
+
+      page.set('prefs.default_search_provider_data.template_url_data', {
+        controlledBy: chrome.settingsPrivate.ControlledBy.EXTENSION,
+        controlledByName: 'fake extension name',
+        enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
+        extensionId: 'fake extension id',
+        extensionCanBeDisabled: true,
+        value: {},
+      });
+      Polymer.dom.flush();
+
+      assertTrue(selectElement.disabled);
+      assertTrue(!!page.$$('extension-controlled-indicator'));
+      assertFalse(!!page.$$('cr-policy-pref-indicator'));
+    });
+
+    test('ControlledByPolicy', async () => {
+      await browserProxy.whenCalled('getSearchEnginesList');
+      const selectElement = page.$$('select');
+      assertFalse(selectElement.disabled);
+      assertFalse(!!page.$$('extension-controlled-indicator'));
+
+      page.set('prefs.default_search_provider_data.template_url_data', {
+        controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
+        enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
+        value: {},
+      });
+      Polymer.dom.flush();
+
+      assertTrue(selectElement.disabled);
+      assertFalse(!!page.$$('extension-controlled-indicator'));
+      assertTrue(!!page.$$('cr-policy-pref-indicator'));
+    });
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 5859cb4..523fe6c0 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -821,8 +821,8 @@
     return super.extraLibraries.concat([
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'test_lifetime_browser_proxy.js',
-      BROWSER_SETTINGS_PATH + 'test_reset_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'test_util.js',
+      'test_os_reset_browser_proxy.js',
       'os_reset_page_test.js',
     ]);
   }
@@ -832,6 +832,29 @@
   mocha.run();
 });
 
+// Test fixture for the "Search and assistant" page.
+// eslint-disable-next-line no-var
+var OSSettingsSearchPageTest = class extends OSSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'chromeos/os_search_page/os_search_page.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      BROWSER_SETTINGS_PATH + 'test_search_engines_browser_proxy.js',
+      'os_search_page_test.js',
+    ]);
+  }
+};
+
+// Settings tests are flaky on debug. See https://crbug.com/968608.
+TEST_F('OSSettingsSearchPageTest', 'MAYBE_AllJsTests', () => {
+  mocha.run();
+});
+
 // Test fixture for the Smb Shares page.
 // eslint-disable-next-line no-var
 var OSSettingsSmbPageTest = class extends OSSettingsBrowserTest {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
index 589a33d..1ac227c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
@@ -28,7 +28,15 @@
   }
 };
 
-TEST_F('OSSettingsUIBrowserTest', 'All', () => {
+// Timeouts in debug because the page can be slow to load.
+// https://crbug.com/987512
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_AllJsTests DISABLED_AllJsTests');
+GEN('#else');
+GEN('#define MAYBE_AllJsTests AllJsTests');
+GEN('#endif');
+
+TEST_F('OSSettingsUIBrowserTest', 'MAYBE_AllJsTests', () => {
   suite('os-settings-ui', () => {
     let ui;
 
diff --git a/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js
new file mode 100644
index 0000000..ef74482d
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js
@@ -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.
+
+cr.define('reset_page', function() {
+  /** @implements {settings.OsResetBrowserProxy} */
+  class TestOsResetBrowserProxy extends TestBrowserProxy {
+    constructor() {
+      super([
+        'onPowerwashDialogShow',
+      ]);
+    }
+
+    /** @override */
+    onPowerwashDialogShow() {
+      this.methodCalled('onPowerwashDialogShow');
+    }
+  }
+
+  return {
+    TestOsResetBrowserProxy: TestOsResetBrowserProxy,
+  };
+});
diff --git a/chrome/test/data/webui/settings/settings_ui_browsertest.js b/chrome/test/data/webui/settings/settings_ui_browsertest.js
index 15d38fbf..ac0a2d8 100644
--- a/chrome/test/data/webui/settings/settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_ui_browsertest.js
@@ -26,13 +26,8 @@
 // and several times that in a Debug build. See https://crbug.com/558434
 // and http://crbug.com/711256.
 
-GEN('#if !defined(NDEBUG) || defined(OS_MACOSX)');
-GEN('#define MAYBE_All DISABLED_All');
-GEN('#else');
-GEN('#define MAYBE_All All');
-GEN('#endif');
-
-TEST_F('SettingsUIBrowserTest', 'MAYBE_All', function() {
+// Disabling everywhere, see flaky failures at crbug.com/986985.
+TEST_F('SettingsUIBrowserTest', 'DISABLED_All', function() {
   suite('settings-ui', function() {
     let toolbar;
     let ui;
diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js
index 4eb227b7..2e4327c 100644
--- a/chrome/test/data/webui/test_api.js
+++ b/chrome/test/data/webui/test_api.js
@@ -704,9 +704,9 @@
         Mojo.bindInterface(
             webUiTest.mojom.TestRunner.name,
             mojo.makeRequest(testRunner).handle);
-      } else if (webUiTest.mojom.TestRunnerProxy) {
+      } else if (webUiTest.mojom.TestRunnerRemote) {
         // For mojo-lite WebUI tests.
-        testRunner = webUiTest.mojom.TestRunner.getProxy();
+        testRunner = webUiTest.mojom.TestRunner.getRemote();
       } else {
         assertNotReached(
             'Mojo bindings found, but no valid test interface loaded');
diff --git a/chrome/updater/win/net/network_unittest.cc b/chrome/updater/win/net/network_unittest.cc
index 66d2e8a5..8500a39 100644
--- a/chrome/updater/win/net/network_unittest.cc
+++ b/chrome/updater/win/net/network_unittest.cc
@@ -3,15 +3,17 @@
 // found in the LICENSE file.
 
 #include "chrome/updater/win/net/network.h"
+
 #include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace updater {
 
 TEST(UpdaterTestNetwork, NetworkFetcherWinHTTPFactory) {
-  base::MessageLoopForUI message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
   auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
   EXPECT_NE(nullptr, fetcher.get());
 }
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 1ad1ebb4..b043dd2e 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -373,10 +373,10 @@
 
   if (enable_cast_wayland_server) {
     sources += [
+      "exo/cast_wm_helper.cc",
+      "exo/cast_wm_helper.h",
       "exo/wayland_server_controller.cc",
       "exo/wayland_server_controller.h",
-      "exo/wm_helper_cast_shell.cc",
-      "exo/wm_helper_cast_shell.h",
       "webview/webview_controller.cc",
       "webview/webview_controller.h",
       "webview/webview_grpc_service.cc",
diff --git a/chromecast/browser/accessibility/touch_exploration_controller.cc b/chromecast/browser/accessibility/touch_exploration_controller.cc
index a83ed3b..c8ef3f0 100644
--- a/chromecast/browser/accessibility/touch_exploration_controller.cc
+++ b/chromecast/browser/accessibility/touch_exploration_controller.cc
@@ -7,6 +7,8 @@
 
 #include "chromecast/browser/accessibility/touch_exploration_controller.h"
 
+#include <algorithm>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -52,8 +54,7 @@
       prev_state_(NO_FINGERS_DOWN),
       DVLOG_on_(true) {}
 
-TouchExplorationController::~TouchExplorationController() {
-}
+TouchExplorationController::~TouchExplorationController() {}
 
 void TouchExplorationController::SetTouchAccessibilityAnchorPoint(
     const gfx::Point& anchor_point_dip) {
@@ -66,9 +67,9 @@
   exclude_bounds_ = bounds;
 }
 
-ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
+ui::EventDispatchDetails TouchExplorationController::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (!event.IsTouchEvent()) {
     if (event.IsKeyEvent()) {
       const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
@@ -77,7 +78,7 @@
                << ", Flags: " << key_event.flags()
                << ", Is char: " << key_event.is_char();
     }
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
   const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
 
@@ -97,10 +98,10 @@
     bool in_exclude_area = exclude_bounds_.Contains(location);
     if (in_exclude_area) {
       if (state_ == NO_FINGERS_DOWN)
-        return ui::EVENT_REWRITE_CONTINUE;
+        return SendEvent(continuation, &event);
       if (touch_event.type() == ui::ET_TOUCH_MOVED ||
           touch_event.type() == ui::ET_TOUCH_PRESSED) {
-        return ui::EVENT_REWRITE_DISCARD;
+        return DiscardEvent(continuation);
       }
       // Otherwise, continue handling events. Basically, we want to let
       // CANCELLED or RELEASE events through so this can get back to
@@ -147,12 +148,11 @@
         new_event->set_location(location);
         new_event->set_root_location(root_location);
         new_event->set_flags(touch_event.flags());
-        *rewritten_event = std::move(new_event);
-        return ui::EVENT_REWRITE_REWRITTEN;
+        return SendEventFinally(continuation, new_event.get());
       }
 
       // Otherwise just pass it through.
-      return ui::EVENT_REWRITE_CONTINUE;
+      return SendEvent(continuation, &event);
     }
 
     current_touch_ids_.erase(it);
@@ -163,12 +163,12 @@
 
     // Can happen if touch exploration is enabled while fingers were down.
     if (it == current_touch_ids_.end())
-      return ui::EVENT_REWRITE_CONTINUE;
+      return SendEvent(continuation, &event);
 
     touch_locations_[*it] = gfx::PointF(location);
   } else {
     NOTREACHED() << "Unexpected event type received: " << event.GetName();
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
   DVLOG_EVENT(touch_event);
 
@@ -185,7 +185,7 @@
         DVLOG(1) << "Reset to no fingers in Rewrite event because the touch  "
                     "release or cancel was on the edge of the screen.";
       }
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
   }
 
@@ -208,102 +208,83 @@
     ProcessGestureEvents();
   }
 
-  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
   // The rest of the processing depends on what state we're in.
   switch (state_) {
     case NO_FINGERS_DOWN:
-      status = InNoFingersDown(touch_event_dip, rewritten_event);
-      break;
+      return InNoFingersDown(touch_event_dip, continuation);
     case SINGLE_TAP_PRESSED:
-      status = InSingleTapPressed(touch_event_dip, rewritten_event);
-      break;
+      return InSingleTapPressed(touch_event_dip, continuation);
     case SINGLE_TAP_RELEASED:
     case TOUCH_EXPLORE_RELEASED:
-      status =
-          InSingleTapOrTouchExploreReleased(touch_event_dip, rewritten_event);
-      break;
+      return InSingleTapOrTouchExploreReleased(touch_event_dip, continuation);
     case DOUBLE_TAP_PENDING:
-      status = InDoubleTapPending(touch_event_dip, rewritten_event);
-      break;
+      return InDoubleTapPending(touch_event_dip, continuation);
     case TOUCH_RELEASE_PENDING:
-      status = InTouchReleasePending(touch_event_dip, rewritten_event);
-      break;
+      return InTouchReleasePending(touch_event_dip, continuation);
     case TOUCH_EXPLORATION:
-      status = InTouchExploration(touch_event_dip, rewritten_event);
-      break;
+      return InTouchExploration(touch_event_dip, continuation);
     case GESTURE_IN_PROGRESS:
-      status = InGestureInProgress(touch_event_dip, rewritten_event);
-      break;
+      return InGestureInProgress(touch_event_dip, continuation);
     case TOUCH_EXPLORE_SECOND_PRESS:
-      status = InTouchExploreSecondPress(touch_event_dip, rewritten_event);
-      break;
+      return InTouchExploreSecondPress(touch_event_dip, continuation);
     case ONE_FINGER_PASSTHROUGH:
-      status = InOneFingerPassthrough(touch_event_dip, rewritten_event);
-      break;
+      return InOneFingerPassthrough(touch_event_dip, continuation);
     case WAIT_FOR_NO_FINGERS:
-      status = InWaitForNoFingers(touch_event_dip, rewritten_event);
-      break;
+      return InWaitForNoFingers(touch_event_dip, continuation);
     case TWO_FINGER_TAP:
-      status = InTwoFingerTap(touch_event_dip, rewritten_event);
-      break;
+      return InTwoFingerTap(touch_event_dip, continuation);
   }
-  if (status == ui::EVENT_REWRITE_REWRITTEN) {
-    DCHECK(rewritten_event->get());
-    SetTouchAccessibilityFlag(rewritten_event->get());
-  }
-  return status;
-}
-
-ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InNoFingersDown(
+ui::EventDispatchDetails TouchExplorationController::InNoFingersDown(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
   if (type != ui::ET_TOUCH_PRESSED) {
     NOTREACHED() << "Unexpected event type received: " << event.GetName();
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   initial_press_ = std::make_unique<ui::TouchEvent>(event);
+  initial_press_continuation_ = continuation;
   most_recent_press_timestamp_ = initial_press_->time_stamp();
   initial_presses_[event.pointer_details().id] = event.location();
   last_unused_finger_event_ = std::make_unique<ui::TouchEvent>(event);
+  last_unused_finger_continuation_ = continuation;
   StartTapTimer();
   SET_STATE(SINGLE_TAP_PRESSED);
-  return ui::EVENT_REWRITE_DISCARD;
+  return DiscardEvent(continuation);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
+ui::EventDispatchDetails TouchExplorationController::InSingleTapPressed(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
 
   if (type == ui::ET_TOUCH_PRESSED) {
     initial_presses_[event.pointer_details().id] = event.location();
     SET_STATE(TWO_FINGER_TAP);
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
     if (current_touch_ids_.size() == 0 &&
         event.pointer_details().id == initial_press_->pointer_details().id) {
-      MaybeSendSimulatedTapInLiftActivationBounds(event);
+      MaybeSendSimulatedTapInLiftActivationBounds(event, continuation);
       SET_STATE(SINGLE_TAP_RELEASED);
     } else if (current_touch_ids_.size() == 0) {
       SET_STATE(NO_FINGERS_DOWN);
     }
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_MOVED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_MOVED) {
     float distance = (event.location() - initial_press_->location()).Length();
     // If the user does not move far enough from the original position, then the
     // resulting movement should not be considered to be a deliberate gesture or
     // touch exploration.
     if (distance <= gesture_detector_config_.touch_slop)
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
 
     float delta_time =
         (event.time_stamp() - most_recent_press_timestamp_).InSecondsF();
@@ -319,27 +300,27 @@
     // gesture detection. Otherwise, jump to the touch exploration mode early.
     if (velocity > gesture_detector_config_.minimum_swipe_velocity) {
       SET_STATE(GESTURE_IN_PROGRESS);
-      return InGestureInProgress(event, rewritten_event);
+      return InGestureInProgress(event, continuation);
     }
     anchor_point_state_ = ANCHOR_POINT_FROM_TOUCH_EXPLORATION;
     EnterTouchToMouseMode();
     SET_STATE(TOUCH_EXPLORATION);
-    return InTouchExploration(event, rewritten_event);
+    return InTouchExploration(event, continuation);
   }
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus
+ui::EventDispatchDetails
 TouchExplorationController::InSingleTapOrTouchExploreReleased(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
   // If there is more than one finger down, then discard to wait until no
   // fingers are down.
   if (current_touch_ids_.size() > 1) {
     SET_STATE(WAIT_FOR_NO_FINGERS);
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
   if (type == ui::ET_TOUCH_PRESSED) {
     // If there is no anchor point for synthesized events because the
@@ -347,7 +328,7 @@
     // send a click, so discard.
     if (anchor_point_state_ == ANCHOR_POINT_NONE) {
       tap_timer_.Stop();
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
     // This is the second tap in a double-tap (or double tap-hold).
     // We set the tap timer. If it fires before the user lifts their finger,
@@ -362,29 +343,33 @@
     // This will update as the finger moves before a possible passthrough, and
     // will determine the offset.
     last_unused_finger_event_.reset(new ui::TouchEvent(event));
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED &&
-             anchor_point_state_ == ANCHOR_POINT_NONE) {
+    last_unused_finger_continuation_ = continuation;
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED &&
+      anchor_point_state_ == ANCHOR_POINT_NONE) {
     // If the previous press was discarded, we need to also handle its
     // release.
     if (current_touch_ids_.size() == 0) {
       SET_STATE(NO_FINGERS_DOWN);
     }
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_MOVED) {
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_MOVED) {
+    return DiscardEvent(continuation);
   }
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InDoubleTapPending(
+ui::EventDispatchDetails TouchExplorationController::InDoubleTapPending(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
   if (type == ui::ET_TOUCH_PRESSED) {
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_MOVED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_MOVED) {
     // If the user moves far enough from the initial touch location (outside
     // the "slop" region, jump to passthrough mode early.
     float delta = (event.location() - initial_press_->location()).Length();
@@ -392,74 +377,80 @@
       tap_timer_.Stop();
       OnTapTimerFired();
     }
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
     if (current_touch_ids_.size() != 0)
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
 
-    SendSimulatedClickOrTap();
+    SendSimulatedClickOrTap(continuation);
 
     SET_STATE(NO_FINGERS_DOWN);
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InTouchReleasePending(
+ui::EventDispatchDetails TouchExplorationController::InTouchReleasePending(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
   if (type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED) {
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
     if (current_touch_ids_.size() != 0)
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
 
-    SendSimulatedClickOrTap();
+    SendSimulatedClickOrTap(continuation);
     SET_STATE(NO_FINGERS_DOWN);
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
+ui::EventDispatchDetails TouchExplorationController::InTouchExploration(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   const ui::EventType type = event.type();
   if (type == ui::ET_TOUCH_PRESSED) {
     // Enter split-tap mode.
     initial_press_ = std::make_unique<ui::TouchEvent>(event);
     tap_timer_.Stop();
+    initial_press_continuation_ = continuation;
     SET_STATE(TOUCH_EXPLORE_SECOND_PRESS);
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
     initial_press_ = std::make_unique<ui::TouchEvent>(event);
+    initial_press_continuation_ = continuation;
     StartTapTimer();
     most_recent_press_timestamp_ = event.time_stamp();
-    MaybeSendSimulatedTapInLiftActivationBounds(event);
+    MaybeSendSimulatedTapInLiftActivationBounds(event, continuation);
     SET_STATE(TOUCH_EXPLORE_RELEASED);
   } else if (type != ui::ET_TOUCH_MOVED) {
     NOTREACHED();
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   // Rewrite as a mouse-move event.
   // |event| locations are in DIP; see |RewriteEvent|. We need to dispatch
   // |screen coords.
   gfx::PointF location_f(ConvertDIPToScreenInPixels(event.location_f()));
-  *rewritten_event = CreateMouseMoveEvent(location_f, event.flags());
+  std::unique_ptr<ui::Event> new_event =
+      CreateMouseMoveEvent(location_f, event.flags());
+  SetTouchAccessibilityFlag(new_event.get());
   last_touch_exploration_ = std::make_unique<ui::TouchEvent>(event);
   if (anchor_point_state_ != ANCHOR_POINT_EXPLICITLY_SET)
     anchor_point_dip_ = last_touch_exploration_->location_f();
-
-  return ui::EVENT_REWRITE_REWRITTEN;
+  return SendEventFinally(continuation, new_event.get());
 }
 
-ui::EventRewriteStatus TouchExplorationController::InGestureInProgress(
+ui::EventDispatchDetails TouchExplorationController::InGestureInProgress(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   // The events were sent to the gesture provider in RewriteEvent already.
   // If no gesture is registered before the tap timer times out, the state
   // will change to "wait for no fingers down" or "touch exploration" depending
@@ -467,57 +458,56 @@
   if (current_touch_ids_.size() == 0) {
     SET_STATE(NO_FINGERS_DOWN);
   }
-  return ui::EVENT_REWRITE_DISCARD;
+  return DiscardEvent(continuation);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InOneFingerPassthrough(
+ui::EventDispatchDetails TouchExplorationController::InOneFingerPassthrough(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (event.pointer_details().id != initial_press_->pointer_details().id) {
     if (current_touch_ids_.size() == 0) {
       SET_STATE(NO_FINGERS_DOWN);
     }
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
   // |event| locations are in DIP; see |RewriteEvent|. We need to dispatch
   // screen coordinates.
   gfx::PointF location_f(
       ConvertDIPToScreenInPixels(event.location_f() - passthrough_offset_));
-  std::unique_ptr<ui::TouchEvent> new_event(new ui::TouchEvent(
-      event.type(), gfx::Point(), event.time_stamp(), event.pointer_details()));
-  new_event->set_location_f(location_f);
-  new_event->set_root_location_f(location_f);
-  new_event->set_flags(event.flags());
-  *rewritten_event = std::move(new_event);
+  ui::TouchEvent new_event(event.type(), gfx::Point(), event.time_stamp(),
+                           event.pointer_details(), event.flags());
+  new_event.set_location_f(location_f);
+  new_event.set_root_location_f(location_f);
+  SetTouchAccessibilityFlag(&new_event);
   if (current_touch_ids_.size() == 0) {
     SET_STATE(NO_FINGERS_DOWN);
   }
-  return ui::EVENT_REWRITE_REWRITTEN;
+  return SendEventFinally(continuation, &new_event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
+ui::EventDispatchDetails TouchExplorationController::InTouchExploreSecondPress(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   ui::EventType type = event.type();
   if (type == ui::ET_TOUCH_PRESSED) {
     // A third finger being pressed means that a split tap can no longer go
     // through. The user enters the wait state, Since there has already been
     // a press dispatched when split tap began, the touch needs to be
     // cancelled.
-    std::unique_ptr<ui::TouchEvent> new_event(new ui::TouchEvent(
-        ui::ET_TOUCH_CANCELLED, gfx::Point(), event.time_stamp(),
-        initial_press_->pointer_details()));
+    ui::TouchEvent new_event(ui::ET_TOUCH_CANCELLED, gfx::Point(),
+                             event.time_stamp(),
+                             initial_press_->pointer_details(), event.flags());
     // TODO(dmazzoni): fix for multiple displays. http://crbug.com/616793
     // |event| locations are in DIP; see |RewriteEvent|. We need to dispatch
     // screen coordinates.
     gfx::PointF location_f(ConvertDIPToScreenInPixels(anchor_point_dip_));
-    new_event->set_location_f(location_f);
-    new_event->set_root_location_f(location_f);
-    new_event->set_flags(event.flags());
-    *rewritten_event = std::move(new_event);
+    new_event.set_location_f(location_f);
+    new_event.set_root_location_f(location_f);
+    SetTouchAccessibilityFlag(&new_event);
     SET_STATE(WAIT_FOR_NO_FINGERS);
-    return ui::EVENT_REWRITE_REWRITTEN;
-  } else if (type == ui::ET_TOUCH_MOVED) {
+    return SendEventFinally(continuation, &new_event);
+  }
+  if (type == ui::ET_TOUCH_MOVED) {
     // If the fingers have moved too far from their original locations,
     // the user can no longer split tap.
     ui::TouchEvent* original_touch;
@@ -530,7 +520,7 @@
     } else {
       NOTREACHED();
       SET_STATE(WAIT_FOR_NO_FINGERS);
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
     // Check the distance between the current finger location and the original
     // location. The slop for this is a bit more generous since keeping two
@@ -540,41 +530,44 @@
         GetSplitTapTouchSlop()) {
       SET_STATE(WAIT_FOR_NO_FINGERS);
     }
-    return ui::EVENT_REWRITE_DISCARD;
-  } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
+    return DiscardEvent(continuation);
+  }
+  if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
     // If the touch exploration finger is lifted, there is no option to return
     // to touch explore anymore. The remaining finger acts as a pending
     // tap or long tap for the last touch explore location.
     if (event.pointer_details().id ==
         last_touch_exploration_->pointer_details().id) {
       SET_STATE(TOUCH_RELEASE_PENDING);
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
 
     // Continue to release the touch only if the touch explore finger is the
     // only finger remaining.
-    if (current_touch_ids_.size() != 1)
-      return ui::EVENT_REWRITE_DISCARD;
+    if (current_touch_ids_.size() != 1) {
+      return DiscardEvent(continuation);
+    }
 
-    SendSimulatedClickOrTap();
+    SendSimulatedClickOrTap(continuation);
 
     SET_STATE(TOUCH_EXPLORATION);
     EnterTouchToMouseMode();
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
   NOTREACHED();
-  return ui::EVENT_REWRITE_CONTINUE;
+  return SendEvent(continuation, &event);
 }
 
-ui::EventRewriteStatus TouchExplorationController::InWaitForNoFingers(
+ui::EventDispatchDetails TouchExplorationController::InWaitForNoFingers(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (current_touch_ids_.size() == 0)
     SET_STATE(NO_FINGERS_DOWN);
-  return ui::EVENT_REWRITE_DISCARD;
+  return DiscardEvent(continuation);
 }
 
-void TouchExplorationController::SendSimulatedClickOrTap() {
+void TouchExplorationController::SendSimulatedClickOrTap(
+    const Continuation continuation) {
   // If we got an anchor point from ChromeVox, send a double-tap gesture
   // and let ChromeVox handle the click.
   const gfx::Point location;
@@ -583,17 +576,18 @@
     delegate_->HandleAccessibilityGesture(ax::mojom::Gesture::kClick);
     return;
   }
-  SendSimulatedTap();
+  SendSimulatedTap(continuation);
 }
 
-void TouchExplorationController::SendSimulatedTap() {
+void TouchExplorationController::SendSimulatedTap(
+    const Continuation continuation) {
   std::unique_ptr<ui::TouchEvent> touch_press;
   touch_press.reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(),
                                        Now(),
                                        initial_press_->pointer_details()));
   touch_press->set_location_f(anchor_point_dip_);
   touch_press->set_root_location_f(anchor_point_dip_);
-  DispatchEvent(touch_press.get());
+  DispatchEvent(touch_press.get(), continuation);
 
   std::unique_ptr<ui::TouchEvent> touch_release;
   touch_release.reset(new ui::TouchEvent(ui::ET_TOUCH_RELEASED, gfx::Point(),
@@ -601,29 +595,30 @@
                                          initial_press_->pointer_details()));
   touch_release->set_location_f(anchor_point_dip_);
   touch_release->set_root_location_f(anchor_point_dip_);
-  DispatchEvent(touch_release.get());
+  DispatchEvent(touch_release.get(), continuation);
 }
 
 void TouchExplorationController::MaybeSendSimulatedTapInLiftActivationBounds(
-    const ui::TouchEvent& event) {
+    const ui::TouchEvent& event,
+    const Continuation continuation) {
   gfx::Point location = event.location();
   gfx::Point anchor_location(anchor_point_dip_.x(), anchor_point_dip_.y());
   if (lift_activation_bounds_.Contains(anchor_location.x(),
                                        anchor_location.y()) &&
       lift_activation_bounds_.Contains(location)) {
     accessibility_sound_player_->PlayTouchTypeEarcon();
-    SendSimulatedTap();
+    SendSimulatedTap(continuation);
   }
 }
 
-ui::EventRewriteStatus TouchExplorationController::InTwoFingerTap(
+ui::EventDispatchDetails TouchExplorationController::InTwoFingerTap(
     const ui::TouchEvent& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   ui::EventType type = event.type();
   if (type == ui::ET_TOUCH_PRESSED) {
     // This is now a three finger gesture.
     SET_STATE(GESTURE_IN_PROGRESS);
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
   if (type == ui::ET_TOUCH_MOVED) {
@@ -635,18 +630,19 @@
     if (distance > gesture_detector_config_.touch_slop) {
       SET_STATE(GESTURE_IN_PROGRESS);
     }
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
-  if (current_touch_ids_.size() != 0)
-    return ui::EVENT_REWRITE_DISCARD;
+  if (current_touch_ids_.size() != 0) {
+    return DiscardEvent(continuation);
+  }
 
   if (type == ui::ET_TOUCH_RELEASED) {
     delegate_->HandleAccessibilityGesture(ax::mojom::Gesture::kTap2);
     SET_STATE(NO_FINGERS_DOWN);
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
-  return ui::EVENT_REWRITE_DISCARD;
+  return DiscardEvent(continuation);
 }
 
 base::TimeTicks TouchExplorationController::Now() {
@@ -679,7 +675,7 @@
                              last_unused_finger_event_->pointer_details()));
       passthrough_press->set_location_f(anchor_point_dip_);
       passthrough_press->set_root_location_f(anchor_point_dip_);
-      DispatchEvent(passthrough_press.get());
+      DispatchEvent(passthrough_press.get(), last_unused_finger_continuation_);
       return;
     }
     case SINGLE_TAP_PRESSED:
@@ -703,13 +699,15 @@
   EnterTouchToMouseMode();
   std::unique_ptr<ui::Event> mouse_move = CreateMouseMoveEvent(
       initial_press_->location_f(), initial_press_->flags());
-  DispatchEvent(mouse_move.get());
+  DispatchEvent(mouse_move.get(), initial_press_continuation_);
   last_touch_exploration_ = std::make_unique<ui::TouchEvent>(*initial_press_);
   anchor_point_dip_ = last_touch_exploration_->location_f();
   anchor_point_state_ = ANCHOR_POINT_FROM_TOUCH_EXPLORATION;
 }
 
-void TouchExplorationController::DispatchEvent(ui::Event* event) {
+void TouchExplorationController::DispatchEvent(
+    ui::Event* event,
+    const Continuation continuation) {
   SetTouchAccessibilityFlag(event);
   if (event->IsLocatedEvent()) {
     ui::LocatedEvent* located_event = event->AsLocatedEvent();
@@ -718,8 +716,9 @@
     located_event->set_location_f(screen_point);
     located_event->set_root_location_f(screen_point);
   }
-  ignore_result(
-      root_window_->GetHost()->dispatcher()->OnEventFromSource(event));
+  if (SendEventFinally(continuation, event).dispatcher_destroyed) {
+    VLOG(0) << "Undispatched event due to destroyed dispatcher.";
+  }
 }
 
 // This is an override for a function that is only called for timer-based events
@@ -874,11 +873,12 @@
 
 void TouchExplorationController::DispatchKeyWithFlags(
     const ui::KeyboardCode key,
-    int flags) {
+    int flags,
+    const Continuation continuation) {
   ui::KeyEvent key_down(ui::ET_KEY_PRESSED, key, flags);
   ui::KeyEvent key_up(ui::ET_KEY_RELEASED, key, flags);
-  DispatchEvent(&key_down);
-  DispatchEvent(&key_up);
+  DispatchEvent(&key_down, continuation);
+  DispatchEvent(&key_up, continuation);
   if (DVLOG_on_) {
     DVLOG(1) << "\nKey down: key code : " << key_down.key_code()
              << ", flags: " << key_down.flags()
@@ -889,9 +889,10 @@
 
 base::OnceClosure TouchExplorationController::BindKeyEventWithFlags(
     const ui::KeyboardCode key,
-    int flags) {
+    int flags,
+    const Continuation continuation) {
   return base::BindOnce(&TouchExplorationController::DispatchKeyWithFlags,
-                        base::Unretained(this), key, flags);
+                        base::Unretained(this), key, flags, continuation);
 }
 
 std::unique_ptr<ui::MouseEvent>
diff --git a/chromecast/browser/accessibility/touch_exploration_controller.h b/chromecast/browser/accessibility/touch_exploration_controller.h
index 2882e958..2ef33f6 100644
--- a/chromecast/browser/accessibility/touch_exploration_controller.h
+++ b/chromecast/browser/accessibility/touch_exploration_controller.h
@@ -8,6 +8,10 @@
 #ifndef CHROMECAST_BROWSER_ACCESSIBILITY_TOUCH_EXPLORATION_CONTROLLER_H_
 #define CHROMECAST_BROWSER_ACCESSIBILITY_TOUCH_EXPLORATION_CONTROLLER_H_
 
+#include <map>
+#include <memory>
+#include <vector>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
@@ -181,50 +185,40 @@
   void SetExcludeBounds(const gfx::Rect& bounds);
 
   // Overridden from ui::EventRewriter
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
  private:
   friend class TouchExplorationControllerTestApi;
 
   // Event handlers based on the current state - see State, below.
-  ui::EventRewriteStatus InNoFingersDown(
+  ui::EventDispatchDetails InNoFingersDown(const ui::TouchEvent& event,
+                                           const Continuation continuation);
+  ui::EventDispatchDetails InSingleTapPressed(const ui::TouchEvent& event,
+                                              const Continuation continuation);
+  ui::EventDispatchDetails InSingleTapOrTouchExploreReleased(
       const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InSingleTapPressed(
+      const Continuation continuation);
+  ui::EventDispatchDetails InDoubleTapPending(const ui::TouchEvent& event,
+                                              const Continuation continuation);
+  ui::EventDispatchDetails InTouchReleasePending(
       const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InSingleTapOrTouchExploreReleased(
+      const Continuation continuation);
+  ui::EventDispatchDetails InTouchExploration(const ui::TouchEvent& event,
+                                              const Continuation continuation);
+  ui::EventDispatchDetails InOneFingerPassthrough(
       const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InDoubleTapPending(
+      const Continuation continuation);
+  ui::EventDispatchDetails InGestureInProgress(const ui::TouchEvent& event,
+                                               const Continuation continuation);
+  ui::EventDispatchDetails InTouchExploreSecondPress(
       const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InTouchReleasePending(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InTouchExploration(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InOneFingerPassthrough(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InGestureInProgress(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InTouchExploreSecondPress(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InWaitForNoFingers(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
-  ui::EventRewriteStatus InTwoFingerTap(
-      const ui::TouchEvent& event,
-      std::unique_ptr<ui::Event>* rewritten_event);
+      const Continuation continuation);
+  ui::EventDispatchDetails InWaitForNoFingers(const ui::TouchEvent& event,
+                                              const Continuation continuation);
+  ui::EventDispatchDetails InTwoFingerTap(const ui::TouchEvent& event,
+                                          const Continuation continuation);
 
   // Returns the current time of the tick clock.
   base::TimeTicks Now();
@@ -237,7 +231,7 @@
   void OnTapTimerFired();
 
   // Dispatch a new event outside of the event rewriting flow.
-  void DispatchEvent(ui::Event* event);
+  void DispatchEvent(ui::Event* event, const Continuation continuation);
 
   // Overridden from GestureProviderAuraClient.
   //
@@ -254,11 +248,14 @@
   void OnSwipeEvent(ui::GestureEvent* swipe_gesture);
 
   // Dispatches a single key with the given flags.
-  void DispatchKeyWithFlags(const ui::KeyboardCode key, int flags);
+  void DispatchKeyWithFlags(const ui::KeyboardCode key,
+                            int flags,
+                            const Continuation continuation);
 
   // Binds DispatchKeyWithFlags to a specific key and flags.
   base::OnceClosure BindKeyEventWithFlags(const ui::KeyboardCode key,
-                                          int flags);
+                                          int flags,
+                                          const Continuation continuation);
 
   std::unique_ptr<ui::MouseEvent> CreateMouseMoveEvent(
       const gfx::PointF& location,
@@ -270,20 +267,22 @@
 
   // Sends a simulated click, if an anchor point was set explicitly. Otherwise,
   // sends a simulated tap at anchor point.
-  void SendSimulatedClickOrTap();
+  void SendSimulatedClickOrTap(const Continuation continuation);
 
   // Sends a simulated tap at anchor point.
-  void SendSimulatedTap();
+  void SendSimulatedTap(const Continuation continuation);
 
   // Sends a simulated tap, if the anchor point falls within lift activation
   // bounds.
-  void MaybeSendSimulatedTapInLiftActivationBounds(const ui::TouchEvent& event);
+  void MaybeSendSimulatedTapInLiftActivationBounds(
+      const ui::TouchEvent& event,
+      const Continuation continuation);
 
   // Some constants used in touch_exploration_controller:
 
   // Within this many dips of the screen edge, the release event generated will
   // reset the state to NoFingersDown.
-  // TODO: Unify with identical value in CastSystemGestureEventHandler.
+  // TODO(kpschoedel): Unify with identical value in SideSwipeDetector.
   const float kLeavingScreenEdge = 35;
 
   // Touch within this distance from a corner can invoke corner passthrough.
@@ -427,6 +426,7 @@
 
   // A copy of the event from the initial touch press.
   std::unique_ptr<ui::TouchEvent> initial_press_;
+  Continuation initial_press_continuation_;
 
   // The timestamp of the most recent press event for the main touch id.
   // The difference between this and |initial_press_->time_stamp| is that
@@ -445,6 +445,7 @@
   // sending events through, but might in the future (e.g. before a finger
   // enters double-tap-hold passthrough, we need to update its location.)
   std::unique_ptr<ui::TouchEvent> last_unused_finger_event_;
+  Continuation last_unused_finger_continuation_;
 
   // The anchor point used as the location of a synthesized tap when the
   // user double-taps anywhere on the screen, and similarly the initial
diff --git a/chromecast/browser/accessibility/touch_exploration_manager.cc b/chromecast/browser/accessibility/touch_exploration_manager.cc
index b1d34cb..a9e80f99 100644
--- a/chromecast/browser/accessibility/touch_exploration_manager.cc
+++ b/chromecast/browser/accessibility/touch_exploration_manager.cc
@@ -7,6 +7,9 @@
 
 #include "chromecast/browser/accessibility/touch_exploration_manager.h"
 
+#include <utility>
+#include <vector>
+
 #include "chromecast/browser/cast_browser_context.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/common/extensions_api/accessibility_private.h"
@@ -47,23 +50,12 @@
   UpdateTouchExplorationState();
 }
 
-ui::EventRewriteStatus TouchExplorationManager::RewriteEvent(
+ui::EventDispatchDetails TouchExplorationManager::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
-  if (touch_exploration_controller_) {
-    return touch_exploration_controller_->RewriteEvent(event, rewritten_event);
-  }
-  return ui::EVENT_REWRITE_CONTINUE;
-}
-
-ui::EventRewriteStatus TouchExplorationManager::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  if (touch_exploration_controller_) {
-    return touch_exploration_controller_->NextDispatchEvent(last_event,
-                                                            new_event);
-  }
-  return ui::EVENT_REWRITE_CONTINUE;
+    const Continuation continuation) {
+  return touch_exploration_controller_
+             ? touch_exploration_controller_->RewriteEvent(event, continuation)
+             : SendEvent(continuation, &event);
 }
 
 void TouchExplorationManager::HandleAccessibilityGesture(
@@ -120,8 +112,7 @@
     }
     if (pass_through_surface) {
       const display::Display display =
-          display::Screen::GetScreen()->GetDisplayNearestWindow(
-              root_window_);
+          display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_);
       const gfx::Rect work_area = display.work_area();
       touch_exploration_controller_->SetExcludeBounds(work_area);
       // Clear the focus highlight.
diff --git a/chromecast/browser/accessibility/touch_exploration_manager.h b/chromecast/browser/accessibility/touch_exploration_manager.h
index 7217e712..54d25c2b 100644
--- a/chromecast/browser/accessibility/touch_exploration_manager.h
+++ b/chromecast/browser/accessibility/touch_exploration_manager.h
@@ -8,6 +8,8 @@
 #ifndef CHROMECAST_BROWSER_ACCESSIBILITY_TOUCH_EXPLORATION_MANAGER_H_
 #define CHROMECAST_BROWSER_ACCESSIBILITY_TOUCH_EXPLORATION_MANAGER_H_
 
+#include <memory>
+
 #include "chromecast/browser/accessibility/accessibility_sound_player.h"
 #include "chromecast/browser/accessibility/touch_exploration_controller.h"
 #include "chromecast/graphics/accessibility/accessibility_focus_ring_controller.h"
@@ -41,12 +43,9 @@
   void Enable(bool enabled);
 
   // ui::EventRewriter overrides:
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
   // TouchExplorationControllerDelegate overrides:
   void HandleAccessibilityGesture(ax::mojom::Gesture gesture) override;
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index f213cf3..2d9c5e5 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -119,7 +119,6 @@
 
 #if BUILDFLAG(ENABLE_CAST_WAYLAND_SERVER)
 #include "chromecast/browser/exo/wayland_server_controller.h"
-#include "chromecast/browser/exo/wm_helper_cast_shell.h"
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
@@ -577,7 +576,8 @@
 
   extensions_browser_client_ =
       std::make_unique<extensions::CastExtensionsBrowserClient>(
-          cast_browser_process_->browser_context(), user_pref_service_.get());
+          cast_browser_process_->browser_context(), user_pref_service_.get(),
+          cast_content_browser_client_->cast_network_contexts());
   extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get());
 
   extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt();
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 1863115..dc18542 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -212,7 +212,8 @@
 CastContentBrowserClient::CastContentBrowserClient(
     CastFeatureListCreator* cast_feature_list_creator)
     : cast_browser_main_parts_(nullptr),
-      cast_network_contexts_(std::make_unique<CastNetworkContexts>()),
+      cast_network_contexts_(
+          std::make_unique<CastNetworkContexts>(GetCorsExemptHeadersList())),
       url_request_context_factory_(new URLRequestContextFactory()),
       cast_feature_list_creator_(cast_feature_list_creator) {
   cast_feature_list_creator_->SetExtraEnableFeatures({
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index b429f74..9824e1b 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -76,6 +76,11 @@
   static std::unique_ptr<CastContentBrowserClient> Create(
       CastFeatureListCreator* cast_feature_list_creator);
 
+  // Returns a list of headers that will be exempt from CORS preflight checks.
+  // This is needed since currently servers don't have the correct response to
+  // preflight checks.
+  static std::vector<std::string> GetCorsExemptHeadersList();
+
   ~CastContentBrowserClient() override;
 
   // Creates and returns the CastService instance for the current process.
diff --git a/chromecast/browser/cast_content_browser_client_simple.cc b/chromecast/browser/cast_content_browser_client_simple.cc
index 89d5f43..b3dfcba 100644
--- a/chromecast/browser/cast_content_browser_client_simple.cc
+++ b/chromecast/browser/cast_content_browser_client_simple.cc
@@ -18,5 +18,10 @@
       new CastContentBrowserClient(cast_feature_list_creator));
 }
 
+// static
+std::vector<std::string> CastContentBrowserClient::GetCorsExemptHeadersList() {
+  return std::vector<std::string>();
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_media_blocker.h b/chromecast/browser/cast_media_blocker.h
index 861ae31..0e4476d7 100644
--- a/chromecast/browser/cast_media_blocker.h
+++ b/chromecast/browser/cast_media_blocker.h
@@ -60,6 +60,8 @@
       const base::flat_map<media_session::mojom::MediaSessionImageType,
                            std::vector<media_session::MediaImage>>& images)
       override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
  private:
   friend shell::CastMediaBlockerTest;
diff --git a/chromecast/browser/cast_network_contexts.cc b/chromecast/browser/cast_network_contexts.cc
index 9475983..f9e9f7f 100644
--- a/chromecast/browser/cast_network_contexts.cc
+++ b/chromecast/browser/cast_network_contexts.cc
@@ -82,8 +82,10 @@
   DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryForSystem);
 };
 
-CastNetworkContexts::CastNetworkContexts()
-    : system_shared_url_loader_factory_(
+CastNetworkContexts::CastNetworkContexts(
+    std::vector<std::string> cors_exempt_headers_list)
+    : cors_exempt_headers_list_(std::move(cors_exempt_headers_list)),
+      system_shared_url_loader_factory_(
           base::MakeRefCounted<URLLoaderFactoryForSystem>(this)) {}
 
 CastNetworkContexts::~CastNetworkContexts() {
@@ -208,6 +210,8 @@
 
   AddProxyToNetworkContextParams(network_context_params.get());
 
+  network_context_params->cors_exempt_header_list = cors_exempt_headers_list_;
+
   return network_context_params;
 }
 
diff --git a/chromecast/browser/cast_network_contexts.h b/chromecast/browser/cast_network_contexts.h
index 1c86cfab..2f356f1 100644
--- a/chromecast/browser/cast_network_contexts.h
+++ b/chromecast/browser/cast_network_contexts.h
@@ -6,6 +6,8 @@
 #define CHROMECAST_BROWSER_CAST_NETWORK_CONTEXTS_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/memory/scoped_refptr.h"
 #include "content/public/browser/browser_thread.h"
@@ -44,7 +46,8 @@
 class CastNetworkContexts : public net::ProxyConfigService::Observer,
                             public network::mojom::ProxyConfigPollerClient {
  public:
-  CastNetworkContexts();
+  explicit CastNetworkContexts(
+      std::vector<std::string> cors_exempt_headers_list);
   ~CastNetworkContexts() override;
 
   // Returns the System NetworkContext. Does any initialization of the
@@ -103,6 +106,8 @@
   // network::mojom::ProxyConfigPollerClient implementation:
   void OnLazyProxyConfigPoll() override;
 
+  const std::vector<std::string> cors_exempt_headers_list_;
+
   // The system NetworkContext.
   network::mojom::NetworkContextPtr system_network_context_;
 
diff --git a/chromecast/browser/exo/wm_helper_cast_shell.cc b/chromecast/browser/exo/cast_wm_helper.cc
similarity index 64%
rename from chromecast/browser/exo/wm_helper_cast_shell.cc
rename to chromecast/browser/exo/cast_wm_helper.cc
index 61c54d4..4d022dc2 100644
--- a/chromecast/browser/exo/wm_helper_cast_shell.cc
+++ b/chromecast/browser/exo/cast_wm_helper.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 "chromecast/browser/exo/wm_helper_cast_shell.h"
+#include "chromecast/browser/exo/cast_wm_helper.h"
 
 #include "base/memory/singleton.h"
 #include "chromecast/browser/cast_browser_process.h"
@@ -46,7 +46,7 @@
 
 }  // namespace
 
-WMHelperCastShell::WMHelperCastShell(
+CastWMHelper::CastWMHelper(
     chromecast::CastWindowManagerAura* cast_window_manager_aura,
     chromecast::CastScreen* cast_screen)
     : cast_window_manager_aura_(cast_window_manager_aura),
@@ -55,66 +55,66 @@
   cast_screen_->AddObserver(&display_observer_);
 }
 
-WMHelperCastShell::~WMHelperCastShell() {
+CastWMHelper::~CastWMHelper() {
   cast_screen_->RemoveObserver(&display_observer_);
 }
 
-void WMHelperCastShell::AddActivationObserver(
+void CastWMHelper::AddActivationObserver(
     wm::ActivationChangeObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::RemoveActivationObserver(
+void CastWMHelper::RemoveActivationObserver(
     wm::ActivationChangeObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::AddFocusObserver(
+void CastWMHelper::AddFocusObserver(
     aura::client::FocusChangeObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::RemoveFocusObserver(
+void CastWMHelper::RemoveFocusObserver(
     aura::client::FocusChangeObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::AddDragDropObserver(DragDropObserver* observer) {
+void CastWMHelper::AddDragDropObserver(DragDropObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::RemoveDragDropObserver(DragDropObserver* observer) {
+void CastWMHelper::RemoveDragDropObserver(DragDropObserver* observer) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::SetDragDropDelegate(aura::Window* window) {
+void CastWMHelper::SetDragDropDelegate(aura::Window* window) {
   aura::client::SetDragDropDelegate(window, this);
 }
 
-void WMHelperCastShell::ResetDragDropDelegate(aura::Window* window) {
+void CastWMHelper::ResetDragDropDelegate(aura::Window* window) {
   aura::client::SetDragDropDelegate(window, nullptr);
 }
 
-VSyncTimingManager& WMHelperCastShell::GetVSyncTimingManager() {
+VSyncTimingManager& CastWMHelper::GetVSyncTimingManager() {
   return vsync_timing_manager_;
 }
 
-void WMHelperCastShell::OnDragEntered(const ui::DropTargetEvent& event) {}
+void CastWMHelper::OnDragEntered(const ui::DropTargetEvent& event) {}
 
-int WMHelperCastShell::OnDragUpdated(const ui::DropTargetEvent& event) {
+int CastWMHelper::OnDragUpdated(const ui::DropTargetEvent& event) {
   NOTIMPLEMENTED();
   return 0;
 }
 
-void WMHelperCastShell::OnDragExited() {}
+void CastWMHelper::OnDragExited() {}
 
-int WMHelperCastShell::OnPerformDrop(const ui::DropTargetEvent& event,
-                                     std::unique_ptr<ui::OSExchangeData> data) {
+int CastWMHelper::OnPerformDrop(const ui::DropTargetEvent& event,
+                                std::unique_ptr<ui::OSExchangeData> data) {
   NOTIMPLEMENTED();
   return ui::DragDropTypes::DRAG_MOVE;
 }
 
-void WMHelperCastShell::AddVSyncParameterObserver(
+void CastWMHelper::AddVSyncParameterObserver(
     viz::mojom::VSyncParameterObserverPtr observer) {
   cast_window_manager_aura_->GetRootWindow()
       ->layer()
@@ -122,103 +122,103 @@
       ->AddVSyncParameterObserver(std::move(observer));
 }
 
-const display::ManagedDisplayInfo& WMHelperCastShell::GetDisplayInfo(
+const display::ManagedDisplayInfo& CastWMHelper::GetDisplayInfo(
     int64_t display_id) const {
   return display_observer_.GetDisplayInfo(display_id);
 }
 
-const std::vector<uint8_t>& WMHelperCastShell::GetDisplayIdentificationData(
+const std::vector<uint8_t>& CastWMHelper::GetDisplayIdentificationData(
     int64_t display_id) const {
   NOTIMPLEMENTED();
   static std::vector<uint8_t> no_data;
   return no_data;
 }
 
-bool WMHelperCastShell::GetActiveModeForDisplayId(
+bool CastWMHelper::GetActiveModeForDisplayId(
     int64_t display_id,
     display::ManagedDisplayMode* mode) const {
   return display_observer_.GetActiveModeForDisplayId(display_id, mode);
 }
 
-aura::Window* WMHelperCastShell::GetPrimaryDisplayContainer(int container_id) {
+aura::Window* CastWMHelper::GetPrimaryDisplayContainer(int container_id) {
   return cast_window_manager_aura_->GetRootWindow();
 }
 
-aura::Window* WMHelperCastShell::GetActiveWindow() const {
+aura::Window* CastWMHelper::GetActiveWindow() const {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-aura::Window* WMHelperCastShell::GetFocusedWindow() const {
+aura::Window* CastWMHelper::GetFocusedWindow() const {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-aura::Window* WMHelperCastShell::GetRootWindowForNewWindows() const {
+aura::Window* CastWMHelper::GetRootWindowForNewWindows() const {
   return cast_window_manager_aura_->GetRootWindow();
 }
 
-aura::client::CursorClient* WMHelperCastShell::GetCursorClient() {
+aura::client::CursorClient* CastWMHelper::GetCursorClient() {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-void WMHelperCastShell::AddPreTargetHandler(ui::EventHandler* handler) {
+void CastWMHelper::AddPreTargetHandler(ui::EventHandler* handler) {
   cast_window_manager_aura_->GetRootWindow()->AddPreTargetHandler(handler);
 }
 
-void WMHelperCastShell::PrependPreTargetHandler(ui::EventHandler* handler) {
+void CastWMHelper::PrependPreTargetHandler(ui::EventHandler* handler) {
   NOTIMPLEMENTED();
 }
 
-void WMHelperCastShell::RemovePreTargetHandler(ui::EventHandler* handler) {
+void CastWMHelper::RemovePreTargetHandler(ui::EventHandler* handler) {
   cast_window_manager_aura_->GetRootWindow()->RemovePreTargetHandler(handler);
 }
 
-void WMHelperCastShell::AddPostTargetHandler(ui::EventHandler* handler) {
+void CastWMHelper::AddPostTargetHandler(ui::EventHandler* handler) {
   cast_window_manager_aura_->GetRootWindow()->AddPostTargetHandler(handler);
 }
 
-void WMHelperCastShell::RemovePostTargetHandler(ui::EventHandler* handler) {
+void CastWMHelper::RemovePostTargetHandler(ui::EventHandler* handler) {
   cast_window_manager_aura_->GetRootWindow()->RemovePostTargetHandler(handler);
 }
 
-bool WMHelperCastShell::InTabletMode() const {
+bool CastWMHelper::InTabletMode() const {
   NOTIMPLEMENTED();
   return false;
 }
 
-double WMHelperCastShell::GetDefaultDeviceScaleFactor() const {
+double CastWMHelper::GetDefaultDeviceScaleFactor() const {
   NOTIMPLEMENTED();
   return 1.0;
 }
 
-void WMHelperCastShell::SetImeBlocked(aura::Window* window, bool ime_blocked) {
+void CastWMHelper::SetImeBlocked(aura::Window* window, bool ime_blocked) {
   NOTIMPLEMENTED();
 }
 
-bool WMHelperCastShell::IsImeBlocked(aura::Window* window) const {
+bool CastWMHelper::IsImeBlocked(aura::Window* window) const {
   NOTIMPLEMENTED();
   return false;
 }
 
-WMHelper::LifetimeManager* WMHelperCastShell::GetLifetimeManager() {
+WMHelper::LifetimeManager* CastWMHelper::GetLifetimeManager() {
   return &lifetime_manager_;
 }
 
-aura::client::CaptureClient* WMHelperCastShell::GetCaptureClient() {
+aura::client::CaptureClient* CastWMHelper::GetCaptureClient() {
   return cast_window_manager_aura_->capture_client();
 }
 
-WMHelperCastShell::CastDisplayObserver::CastDisplayObserver() {}
+CastWMHelper::CastDisplayObserver::CastDisplayObserver() {}
 
-WMHelperCastShell::CastDisplayObserver::~CastDisplayObserver() {}
+CastWMHelper::CastDisplayObserver::~CastDisplayObserver() {}
 
-void WMHelperCastShell::CastDisplayObserver::OnWillProcessDisplayChanges() {}
+void CastWMHelper::CastDisplayObserver::OnWillProcessDisplayChanges() {}
 
-void WMHelperCastShell::CastDisplayObserver::OnDidProcessDisplayChanges() {}
+void CastWMHelper::CastDisplayObserver::OnDidProcessDisplayChanges() {}
 
-void WMHelperCastShell::CastDisplayObserver::OnDisplayAdded(
+void CastWMHelper::CastDisplayObserver::OnDisplayAdded(
     const display::Display& new_display) {
   display::ManagedDisplayInfo md(new_display.id(), "CastDisplayInfo", true);
   md.SetRotation(new_display.rotation(),
@@ -229,12 +229,12 @@
   display_info_.emplace(new_display.id(), md);
 }
 
-void WMHelperCastShell::CastDisplayObserver::OnDisplayRemoved(
+void CastWMHelper::CastDisplayObserver::OnDisplayRemoved(
     const display::Display& old_display) {
   display_info_.erase(old_display.id());
 }
 
-void WMHelperCastShell::CastDisplayObserver::OnDisplayMetricsChanged(
+void CastWMHelper::CastDisplayObserver::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t changed_metrics) {
   if (display_info_.find(display.id()) == display_info_.end())
@@ -246,15 +246,14 @@
 }
 
 const display::ManagedDisplayInfo&
-WMHelperCastShell::CastDisplayObserver::GetDisplayInfo(
-    int64_t display_id) const {
+CastWMHelper::CastDisplayObserver::GetDisplayInfo(int64_t display_id) const {
   auto iter = display_info_.find(display_id);
   DCHECK(iter != display_info_.end())
       << "Failed to find display " << display_id;
   return iter->second;
 }
 
-bool WMHelperCastShell::CastDisplayObserver::GetActiveModeForDisplayId(
+bool CastWMHelper::CastDisplayObserver::GetActiveModeForDisplayId(
     int64_t display_id,
     display::ManagedDisplayMode* mode) const {
   auto iter = display_info_.find(display_id);
diff --git a/chromecast/browser/exo/wm_helper_cast_shell.h b/chromecast/browser/exo/cast_wm_helper.h
similarity index 90%
rename from chromecast/browser/exo/wm_helper_cast_shell.h
rename to chromecast/browser/exo/cast_wm_helper.h
index 206bac3..d79df9b 100644
--- a/chromecast/browser/exo/wm_helper_cast_shell.h
+++ b/chromecast/browser/exo/cast_wm_helper.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 CHROMECAST_BROWSER_EXO_WM_HELPER_CAST_SHELL_H_
-#define CHROMECAST_BROWSER_EXO_WM_HELPER_CAST_SHELL_H_
+#ifndef CHROMECAST_BROWSER_EXO_CAST_WM_HELPER_H_
+#define CHROMECAST_BROWSER_EXO_CAST_WM_HELPER_H_
 
 #include <cstdint>
 #include <map>
@@ -54,11 +54,11 @@
 
 // A CastShell-specific helper class for accessing WindowManager related
 // features.
-class WMHelperCastShell : public WMHelper, public VSyncTimingManager::Delegate {
+class CastWMHelper : public WMHelper, public VSyncTimingManager::Delegate {
  public:
-  WMHelperCastShell(chromecast::CastWindowManagerAura* cast_window_manager_aura,
-                    chromecast::CastScreen* cast_screen);
-  ~WMHelperCastShell() override;
+  CastWMHelper(chromecast::CastWindowManagerAura* cast_window_manager_aura,
+               chromecast::CastScreen* cast_screen);
+  ~CastWMHelper() override;
 
   // Overridden from WMHelper
   void AddActivationObserver(wm::ActivationChangeObserver* observer) override;
@@ -139,9 +139,9 @@
   LifetimeManager lifetime_manager_;
   VSyncTimingManager vsync_timing_manager_;
 
-  DISALLOW_COPY_AND_ASSIGN(WMHelperCastShell);
+  DISALLOW_COPY_AND_ASSIGN(CastWMHelper);
 };
 
 }  // namespace exo
 
-#endif  // CHROMECAST_BROWSER_EXO_WM_HELPER_CAST_SHELL_H_
+#endif  // CHROMECAST_BROWSER_EXO_CAST_WM_HELPER_H_
diff --git a/chromecast/browser/exo/wayland_server_controller.cc b/chromecast/browser/exo/wayland_server_controller.cc
index 85081c56..422207e4 100644
--- a/chromecast/browser/exo/wayland_server_controller.cc
+++ b/chromecast/browser/exo/wayland_server_controller.cc
@@ -4,7 +4,7 @@
 
 #include "chromecast/browser/exo/wayland_server_controller.h"
 
-#include "chromecast/browser/exo/wm_helper_cast_shell.h"
+#include "chromecast/browser/exo/cast_wm_helper.h"
 #include "chromecast/graphics/cast_screen.h"
 #include "components/exo/display.h"
 #include "components/exo/wayland/server.h"
@@ -15,7 +15,7 @@
 
 WaylandServerController::WaylandServerController(
     CastWindowManagerAura* window_manager) {
-  wm_helper_ = std::make_unique<exo::WMHelperCastShell>(
+  wm_helper_ = std::make_unique<exo::CastWMHelper>(
       window_manager, static_cast<CastScreen*>(CastScreen::GetScreen()));
   exo::WMHelper::SetInstance(wm_helper_.get());
   display_ = std::make_unique<exo::Display>();
diff --git a/chromecast/browser/extensions/cast_extensions_browser_client.cc b/chromecast/browser/extensions/cast_extensions_browser_client.cc
index 9b2e9ef..a2fec43 100644
--- a/chromecast/browser/extensions/cast_extensions_browser_client.cc
+++ b/chromecast/browser/extensions/cast_extensions_browser_client.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
+#include "chromecast/browser/cast_network_contexts.h"
 #include "chromecast/browser/extensions/cast_extension_host_delegate.h"
 #include "chromecast/browser/extensions/cast_extension_system_factory.h"
 #include "chromecast/browser/extensions/cast_extension_web_contents_observer.h"
@@ -40,10 +41,13 @@
 
 CastExtensionsBrowserClient::CastExtensionsBrowserClient(
     BrowserContext* context,
-    PrefService* pref_service)
+    PrefService* pref_service,
+    chromecast::shell::CastNetworkContexts* cast_network_contexts)
     : browser_context_(context),
+      cast_network_contexts_(cast_network_contexts),
       pref_service_(pref_service),
       api_client_(new CastExtensionsAPIClient) {
+  DCHECK(cast_network_contexts_);
   // Set to UNKNOWN to enable all APIs.
   // TODO(achaulk): figure out what channel to use here.
   SetCurrentChannel(version_info::Channel::UNKNOWN);
@@ -54,6 +58,11 @@
 
 CastExtensionsBrowserClient::~CastExtensionsBrowserClient() {}
 
+network::mojom::NetworkContext*
+CastExtensionsBrowserClient::GetSystemNetworkContext() {
+  return cast_network_contexts_->GetSystemContext();
+}
+
 bool CastExtensionsBrowserClient::IsShuttingDown() {
   return false;
 }
diff --git a/chromecast/browser/extensions/cast_extensions_browser_client.h b/chromecast/browser/extensions/cast_extensions_browser_client.h
index d51a63a4..2beaca0 100644
--- a/chromecast/browser/extensions/cast_extensions_browser_client.h
+++ b/chromecast/browser/extensions/cast_extensions_browser_client.h
@@ -15,6 +15,12 @@
 
 class PrefService;
 
+namespace chromecast {
+namespace shell {
+class CastNetworkContexts;
+}  // namespace shell
+}  // namespace chromecast
+
 namespace extensions {
 
 class ExtensionsAPIClient;
@@ -25,11 +31,14 @@
  public:
   // |context| is the single BrowserContext used for IsValidContext() below.
   // |pref_service| is used for GetPrefServiceForContext() below.
-  CastExtensionsBrowserClient(content::BrowserContext* context,
-                              PrefService* pref_service);
+  CastExtensionsBrowserClient(
+      content::BrowserContext* context,
+      PrefService* pref_service,
+      chromecast::shell::CastNetworkContexts* cast_network_contexts);
   ~CastExtensionsBrowserClient() override;
 
   // ExtensionsBrowserClient overrides:
+  network::mojom::NetworkContext* GetSystemNetworkContext() override;
   bool IsShuttingDown() override;
   bool AreExtensionsDisabled(const base::CommandLine& command_line,
                              content::BrowserContext* context) override;
@@ -112,6 +121,8 @@
   // The single BrowserContext for cast_shell. Not owned.
   content::BrowserContext* browser_context_;
 
+  chromecast::shell::CastNetworkContexts* cast_network_contexts_;
+
   // The PrefService for |browser_context_|. Not owned.
   PrefService* pref_service_;
 
diff --git a/chromecast/graphics/accessibility/fullscreen_magnification_controller.cc b/chromecast/graphics/accessibility/fullscreen_magnification_controller.cc
index eefa1fe8..ff77568 100644
--- a/chromecast/graphics/accessibility/fullscreen_magnification_controller.cc
+++ b/chromecast/graphics/accessibility/fullscreen_magnification_controller.cc
@@ -4,6 +4,8 @@
 
 #include "chromecast/graphics/accessibility/fullscreen_magnification_controller.h"
 
+#include <vector>
+
 #include "base/numerics/ranges.h"
 #include "chromecast/graphics/gestures/cast_gesture_handler.h"
 #include "third_party/skia/include/core/SkPaint.h"
@@ -152,14 +154,14 @@
 }
 
 // Overridden from ui::EventRewriter
-ui::EventRewriteStatus FullscreenMagnificationController::RewriteEvent(
+ui::EventDispatchDetails FullscreenMagnificationController::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (!IsEnabled()) {
-    return ui::EventRewriteStatus::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
   if (!event.IsTouchEvent())
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
 
   const ui::TouchEvent* touch_event = event.AsTouchEvent();
 
@@ -193,7 +195,7 @@
         touch_event_dip.unique_event_id(), false /* event_consumed */,
         false /* is_source_touch_event_set_non_blocking */);
   } else {
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
   // The user can change the zoom level with two fingers pinch and pan around
@@ -208,21 +210,19 @@
     consume_touch_event_ = true;
 
     if (!press_event_map_.empty()) {
-      auto it = press_event_map_.begin();
-
-      std::unique_ptr<ui::TouchEvent> rewritten_touch_event =
-          std::make_unique<ui::TouchEvent>(ui::ET_TOUCH_CANCELLED, gfx::Point(),
-                                           touch_event_dip.time_stamp(),
-                                           it->second->pointer_details());
-      rewritten_touch_event->set_location_f(it->second->location_f());
-      rewritten_touch_event->set_root_location_f(it->second->root_location_f());
-      rewritten_touch_event->set_flags(it->second->flags());
-      *rewritten_event = std::move(rewritten_touch_event);
-
-      // The other event is cancelled in NextDispatchEvent.
-      press_event_map_.erase(it);
-
-      return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
+      DCHECK_EQ(2u, press_event_map_.size());
+      ui::EventDispatchDetails details;
+      for (const auto& it : press_event_map_) {
+        ui::TouchEvent rewritten_touch_event(
+            ui::ET_TOUCH_CANCELLED, it.second->location_f(),
+            it.second->root_location_f(), touch_event_dip.time_stamp(),
+            it.second->pointer_details(), it.second->flags());
+        details = SendEvent(continuation, &rewritten_touch_event);
+        if (details.dispatcher_destroyed)
+          break;
+      }
+      press_event_map_.clear();
+      return details;
     }
   }
 
@@ -239,31 +239,9 @@
   }
 
   if (discard)
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
 
-  return ui::EVENT_REWRITE_CONTINUE;
-}
-
-ui::EventRewriteStatus FullscreenMagnificationController::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  DCHECK_EQ(1u, press_event_map_.size());
-
-  auto it = press_event_map_.begin();
-
-  std::unique_ptr<ui::TouchEvent> event = std::make_unique<ui::TouchEvent>(
-      ui::ET_TOUCH_CANCELLED, gfx::Point(), last_event.time_stamp(),
-      it->second->pointer_details());
-  event->set_location_f(it->second->location_f());
-  event->set_root_location_f(it->second->root_location_f());
-  event->set_flags(it->second->flags());
-  *new_event = std::move(event);
-
-  press_event_map_.erase(it);
-
-  DCHECK_EQ(0u, press_event_map_.size());
-
-  return ui::EVENT_REWRITE_REWRITTEN;
+  return SendEvent(continuation, &event);
 }
 
 bool FullscreenMagnificationController::RedrawDIP(
diff --git a/chromecast/graphics/accessibility/fullscreen_magnification_controller.h b/chromecast/graphics/accessibility/fullscreen_magnification_controller.h
index 0d00316c..e439bc8 100644
--- a/chromecast/graphics/accessibility/fullscreen_magnification_controller.h
+++ b/chromecast/graphics/accessibility/fullscreen_magnification_controller.h
@@ -5,6 +5,9 @@
 #ifndef CHROMECAST_GRAPHICS_ACCESSIBILITY_FULLSCREEN_MAGNIFICATION_CONTROLLER_H_
 #define CHROMECAST_GRAPHICS_ACCESSIBILITY_FULLSCREEN_MAGNIFICATION_CONTROLLER_H_
 
+#include <map>
+#include <memory>
+
 #include "chromecast/graphics/accessibility/magnification_controller.h"
 #include "ui/compositor/layer_delegate.h"
 #include "ui/events/event_handler.h"
@@ -57,12 +60,9 @@
   bool RedrawDIP(const gfx::PointF& position_in_dip, float scale);
 
   // Overridden from ui::EventRewriter
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
   // Adds the layer for the highlight-ring which provides a visual indicator
   // that magnification is enabled.
diff --git a/chromecast/graphics/cast_touch_event_gate.cc b/chromecast/graphics/cast_touch_event_gate.cc
index 73c9db0..abeeb2ba 100644
--- a/chromecast/graphics/cast_touch_event_gate.cc
+++ b/chromecast/graphics/cast_touch_event_gate.cc
@@ -42,22 +42,16 @@
   }
 }
 
-ui::EventRewriteStatus CastTouchEventGate::RewriteEvent(
+ui::EventDispatchDetails CastTouchEventGate::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (!enabled_ || !event.IsTouchEvent())
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
 
   for (auto* observer : observers_) {
     observer->OnTouchActivity();
   }
-  return ui::EVENT_REWRITE_DISCARD;
-}
-
-ui::EventRewriteStatus CastTouchEventGate::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  return ui::EVENT_REWRITE_DISCARD;
+  return DiscardEvent(continuation);
 }
 
 }  // namespace chromecast
diff --git a/chromecast/graphics/cast_touch_event_gate.h b/chromecast/graphics/cast_touch_event_gate.h
index b59d535..968b12f 100644
--- a/chromecast/graphics/cast_touch_event_gate.h
+++ b/chromecast/graphics/cast_touch_event_gate.h
@@ -32,13 +32,9 @@
   void RemoveObserver(CastTouchActivityObserver* observer);
 
   // ui::EventRewriter implementation.
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
  private:
   bool enabled_ = false;
diff --git a/chromecast/graphics/gestures/multiple_tap_detector.cc b/chromecast/graphics/gestures/multiple_tap_detector.cc
index 2ba628e..2456a51 100644
--- a/chromecast/graphics/gestures/multiple_tap_detector.cc
+++ b/chromecast/graphics/gestures/multiple_tap_detector.cc
@@ -4,6 +4,8 @@
 
 #include "chromecast/graphics/gestures/multiple_tap_detector.h"
 
+#include <memory>
+
 #include "base/auto_reset.h"
 #include "base/logging.h"
 #include "ui/aura/window.h"
@@ -15,24 +17,6 @@
 
 namespace chromecast {
 
-namespace {
-
-std::unique_ptr<ui::TouchEvent> MakeCancelEvent(
-    const base::TimeTicks& time_stamp,
-    const ui::TouchEvent& stashed_press) {
-  std::unique_ptr<ui::TouchEvent> rewritten_touch_event =
-      std::make_unique<ui::TouchEvent>(ui::ET_TOUCH_CANCELLED, gfx::Point(),
-                                       time_stamp,
-                                       stashed_press.pointer_details());
-  rewritten_touch_event->set_location_f(stashed_press.location_f());
-  rewritten_touch_event->set_root_location_f(stashed_press.root_location_f());
-  rewritten_touch_event->set_flags(stashed_press.flags());
-
-  return rewritten_touch_event;
-}
-
-}  // namespace
-
 MultipleTapDetector::MultipleTapDetector(aura::Window* root_window,
                                          MultipleTapDetectorDelegate* delegate)
     : root_window_(root_window),
@@ -47,11 +31,11 @@
   root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
 }
 
-ui::EventRewriteStatus MultipleTapDetector::RewriteEvent(
+ui::EventDispatchDetails MultipleTapDetector::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* rewritten_event) {
+    const Continuation continuation) {
   if (!enabled_ || !delegate_ || !event.IsTouchEvent()) {
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
@@ -59,11 +43,11 @@
     // If a press happened again before the minimum inter-tap interval, cancel
     // the detection.
     if (tap_state_ == MultiTapState::INTERVAL_WAIT &&
-        (event.time_stamp() - stashed_events_.back().time_stamp()) <
+        (event.time_stamp() - stashed_events_.back().event.time_stamp()) <
             gesture_detector_config_.double_tap_min_time) {
       stashed_events_.clear();
       TapDetectorStateReset();
-      return ui::EVENT_REWRITE_CONTINUE;
+      return SendEvent(continuation, &event);
     }
 
     // If the user moved too far from the last tap position, it's not a multi
@@ -73,7 +57,7 @@
       if (distance > gesture_detector_config_.double_tap_slop) {
         TapDetectorStateReset();
         stashed_events_.clear();
-        return ui::EVENT_REWRITE_CONTINUE;
+        return SendEvent(continuation, &event);
       }
     }
 
@@ -90,14 +74,14 @@
     // If we've already gotten one tap, discard this event, only the original
     // tap needs to get through.
     if (tap_count_) {
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
 
     // Copy the event so we can issue a cancel for it later if this turns out to
     // be a multi-tap.
-    stashed_events_.push_back(touch_event);
+    stashed_events_.emplace_back(touch_event, continuation);
 
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   // Finger was released while we were waiting for one, count it as a tap.
@@ -113,35 +97,25 @@
       TapDetectorStateReset();
       delegate_->OnTripleTap(touch_event.location());
 
-      // Start issuing cancel events for old presses.
-      DCHECK(!stashed_events_.empty());
-      *rewritten_event =
-          MakeCancelEvent(touch_event.time_stamp(), stashed_events_.front());
-      stashed_events_.pop_front();
-
-      if (!stashed_events_.empty())
-        return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
-      else
-        return ui::EVENT_REWRITE_REWRITTEN;
+      // Issue cancel events for old presses.
+      ui::EventDispatchDetails details;
+      for (const auto& it : stashed_events_) {
+        ui::TouchEvent cancel_event(
+            ui::ET_TOUCH_CANCELLED, it.event.location_f(),
+            it.event.root_location_f(), it.event.time_stamp(),
+            it.event.pointer_details(), it.event.flags());
+        details = SendEvent(it.continuation, &cancel_event);
+        if (details.dispatcher_destroyed)
+          break;
+      }
+      stashed_events_.clear();
+      return details;
     } else if (tap_count_ > 1) {
-      return ui::EVENT_REWRITE_DISCARD;
+      return DiscardEvent(continuation);
     }
   }
 
-  return ui::EVENT_REWRITE_CONTINUE;
-}
-
-ui::EventRewriteStatus MultipleTapDetector::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  *new_event =
-      MakeCancelEvent(last_event.time_stamp(), stashed_events_.front());
-  stashed_events_.pop_front();
-
-  if (!stashed_events_.empty())
-    return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
-  else
-    return ui::EVENT_REWRITE_DISCARD;
+  return SendEvent(continuation, &event);
 }
 
 void MultipleTapDetector::OnTapIntervalTimerFired() {
@@ -149,14 +123,14 @@
   // So call out the double-tap.
   if (tap_count_ == 2) {
     delegate_->OnDoubleTap(last_tap_location_);
-
-    // Unfortunately we cannot NextDispatchEvent to issue a cancel on the second
-    // tap, so we have to manually DispatchEvent to the EventSource.
-    // Subsequent EventRewriters in the chain will not see the event.
     if (!stashed_events_.empty()) {
-      std::unique_ptr<ui::TouchEvent> cancel_event =
-          MakeCancelEvent(base::TimeTicks::Now(), stashed_events_.front());
-      DispatchEvent(cancel_event.get());
+      Stash& stash = stashed_events_.front();
+      ui::TouchEvent cancel_event(
+          ui::ET_TOUCH_CANCELLED, stash.event.location_f(),
+          stash.event.root_location_f(), base::TimeTicks::Now(),
+          stash.event.pointer_details(), stash.event.flags());
+      DCHECK(
+          !SendEvent(stash.continuation, &cancel_event).dispatcher_destroyed);
     }
   }
   TapDetectorStateReset();
@@ -174,13 +148,9 @@
   triple_tap_timer_.Stop();
 }
 
-void MultipleTapDetector::DispatchEvent(ui::TouchEvent* event) {
-  // Turn off triple-tap before re-dispatching to avoid infinite recursion into
-  // this detector.
-  base::AutoReset<bool> toggle_enable(&enabled_, false);
-  DCHECK(!root_window_->GetHost()
-              ->dispatcher()
-              ->OnEventFromSource(event)
-              .dispatcher_destroyed);
-}
+MultipleTapDetector::Stash::Stash(const ui::TouchEvent& e, const Continuation c)
+    : event(e), continuation(c) {}
+
+MultipleTapDetector::Stash::~Stash() {}
+
 }  // namespace chromecast
diff --git a/chromecast/graphics/gestures/multiple_tap_detector.h b/chromecast/graphics/gestures/multiple_tap_detector.h
index 0810971f..59879f3d 100644
--- a/chromecast/graphics/gestures/multiple_tap_detector.h
+++ b/chromecast/graphics/gestures/multiple_tap_detector.h
@@ -53,12 +53,9 @@
   bool enabled() const { return enabled_; }
 
   // Overridden from ui::EventRewriter
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
  private:
   friend class MultipleTapDetectorTest;
@@ -84,7 +81,14 @@
   int tap_count_;
   gfx::Point last_tap_location_;
   base::OneShotTimer triple_tap_timer_;
-  std::deque<ui::TouchEvent> stashed_events_;
+  class Stash {
+   public:
+    Stash(const ui::TouchEvent& e, const Continuation c);
+    ~Stash();
+    const ui::TouchEvent event;
+    const Continuation continuation;
+  };
+  std::deque<Stash> stashed_events_;
 
   DISALLOW_COPY_AND_ASSIGN(MultipleTapDetector);
 };
diff --git a/chromecast/graphics/gestures/side_swipe_detector.cc b/chromecast/graphics/gestures/side_swipe_detector.cc
index 9ea0233..7bfde99 100644
--- a/chromecast/graphics/gestures/side_swipe_detector.cc
+++ b/chromecast/graphics/gestures/side_swipe_detector.cc
@@ -99,11 +99,11 @@
   stashed_events_.push_back(event);
 }
 
-ui::EventRewriteStatus SideSwipeDetector::RewriteEvent(
+ui::EventDispatchDetails SideSwipeDetector::RewriteEvent(
     const ui::Event& event,
-    std::unique_ptr<ui::Event>* new_event) {
+    const Continuation continuation) {
   if (!event.IsTouchEvent()) {
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   const ui::TouchEvent* touch_event = event.AsTouchEvent();
@@ -127,12 +127,12 @@
     // Check to see if we have any potential consumers of events on this side.
     // If not, we can continue on without consuming it.
     if (!gesture_handler_->CanHandleSwipe(side_swipe_origin)) {
-      return ui::EVENT_REWRITE_CONTINUE;
+      return SendEvent(continuation, &event);
     }
 
     // Detect the beginning of a system gesture swipe.
     if (touch_event->type() != ui::ET_TOUCH_PRESSED) {
-      return ui::EVENT_REWRITE_CONTINUE;
+      return SendEvent(continuation, &event);
     }
 
     current_swipe_ = side_swipe_origin;
@@ -154,17 +154,17 @@
     root_window_->CleanupGestureState();
 
     // And then stop the original event from propagating.
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
   // If no swipe in progress, just move on.
   if (current_swipe_ == CastSideSwipeOrigin::NONE) {
-    return ui::EVENT_REWRITE_CONTINUE;
+    return SendEvent(continuation, &event);
   }
 
   // If the finger involved is not the one we're looking for, discard it.
   if (touch_event->pointer_details().id != current_pointer_id_) {
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
   // A swipe is in progress, or has completed, so stop propagation of underlying
@@ -183,19 +183,22 @@
     current_pointer_id_ = ui::PointerDetails::kUnknownPointerId;
 
     // If the finger is still inside the touch margin at release, this is not
-    // really a side swipe. Start streaming out events we stashed for later
-    // retrieval.
+    // really a side swipe. Stream out events we stashed for later retrieval.
     if (side_swipe_origin != CastSideSwipeOrigin::NONE &&
         !stashed_events_.empty()) {
-      *new_event = ui::Event::Clone(stashed_events_.front());
-      stashed_events_.pop_front();
-      return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
+      ui::EventDispatchDetails details;
+      for (const auto& it : stashed_events_) {
+        details = SendEvent(continuation, &it);
+        if (details.dispatcher_destroyed)
+          break;
+      }
+      stashed_events_.clear();
+      return details;
     }
 
     // Otherwise, clear them.
     stashed_events_.clear();
-
-    return ui::EVENT_REWRITE_DISCARD;
+    return DiscardEvent(continuation);
   }
 
   // The system gesture is ongoing...
@@ -205,20 +208,7 @@
            << current_swipe_time_.Elapsed().InMilliseconds() << "ms @ "
            << touch_location.ToString();
 
-  return ui::EVENT_REWRITE_DISCARD;
-}
-
-ui::EventRewriteStatus SideSwipeDetector::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  if (stashed_events_.empty()) {
-    return ui::EVENT_REWRITE_DISCARD;
-  }
-
-  *new_event = ui::Event::Clone(stashed_events_.front());
-  stashed_events_.pop_front();
-
-  return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
+  return DiscardEvent(continuation);
 }
 
 }  // namespace chromecast
diff --git a/chromecast/graphics/gestures/side_swipe_detector.h b/chromecast/graphics/gestures/side_swipe_detector.h
index 095ad04..0222f2ce 100644
--- a/chromecast/graphics/gestures/side_swipe_detector.h
+++ b/chromecast/graphics/gestures/side_swipe_detector.h
@@ -5,6 +5,8 @@
 #ifndef CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_
 #define CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_
 
+#include <deque>
+
 #include "base/timer/elapsed_timer.h"
 #include "chromecast/graphics/gestures/cast_gesture_handler.h"
 #include "ui/events/event_rewriter.h"
@@ -32,12 +34,9 @@
                                       const gfx::Rect& screen_bounds) const;
 
   // Overridden from ui::EventRewriter
-  ui::EventRewriteStatus RewriteEvent(
+  ui::EventDispatchDetails RewriteEvent(
       const ui::Event& event,
-      std::unique_ptr<ui::Event>* rewritten_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
+      const Continuation continuation) override;
 
  private:
   void StashEvent(const ui::TouchEvent& event);
diff --git a/chromecast/media/cdm/OWNERS b/chromecast/media/cdm/OWNERS
new file mode 100644
index 0000000..f6a6baf
--- /dev/null
+++ b/chromecast/media/cdm/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internals>Media>Encrypted
diff --git a/chromeos/assistant/OWNERS b/chromeos/assistant/OWNERS
index c47d135..a03f56b 100644
--- a/chromeos/assistant/OWNERS
+++ b/chromeos/assistant/OWNERS
@@ -2,4 +2,4 @@
 wutao@chromium.org
 xiaohuic@chromium.org
 
-# COMPONENTS: UI>Shell>Assistant
+# COMPONENT: UI>Shell>Assistant
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index a7c62a8..9abf076 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -135,10 +135,10 @@
 
       <!-- Managed guest session warnings -->
       <message name="IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING" desc="Text shown in the public account user pod in case of risky configuration, informing the user that this session is managed and admin can monitor all activity.">
-        The administrator of this device has access to all activity, including passwords and communications.
+        <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> manages this device and has access to all user activity, including webpages visited, passwords, and email.
       </message>
       <message name="IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING" desc="Text shown in the public account user pod in case of non-risky configuration, informing the user that this session is managed and admin can monitor all activity.">
-        The device administrator may be able to monitor your activity.
+        <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> manages this device and may be able to monitor your activity.
       </message>
 
       <!-- Status tray enterprise management. -->
diff --git a/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING.png.sha1
index 0612919..0ebd593 100644
--- a/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING.png.sha1
@@ -1 +1 @@
-35ff7a23ed3df6ccbc9b2267ebaaf8ed8bada467
\ No newline at end of file
+00708b9d291e109d1f196d698b038262d4fe1022
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING.png.sha1
index 6208ff3d..aef24bb 100644
--- a/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_SOFT_WARNING.png.sha1
@@ -1 +1 @@
-980ed20bd0ed1748276b5ea307a0e3e841745dcc
\ No newline at end of file
+818ef020fef4427b569cfadef4ec11546d81e1ce
\ No newline at end of file
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index 13050e2..d143615 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -23,6 +23,7 @@
     "//chromeos/components/nearby:unit_tests",
     "//chromeos/components/power:unit_tests",
     "//chromeos/components/proximity_auth:unit_tests",
+    "//chromeos/components/sync_wifi:unit_tests",
     "//chromeos/components/tether:unit_tests",
     "//mojo/core/embedder",
   ]
diff --git a/chromeos/components/proximity_auth/BUILD.gn b/chromeos/components/proximity_auth/BUILD.gn
index c8971a7e..01f7efd 100644
--- a/chromeos/components/proximity_auth/BUILD.gn
+++ b/chromeos/components/proximity_auth/BUILD.gn
@@ -121,6 +121,8 @@
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/multidevice/logging",
     "//chromeos/constants",
+    "//chromeos/dbus/power",
+    "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/services/multidevice_setup/public/cpp:prefs",
     "//chromeos/services/multidevice_setup/public/cpp:test_support",
     "//chromeos/services/secure_channel:test_support",
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl.cc b/chromeos/components/proximity_auth/unlock_manager_impl.cc
index 84a09a3..610ed3b 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl.cc
+++ b/chromeos/components/proximity_auth/unlock_manager_impl.cc
@@ -12,6 +12,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/components/proximity_auth/messenger.h"
@@ -24,13 +25,22 @@
 namespace proximity_auth {
 namespace {
 
-// The maximum amount of time, in seconds, that the unlock manager can stay in
-// the 'waking up' state after resuming from sleep.
+// The maximum amount of time that the unlock manager can stay in the 'waking
+// up' state after resuming from sleep.
 constexpr base::TimeDelta kWakingUpDuration = base::TimeDelta::FromSeconds(15);
 
-// The limit, in seconds, on the elapsed time for an auth attempt. If an auth
-// attempt exceeds this limit, it will time out and be rejected. This is
-// provided as a failsafe, in case something goes wrong.
+// The maximum amount of time that we wait for the BluetoothAdapter to be
+// fully initialized after resuming from sleep.
+// TODO(crbug.com/986896): This is necessary because the BluetoothAdapter
+// returns incorrect presence and power values directly after resume, and does
+// not return correct values until about 1-2 seconds later. Remove this once
+// the bug is fixed.
+constexpr base::TimeDelta kBluetoothAdapterResumeMaxDuration =
+    base::TimeDelta::FromSeconds(3);
+
+// The limit on the elapsed time for an auth attempt. If an auth attempt exceeds
+// this limit, it will time out and be rejected. This is provided as a failsafe,
+// in case something goes wrong.
 constexpr base::TimeDelta kAuthAttemptTimeout = base::TimeDelta::FromSeconds(5);
 
 constexpr base::TimeDelta kMinGetUnlockableRemoteStatusDuration =
@@ -107,14 +117,9 @@
     ProximityAuthSystem::ScreenlockType screenlock_type,
     ProximityAuthClient* proximity_auth_client)
     : screenlock_type_(screenlock_type),
-      life_cycle_(nullptr),
       proximity_auth_client_(proximity_auth_client),
-      is_attempting_auth_(false),
-      is_performing_initial_scan_(false),
-      screenlock_state_(ScreenlockState::INACTIVE),
-      initial_scan_timeout_weak_ptr_factory_(this),
-      reject_auth_attempt_weak_ptr_factory_(this),
-      weak_ptr_factory_(this) {
+      bluetooth_suspension_recovery_timer_(
+          std::make_unique<base::OneShotTimer>()) {
   chromeos::PowerManagerClient::Get()->AddObserver(this);
 
   if (device::BluetoothAdapterFactory::IsBluetoothSupported()) {
@@ -314,15 +319,44 @@
   UpdateLockScreen();
 }
 
+void UnlockManagerImpl::SuspendImminent(
+    power_manager::SuspendImminent::Reason reason) {
+  // TODO(crbug.com/986896): For a short time window after resuming from
+  // suspension, BluetoothAdapter returns incorrect presence and power values.
+  // Cache the correct values now, in case we need to check those values during
+  // that time window when the device resumes.
+  was_bluetooth_present_and_powered_before_last_suspend_ =
+      IsBluetoothPresentAndPowered();
+  bluetooth_suspension_recovery_timer_->Stop();
+}
+
 void UnlockManagerImpl::SuspendDone(const base::TimeDelta& sleep_duration) {
+  bluetooth_suspension_recovery_timer_->Start(
+      FROM_HERE, kBluetoothAdapterResumeMaxDuration,
+      base::Bind(&UnlockManagerImpl::UpdateLockScreen,
+                 weak_ptr_factory_.GetWeakPtr()));
+
   SetIsPerformingInitialScan(true /* is_performing_initial_scan */);
 }
 
 bool UnlockManagerImpl::IsBluetoothPresentAndPowered() const {
+  // TODO(crbug.com/986896): If the BluetoothAdapter is still "resuming after
+  // suspension" at this time, it's prone to this bug, meaning we cannot trust
+  // its returned presence and power values. If this is the case, depend on
+  // the cached |was_bluetooth_present_and_powered_before_last_suspend_| to
+  // signal if Bluetooth is enabled; otherwise, directly check request values
+  // from BluetoothAdapter. Remove this check once the bug is fixed.
+  if (IsBluetoothAdapterRecoveringFromSuspend())
+    return was_bluetooth_present_and_powered_before_last_suspend_;
+
   return bluetooth_adapter_ && bluetooth_adapter_->IsPresent() &&
          bluetooth_adapter_->IsPowered();
 }
 
+bool UnlockManagerImpl::IsBluetoothAdapterRecoveringFromSuspend() const {
+  return bluetooth_suspension_recovery_timer_->IsRunning();
+}
+
 void UnlockManagerImpl::AttemptToStartRemoteDeviceLifecycle() {
   if (IsBluetoothPresentAndPowered() && life_cycle_ &&
       life_cycle_->GetState() == RemoteDeviceLifeCycle::State::STOPPED) {
@@ -660,4 +694,9 @@
   attempt_get_remote_status_start_time_ = base::Time();
 }
 
+void UnlockManagerImpl::SetBluetoothSuspensionRecoveryTimerForTesting(
+    std::unique_ptr<base::OneShotTimer> timer) {
+  bluetooth_suspension_recovery_timer_ = std::move(timer);
+}
+
 }  // namespace proximity_auth
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl.h b/chromeos/components/proximity_auth/unlock_manager_impl.h
index 6259d1f..fc52823 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl.h
+++ b/chromeos/components/proximity_auth/unlock_manager_impl.h
@@ -23,6 +23,10 @@
 #include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
+namespace base {
+class OneShotTimer;
+}  // namespace base
+
 namespace proximity_auth {
 
 class Messenger;
@@ -57,6 +61,8 @@
       RemoteDeviceLifeCycle* life_cycle);
 
  private:
+  friend class ProximityAuthUnlockManagerImplTest;
+
   // The possible lock screen states for the remote device.
   enum class RemoteScreenlockState {
     UNKNOWN,
@@ -90,6 +96,7 @@
                              bool powered) override;
 
   // chromeos::PowerManagerClient::Observer:
+  void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
   void SuspendDone(const base::TimeDelta& sleep_duration) override;
 
   // RemoteDeviceLifeCycle::Observer:
@@ -99,6 +106,17 @@
   // Returns true if the BluetoothAdapter is present and powered.
   bool IsBluetoothPresentAndPowered() const;
 
+  // TODO(crbug.com/986896): Waiting a certain time, after resume, before
+  // trusting the presence and power values returned by BluetoothAdapter is
+  // necessary because the BluetoothAdapter returns incorrect values directly
+  // after resume, and does not return correct values until about 1-2 seconds
+  // later. Remove this function once the bug is resolved.
+  //
+  // This function returns true if the BluetoothAdapter is still resuming from
+  // suspension, indicating that its returned presence and power values cannot
+  // yet be trusted.
+  bool IsBluetoothAdapterRecoveringFromSuspend() const;
+
   // If the RemoteDeviceLifeCycle is available, ensure it is started (but only
   // if Bluetooth is available).
   void AttemptToStartRemoteDeviceLifecycle();
@@ -159,27 +177,43 @@
   // Clears the timers for beginning a scan and fetching remote status.
   void ResetPerformanceMetricsTimestamps();
 
+  void SetBluetoothSuspensionRecoveryTimerForTesting(
+      std::unique_ptr<base::OneShotTimer> timer);
+
   // Whether |this| manager is being used for sign-in or session unlock.
   const ProximityAuthSystem::ScreenlockType screenlock_type_;
 
-  // Whether the user is present at the remote device. Unset if no remote status
-  // update has yet been received.
-  std::unique_ptr<RemoteScreenlockState> remote_screenlock_state_;
+  // Used to call into the embedder. Expected to outlive |this| instance.
+  ProximityAuthClient* proximity_auth_client_;
 
-  // Controls the proximity auth flow logic for a remote device. Not owned, and
-  // expcted to outlive |this| instance.
-  RemoteDeviceLifeCycle* life_cycle_;
+  // Starts running after resuming from suspension, and fires once enough time
+  // has elapsed such that the BluetoothAdapter's presence and power values can
+  // be trusted again. To be removed once https://crbug.com/986896 is fixed.
+  std::unique_ptr<base::OneShotTimer> bluetooth_suspension_recovery_timer_;
+
+  // The Bluetooth adapter. Null if there is no adapter present on the local
+  // device.
+  scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
 
   // Tracks whether the remote device is currently in close enough proximity to
   // the local device to allow unlocking.
   std::unique_ptr<ProximityMonitor> proximity_monitor_;
 
-  // Used to call into the embedder. Expected to outlive |this| instance.
-  ProximityAuthClient* proximity_auth_client_;
+  // Whether the user is present at the remote device. Unset if no remote status
+  // update has yet been received.
+  std::unique_ptr<RemoteScreenlockState> remote_screenlock_state_;
+
+  // The sign-in secret received from the remote device by decrypting the
+  // sign-in challenge.
+  std::unique_ptr<std::string> sign_in_secret_;
+
+  // Controls the proximity auth flow logic for a remote device. Not owned, and
+  // expcted to outlive |this| instance.
+  RemoteDeviceLifeCycle* life_cycle_ = nullptr;
 
   // True if the manager is currently processing a user-initiated authentication
   // attempt, which is initiated when the user pod is clicked.
-  bool is_attempting_auth_;
+  bool is_attempting_auth_ = false;
 
   // If true, either the lock screen was just shown (after resuming from
   // suspend, or directly locking the screen), or the focused user pod was
@@ -188,18 +222,17 @@
   // point the user visually sees an indication that the phone cannot be found).
   // Though this field becomes false after this timeout, Smart Lock continues
   // to scan for the phone until the user unlocks the screen.
-  bool is_performing_initial_scan_;
+  bool is_performing_initial_scan_ = false;
 
-  // The Bluetooth adapter. Null if there is no adapter present on the local
-  // device.
-  scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
-
-  // The sign-in secret received from the remote device by decrypting the
-  // sign-in challenge.
-  std::unique_ptr<std::string> sign_in_secret_;
+  // TODO(crbug.com/986896): For a short time window after resuming from
+  // suspension, BluetoothAdapter returns incorrect presence and power values.
+  // This field acts as a cache in case we need to check those values during
+  // that time window when the device resumes. Remove this field once the bug
+  // is fixed.
+  bool was_bluetooth_present_and_powered_before_last_suspend_ = false;
 
   // The state of the current screen lock UI.
-  ScreenlockState screenlock_state_;
+  ScreenlockState screenlock_state_ = ScreenlockState::INACTIVE;
 
   // The timestamp of when UnlockManager begins to try to establish a secure
   // connection to the requested remote device of the provided
@@ -214,15 +247,16 @@
   // Used to track if the "initial scan" has timed out. See
   // |is_performing_initial_scan_| for more.
   base::WeakPtrFactory<UnlockManagerImpl>
-      initial_scan_timeout_weak_ptr_factory_;
+      initial_scan_timeout_weak_ptr_factory_{this};
 
   // Used to reject auth attempts after a timeout. An in-progress auth attempt
   // blocks the sign-in screen UI, so it's important to prevent the auth attempt
   // from blocking the UI in case a step in the code path hangs.
-  base::WeakPtrFactory<UnlockManagerImpl> reject_auth_attempt_weak_ptr_factory_;
+  base::WeakPtrFactory<UnlockManagerImpl> reject_auth_attempt_weak_ptr_factory_{
+      this};
 
   // Used to vend all other weak pointers.
-  base::WeakPtrFactory<UnlockManagerImpl> weak_ptr_factory_;
+  base::WeakPtrFactory<UnlockManagerImpl> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(UnlockManagerImpl);
 };
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc b/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
index dd0891dc..c06f7e1 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
+++ b/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/mock_timer.h"
 #include "build/build_config.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/multidevice/remote_device_test_util.h"
@@ -23,6 +24,9 @@
 #include "chromeos/components/proximity_auth/remote_device_life_cycle.h"
 #include "chromeos/components/proximity_auth/remote_status_update.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "chromeos/services/secure_channel/public/cpp/client/fake_client_channel.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -160,16 +164,16 @@
   ~ProximityAuthUnlockManagerImplTest() override = default;
 
   void SetUp() override {
+    chromeos::PowerManagerClient::InitializeFake();
+
     ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
     ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
+
     ON_CALL(messenger_, SupportsSignIn()).WillByDefault(Return(true));
     ON_CALL(messenger_, GetChannel())
         .WillByDefault(Return(fake_client_channel_.get()));
-
     life_cycle_.set_messenger(&messenger_);
     life_cycle_.set_channel(fake_client_channel_.get());
-
-    chromeos::PowerManagerClient::InitializeFake();
   }
 
   void TearDown() override {
@@ -189,6 +193,11 @@
       ProximityAuthSystem::ScreenlockType screenlock_type) {
     unlock_manager_.reset(
         new TestUnlockManager(screenlock_type, &proximity_auth_client_));
+
+    auto mock_timer = std::make_unique<base::MockOneShotTimer>();
+    mock_bluetooth_suspension_recovery_timer_ = mock_timer.get();
+    unlock_manager_->SetBluetoothSuspensionRecoveryTimerForTesting(
+        std::move(mock_timer));
   }
 
   void SimulateUserPresentState() {
@@ -222,6 +231,7 @@
   NiceMock<MockProximityAuthClient> proximity_auth_client_;
   NiceMock<MockMessenger> messenger_;
   std::unique_ptr<TestUnlockManager> unlock_manager_;
+  base::MockOneShotTimer* mock_bluetooth_suspension_recovery_timer_ = nullptr;
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -528,6 +538,119 @@
   EXPECT_TRUE(life_cycle_.started());
 }
 
+TEST_F(
+    ProximityAuthUnlockManagerImplTest,
+    CacheBluetoothAdapterStateAfterSuspendAndResume_AttemptConnectionWhileBluetoothAdapterIsStillRecovering) {
+  CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
+
+  ASSERT_FALSE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
+      power_manager::SuspendImminent_Reason_LID_CLOSED);
+
+  // Simulate https://crbug.com/986896 by returning false for presence and power
+  // directly after resuming, but do not fire
+  // |mock_bluetooth_suspension_recovery_timer_|, simulating that not enough
+  // time has passed for the BluetoothAdapter to recover. It's expected under
+  // these conditions that:
+  // * ProximityAuthClient::UpdateScreenlockState() never be called with
+  //   ScreenlockState::NO_BLUETOOTH.
+  // * ProximityAuthClient::UpdateScreenlockState() only be called once with
+  //   ScreenlockState::BLUETOOTH_CONNECTING, because it should only be called
+  //   on when the ScreenlockState value changes.
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::NO_BLUETOOTH))
+      .Times(0);
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::BLUETOOTH_CONNECTING));
+
+  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
+  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
+  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+
+  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
+  // resume: providing a new RemoteDeviceLifeCycle. This shouldn't trigger a new
+  // call to ProximityAuthClient::UpdateScreenlockState().
+  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
+  EXPECT_TRUE(life_cycle_.started());
+
+  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+}
+
+TEST_F(
+    ProximityAuthUnlockManagerImplTest,
+    CacheBluetoothAdapterStateAfterSuspendAndResume_AttemptConnectionOnceBluetoothAdapterHasHadTimeToRecover) {
+  CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
+
+  ASSERT_FALSE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
+      power_manager::SuspendImminent_Reason_LID_CLOSED);
+
+  // Simulate https://crbug.com/986896 by returning false for presence and power
+  // directly after resuming, and then fire
+  // |mock_bluetooth_suspension_recovery_timer_|, simulating that enough time
+  // has passed for the BluetoothAdapter to recover - this means that Bluetooth
+  // is truly off after resume and the user should be visually informed as such.
+  // It's expected under these conditions that:
+  // * ProximityAuthClient::UpdateScreenlockState() only be called once with
+  //   ScreenlockState::NO_BLUETOOTH, but after the timer fires (this is
+  //   impossible to explicitly do in code with mocks, unfortunately).
+  // * ProximityAuthClient::UpdateScreenlockState() only be called once with
+  //   ScreenlockState::BLUETOOTH_CONNECTING, directly after SuspendDone.
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::NO_BLUETOOTH));
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::BLUETOOTH_CONNECTING));
+
+  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
+  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
+  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+
+  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
+  // resume: providing a new RemoteDeviceLifeCycle. This shouldn't trigger a new
+  // call to ProximityAuthClient::UpdateScreenlockState().
+  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
+  EXPECT_TRUE(life_cycle_.started());
+
+  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
+  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));
+
+  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
+
+  // This leads to ProximityAuthClient::UpdateScreenlockState() being called
+  // with ScreenlockState::NO_BLUETOOTH.
+  mock_bluetooth_suspension_recovery_timer_->Fire();
+}
+
+TEST_F(ProximityAuthUnlockManagerImplTest,
+       BluetoothOffMessagePresentedImmediatelyIfBluetoothWasOffBeforeSuspend) {
+  CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
+
+  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
+  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
+      power_manager::SuspendImminent_Reason_LID_CLOSED);
+
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::NO_BLUETOOTH));
+  EXPECT_CALL(proximity_auth_client_,
+              UpdateScreenlockState(ScreenlockState::BLUETOOTH_CONNECTING))
+      .Times(0);
+
+  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
+
+  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
+  // resume: providing a new RemoteDeviceLifeCycle.
+  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
+  EXPECT_FALSE(life_cycle_.started());
+}
+
 TEST_F(ProximityAuthUnlockManagerImplTest, StartsProximityMonitor) {
   CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
   SimulateUserPresentState();
diff --git a/chromeos/components/sync_wifi/BUILD.gn b/chromeos/components/sync_wifi/BUILD.gn
new file mode 100644
index 0000000..b5acece
--- /dev/null
+++ b/chromeos/components/sync_wifi/BUILD.gn
@@ -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.
+
+static_library("sync_wifi") {
+  sources = [
+    "synced_network_updater.h",
+    "wifi_configuration_bridge.cc",
+    "wifi_configuration_bridge.h",
+  ]
+  deps = [
+    "//base",
+    "//components/sync",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "wifi_configuration_bridge_unittest.cc",
+  ]
+  deps = [
+    ":sync_wifi",
+    "//base/test:test_support",
+    "//components/sync:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/components/sync_wifi/DEPS b/chromeos/components/sync_wifi/DEPS
new file mode 100644
index 0000000..5fb592e
--- /dev/null
+++ b/chromeos/components/sync_wifi/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/sync",
+]
diff --git a/chromeos/components/sync_wifi/OWNERS b/chromeos/components/sync_wifi/OWNERS
new file mode 100644
index 0000000..a663579
--- /dev/null
+++ b/chromeos/components/sync_wifi/OWNERS
@@ -0,0 +1,4 @@
+jonmann@chromium.org
+khorimoto@chromium.org
+
+# COMPONENT: OS>Systems>Network>WiFi
diff --git a/chromeos/components/sync_wifi/README.md b/chromeos/components/sync_wifi/README.md
new file mode 100644
index 0000000..e0cc93f
--- /dev/null
+++ b/chromeos/components/sync_wifi/README.md
@@ -0,0 +1,20 @@
+Wifi Configuration Sync
+
+sync_wifi is a component which provides the necessary APIs to sync Wi-Fi
+credentials across devices.  This component will receive changes from the
+Chrome sync server as well as monitor local changes to the network list
+and keep the two network lists in sync with each other.  Local changes will
+be monitored using chromeos::NetworkStateHandler and updated using
+chromeos::NetworkConfigurationHandler.  Changes from the server will be
+received through the syncer::ModelTypeSyncBridge interface.
+
+Only password protected networks which were added by the specific user will be
+synced to their account.  Public networks, enterprise networks, and networks
+which have static ip configurations will not be synced.
+
+The network configurations with credentials will be stored in the users
+cryptohome using a syncer::ModelTypeStore and held in memory during the
+user session.  All network details will be encrypted before getting sent
+to the Chrome sync server.
+
+This feature is tracked at http://crbug.com/954648
\ No newline at end of file
diff --git a/chromeos/components/sync_wifi/synced_network_updater.h b/chromeos/components/sync_wifi/synced_network_updater.h
new file mode 100644
index 0000000..8e5eedc
--- /dev/null
+++ b/chromeos/components/sync_wifi/synced_network_updater.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 CHROMEOS_COMPONENTS_SYNC_WIFI_SYNCED_NETWORK_UPDATER_H_
+#define CHROMEOS_COMPONENTS_SYNC_WIFI_SYNCED_NETWORK_UPDATER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace sync_pb {
+class WifiConfigurationSpecificsData;
+}
+
+namespace sync_wifi {
+
+// Applies updates to synced networks to the local networking stack.
+class SyncedNetworkUpdater {
+ public:
+  virtual ~SyncedNetworkUpdater() = default;
+
+  virtual void AddOrUpdateNetwork(
+      const sync_pb::WifiConfigurationSpecificsData& specifics) = 0;
+  virtual void RemoveNetwork(const std::string& ssid) = 0;
+
+ protected:
+  SyncedNetworkUpdater() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SyncedNetworkUpdater);
+};
+
+}  // namespace sync_wifi
+
+#endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_SYNCED_NETWORK_UPDATER_H_
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge.cc b/chromeos/components/sync_wifi/wifi_configuration_bridge.cc
new file mode 100644
index 0000000..95bfc13
--- /dev/null
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge.cc
@@ -0,0 +1,198 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/sync_wifi/wifi_configuration_bridge.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "chromeos/components/sync_wifi/synced_network_updater.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+
+namespace sync_wifi {
+
+namespace {
+
+std::unique_ptr<syncer::EntityData> GenerateWifiEntityData(
+    const sync_pb::WifiConfigurationSpecificsData& data) {
+  auto entity_data = std::make_unique<syncer::EntityData>();
+  entity_data->specifics.mutable_wifi_configuration()
+      ->mutable_client_only_encrypted_data()
+      ->CopyFrom(data);
+  entity_data->name = data.ssid();
+  return entity_data;
+}
+}  // namespace
+
+WifiConfigurationBridge::WifiConfigurationBridge(
+    SyncedNetworkUpdater* synced_network_updater,
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+    syncer::OnceModelTypeStoreFactory create_store_callback)
+    : ModelTypeSyncBridge(std::move(change_processor)),
+      synced_network_updater_(synced_network_updater) {
+  std::move(create_store_callback)
+      .Run(syncer::WIFI_CONFIGURATIONS,
+           base::BindOnce(&WifiConfigurationBridge::OnStoreCreated,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+WifiConfigurationBridge::~WifiConfigurationBridge() {}
+
+std::unique_ptr<syncer::MetadataChangeList>
+WifiConfigurationBridge::CreateMetadataChangeList() {
+  return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
+}
+
+base::Optional<syncer::ModelError> WifiConfigurationBridge::MergeSyncData(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_data) {
+  DCHECK(entries_.empty());
+  return ApplySyncChanges(std::move(metadata_change_list),
+                          std::move(entity_data));
+}
+
+base::Optional<syncer::ModelError> WifiConfigurationBridge::ApplySyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_changes) {
+  std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+
+  for (std::unique_ptr<syncer::EntityChange>& change : entity_changes) {
+    if (change->type() == syncer::EntityChange::ACTION_DELETE) {
+      auto it = entries_.find(change->storage_key());
+      if (it != entries_.end()) {
+        entries_.erase(it);
+        batch->DeleteData(change->storage_key());
+        synced_network_updater_->RemoveNetwork(change->storage_key());
+      }
+      continue;
+    }
+
+    auto& specifics = change->data()
+                          .specifics.wifi_configuration()
+                          .client_only_encrypted_data();
+    synced_network_updater_->AddOrUpdateNetwork(specifics);
+
+    batch->WriteData(change->storage_key(), specifics.SerializeAsString());
+    entries_[change->storage_key()] = std::move(specifics);
+  }
+
+  batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
+  Commit(std::move(batch));
+
+  return base::nullopt;
+}
+
+void WifiConfigurationBridge::GetData(StorageKeyList storage_keys,
+                                      DataCallback callback) {
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+
+  for (const std::string& id : storage_keys) {
+    auto it = entries_.find(id);
+    if (it == entries_.end()) {
+      continue;
+    }
+    batch->Put(id, GenerateWifiEntityData(it->second));
+  }
+  std::move(callback).Run(std::move(batch));
+}
+
+void WifiConfigurationBridge::GetAllDataForDebugging(DataCallback callback) {
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+  for (const auto& entry : entries_) {
+    batch->Put(entry.first, GenerateWifiEntityData(entry.second));
+  }
+  std::move(callback).Run(std::move(batch));
+}
+
+std::string WifiConfigurationBridge::GetClientTag(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKey(entity_data);
+}
+
+std::string WifiConfigurationBridge::GetStorageKey(
+    const syncer::EntityData& entity_data) {
+  return entity_data.specifics.wifi_configuration()
+      .client_only_encrypted_data()
+      .ssid();
+}
+
+void WifiConfigurationBridge::OnStoreCreated(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore> store) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  store_ = std::move(store);
+  store_->ReadAllData(base::BindOnce(&WifiConfigurationBridge::OnReadAllData,
+                                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WifiConfigurationBridge::OnReadAllData(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore::RecordList> records) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  for (syncer::ModelTypeStore::Record& record : *records) {
+    sync_pb::WifiConfigurationSpecificsData data;
+    if (record.id.empty() || !data.ParseFromString(record.value)) {
+      DVLOG(1) << "Unable to parse proto for entry with key: " << record.id;
+      continue;
+    }
+    entries_[record.id] = std::move(data);
+  }
+
+  store_->ReadAllMetadata(
+      base::BindOnce(&WifiConfigurationBridge::OnReadAllMetadata,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WifiConfigurationBridge::OnReadAllMetadata(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+  change_processor()->ModelReadyToSync(std::move(metadata_batch));
+}
+
+void WifiConfigurationBridge::OnCommit(
+    const base::Optional<syncer::ModelError>& error) {
+  if (error)
+    change_processor()->ReportError(*error);
+}
+
+void WifiConfigurationBridge::Commit(
+    std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch) {
+  store_->CommitWriteBatch(std::move(batch),
+                           base::BindOnce(&WifiConfigurationBridge::OnCommit,
+                                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+std::vector<std::string> WifiConfigurationBridge::GetAllSsidsForTesting() {
+  std::vector<std::string> ssids;
+  for (const auto& entry : entries_)
+    ssids.push_back(entry.second.ssid());
+
+  return ssids;
+}
+
+}  // namespace sync_wifi
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge.h b/chromeos/components/sync_wifi/wifi_configuration_bridge.h
new file mode 100644
index 0000000..f7fe8d95
--- /dev/null
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge.h
@@ -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.
+
+#ifndef CHROMEOS_COMPONENTS_SYNC_WIFI_WIFI_CONFIGURATION_BRIDGE_H_
+#define CHROMEOS_COMPONENTS_SYNC_WIFI_WIFI_CONFIGURATION_BRIDGE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chromeos/components/sync_wifi/synced_network_updater.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+class ModelTypeChangeProcessor;
+}  // namespace syncer
+
+namespace sync_wifi {
+
+// Receives updates to network configurations from the Chrome sync back end and
+// from the system network stack and keeps both lists in sync.
+class WifiConfigurationBridge : public syncer::ModelTypeSyncBridge {
+ public:
+  WifiConfigurationBridge(
+      SyncedNetworkUpdater* synced_network_updater,
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+      syncer::OnceModelTypeStoreFactory create_store_callback);
+  ~WifiConfigurationBridge() override;
+
+  // syncer::ModelTypeSyncBridge:
+  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+      override;
+  base::Optional<syncer::ModelError> MergeSyncData(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_data) override;
+  base::Optional<syncer::ModelError> ApplySyncChanges(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+  std::string GetClientTag(const syncer::EntityData& entity_data) override;
+  std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+  // Comes from |entries_| the in-memory map.
+  std::vector<std::string> GetAllSsidsForTesting();
+
+ private:
+  void Commit(std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch);
+
+  // Callbacks for ModelTypeStore.
+  void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
+                      std::unique_ptr<syncer::ModelTypeStore> store);
+  void OnReadAllData(
+      const base::Optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::ModelTypeStore::RecordList> records);
+  void OnReadAllMetadata(const base::Optional<syncer::ModelError>& error,
+                         std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+  void OnCommit(const base::Optional<syncer::ModelError>& error);
+
+  // An in-memory list of the proto's that mirrors what is on the sync server.
+  // This gets updated when changes are received from the server and after local
+  // changes have been committed.  On initialization of this class, it is
+  // populated with the contents of |store_|.
+  base::flat_map<std::string, sync_pb::WifiConfigurationSpecificsData> entries_;
+
+  // The on disk store of WifiConfigurationSpecifics protos that mirrors what
+  // is on the sync server.  This gets updated when changes are received from
+  // the server and after local changes have been committed to the server.
+  std::unique_ptr<syncer::ModelTypeStore> store_;
+
+  SyncedNetworkUpdater* synced_network_updater_;
+
+  base::WeakPtrFactory<WifiConfigurationBridge> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(WifiConfigurationBridge);
+};
+
+}  // namespace sync_wifi
+
+#endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_WIFI_CONFIGURATION_BRIDGE_H_
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
new file mode 100644
index 0000000..0853a3d
--- /dev/null
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
@@ -0,0 +1,269 @@
+// Copyright 2019 The Chromium Authors. 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/components/sync_wifi/wifi_configuration_bridge.h"
+
+#include <map>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/components/sync_wifi/synced_network_updater.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model/model_type_store_test_util.h"
+#include "components/sync/model_impl/in_memory_metadata_change_list.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+#include "components/sync/test/test_matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_wifi {
+
+namespace {
+
+using sync_pb::WifiConfigurationSpecificsData;
+using testing::_;
+using testing::AllOf;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Pair;
+using testing::Return;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
+const char kSsidMeow[] = "meow";
+const char kSsidWoof[] = "woof";
+
+WifiConfigurationSpecificsData CreateSpecifics(const std::string& ssid) {
+  WifiConfigurationSpecificsData specifics;
+  specifics.set_ssid(ssid);
+  specifics.set_security_type(
+      WifiConfigurationSpecificsData::SECURITY_TYPE_PSK);
+  specifics.set_passphrase("password");
+  specifics.set_automatically_connect(
+      WifiConfigurationSpecificsData::AUTOMATICALLY_CONNECT_ENABLED);
+  specifics.set_is_preferred(
+      WifiConfigurationSpecificsData::IS_PREFERRED_ENABLED);
+  specifics.set_metered(WifiConfigurationSpecificsData::METERED_OPTION_AUTO);
+  sync_pb::WifiConfigurationSpecificsData_ProxyConfiguration proxy_config;
+  proxy_config.set_proxy_option(WifiConfigurationSpecificsData::
+                                    ProxyConfiguration::PROXY_OPTION_DISABLED);
+  specifics.mutable_proxy_configuration()->CopyFrom(proxy_config);
+  return specifics;
+}
+
+std::unique_ptr<syncer::EntityData> GenerateWifiEntityData(
+    const sync_pb::WifiConfigurationSpecificsData& data) {
+  auto entity_data = std::make_unique<syncer::EntityData>();
+  entity_data->specifics.mutable_wifi_configuration()
+      ->mutable_client_only_encrypted_data()
+      ->CopyFrom(data);
+  entity_data->name = data.ssid();
+  return entity_data;
+}
+
+bool VectorContainsString(std::vector<std::string> v, std::string s) {
+  return std::find(v.begin(), v.end(), s) != v.end();
+}
+
+bool VectorContainsSsid(
+    const std::vector<sync_pb::WifiConfigurationSpecificsData>& v,
+    std::string s) {
+  for (sync_pb::WifiConfigurationSpecificsData specifics : v) {
+    if (specifics.ssid() == s)
+      return true;
+  }
+  return false;
+}
+
+class TestSyncedNetworkUpdater : public SyncedNetworkUpdater {
+ public:
+  TestSyncedNetworkUpdater() = default;
+  ~TestSyncedNetworkUpdater() override = default;
+
+  const std::vector<sync_pb::WifiConfigurationSpecificsData>&
+  add_or_update_calls() {
+    return add_update_calls_;
+  }
+
+  const std::vector<std::string>& remove_calls() { return remove_calls_; }
+
+ private:
+  void AddOrUpdateNetwork(
+      const sync_pb::WifiConfigurationSpecificsData& specifics) override {
+    add_update_calls_.push_back(specifics);
+  }
+
+  void RemoveNetwork(const std::string& ssid) override {
+    remove_calls_.push_back(ssid);
+  }
+
+  std::vector<sync_pb::WifiConfigurationSpecificsData> add_update_calls_;
+  std::vector<std::string> remove_calls_;
+};
+
+class WifiConfigurationBridgeTest : public testing::Test {
+ protected:
+  WifiConfigurationBridgeTest()
+      : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {}
+
+  void SetUp() override {
+    ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(true));
+    synced_network_updater_ = std::make_unique<TestSyncedNetworkUpdater>();
+    bridge_ = std::make_unique<WifiConfigurationBridge>(
+        synced_network_updater(), mock_processor_.CreateForwardingProcessor(),
+        syncer::ModelTypeStoreTestUtil::MoveStoreToFactory(std::move(store_)));
+  }
+
+  void DisableBridge() {
+    ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(false));
+  }
+
+  syncer::EntityChangeList CreateEntityAddList(
+      const std::vector<WifiConfigurationSpecificsData>& specifics_list) {
+    syncer::EntityChangeList changes;
+    for (const auto& data : specifics_list) {
+      auto entity_data = std::make_unique<syncer::EntityData>();
+      sync_pb::WifiConfigurationSpecifics specifics;
+
+      specifics.mutable_client_only_encrypted_data()->CopyFrom(data);
+      entity_data->specifics.mutable_wifi_configuration()->CopyFrom(specifics);
+
+      entity_data->name = data.ssid();
+
+      changes.push_back(
+          syncer::EntityChange::CreateAdd(data.ssid(), std::move(entity_data)));
+    }
+    return changes;
+  }
+
+  syncer::MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
+
+  WifiConfigurationBridge* bridge() { return bridge_.get(); }
+
+  TestSyncedNetworkUpdater* synced_network_updater() {
+    return synced_network_updater_.get();
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment task_environment_;
+
+  std::unique_ptr<syncer::ModelTypeStore> store_;
+
+  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
+
+  std::unique_ptr<WifiConfigurationBridge> bridge_;
+
+  std::unique_ptr<TestSyncedNetworkUpdater> synced_network_updater_;
+
+  DISALLOW_COPY_AND_ASSIGN(WifiConfigurationBridgeTest);
+};
+
+TEST_F(WifiConfigurationBridgeTest, InitWithTwoNetworksFromServer) {
+  syncer::EntityChangeList remote_input;
+
+  WifiConfigurationSpecificsData entry1 = CreateSpecifics(kSsidMeow);
+  WifiConfigurationSpecificsData entry2 = CreateSpecifics(kSsidWoof);
+
+  remote_input.push_back(syncer::EntityChange::CreateAdd(
+      kSsidMeow, GenerateWifiEntityData(entry1)));
+  remote_input.push_back(syncer::EntityChange::CreateAdd(
+      kSsidWoof, GenerateWifiEntityData(entry2)));
+
+  bridge()->MergeSyncData(
+      std::make_unique<syncer::InMemoryMetadataChangeList>(),
+      std::move(remote_input));
+
+  std::vector<std::string> ssids = bridge()->GetAllSsidsForTesting();
+  EXPECT_EQ(2u, ssids.size());
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidMeow));
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidWoof));
+
+  const std::vector<sync_pb::WifiConfigurationSpecificsData>& networks =
+      synced_network_updater()->add_or_update_calls();
+  EXPECT_EQ(2u, networks.size());
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidMeow));
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidWoof));
+}
+
+TEST_F(WifiConfigurationBridgeTest, ApplySyncChangesAddTwoSpecifics) {
+  const WifiConfigurationSpecificsData specifics1 = CreateSpecifics(kSsidMeow);
+  const WifiConfigurationSpecificsData specifics2 = CreateSpecifics(kSsidWoof);
+
+  base::Optional<syncer::ModelError> error =
+      bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                                 CreateEntityAddList({specifics1, specifics2}));
+  EXPECT_FALSE(error);
+  std::vector<std::string> ssids = bridge()->GetAllSsidsForTesting();
+  EXPECT_EQ(2u, ssids.size());
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidMeow));
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidWoof));
+
+  const std::vector<sync_pb::WifiConfigurationSpecificsData>& networks =
+      synced_network_updater()->add_or_update_calls();
+  EXPECT_EQ(2u, networks.size());
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidMeow));
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidWoof));
+}
+
+TEST_F(WifiConfigurationBridgeTest, ApplySyncChangesOneAdd) {
+  WifiConfigurationSpecificsData entry = CreateSpecifics(kSsidMeow);
+
+  syncer::EntityChangeList add_changes;
+
+  add_changes.push_back(syncer::EntityChange::CreateAdd(
+      kSsidMeow, GenerateWifiEntityData(entry)));
+
+  bridge()->ApplySyncChanges(
+      std::make_unique<syncer::InMemoryMetadataChangeList>(),
+      std::move(add_changes));
+  std::vector<std::string> ssids = bridge()->GetAllSsidsForTesting();
+  EXPECT_EQ(1u, ssids.size());
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidMeow));
+
+  const std::vector<sync_pb::WifiConfigurationSpecificsData>& networks =
+      synced_network_updater()->add_or_update_calls();
+  EXPECT_EQ(1u, networks.size());
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidMeow));
+}
+
+TEST_F(WifiConfigurationBridgeTest, ApplySyncChangesOneDeletion) {
+  WifiConfigurationSpecificsData entry = CreateSpecifics(kSsidMeow);
+
+  syncer::EntityChangeList add_changes;
+
+  add_changes.push_back(syncer::EntityChange::CreateAdd(
+      kSsidMeow, GenerateWifiEntityData(entry)));
+
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                             std::move(add_changes));
+  std::vector<std::string> ssids = bridge()->GetAllSsidsForTesting();
+  EXPECT_EQ(1u, ssids.size());
+  EXPECT_TRUE(VectorContainsString(ssids, kSsidMeow));
+
+  const std::vector<sync_pb::WifiConfigurationSpecificsData>& networks =
+      synced_network_updater()->add_or_update_calls();
+  EXPECT_EQ(1u, networks.size());
+  EXPECT_TRUE(VectorContainsSsid(networks, kSsidMeow));
+
+  syncer::EntityChangeList delete_changes;
+  delete_changes.push_back(syncer::EntityChange::CreateDelete(kSsidMeow));
+
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                             std::move(delete_changes));
+  EXPECT_TRUE(bridge()->GetAllSsidsForTesting().empty());
+
+  const std::vector<std::string>& removed_networks =
+      synced_network_updater()->remove_calls();
+  EXPECT_EQ(1u, removed_networks.size());
+  EXPECT_TRUE(VectorContainsString(removed_networks, kSsidMeow));
+}
+
+}  // namespace
+
+}  // namespace sync_wifi
\ No newline at end of file
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.cc b/chromeos/dbus/kerberos/fake_kerberos_client.cc
index 049197df..7ba95233 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.cc
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.cc
@@ -5,7 +5,6 @@
 #include "chromeos/dbus/kerberos/fake_kerberos_client.h"
 
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -127,26 +126,33 @@
 void FakeKerberosClient::ClearAccounts(
     const kerberos::ClearAccountsRequest& request,
     ClearAccountsCallback callback) {
-  switch (request.mode()) {
-    case kerberos::CLEAR_ALL:
-      accounts_.clear();
-      break;
+  std::unordered_set<std::string> keep_list(
+      request.principal_names_to_ignore_size());
+  for (int n = 0; n < request.principal_names_to_ignore_size(); ++n)
+    keep_list.insert(request.principal_names_to_ignore(n));
 
-    case kerberos::CLEAR_ONLY_UNMANAGED_ACCOUNTS:
-      accounts_.erase(std::remove_if(accounts_.begin(), accounts_.end(),
-                                     [](const AccountData& account) {
-                                       return !account.is_managed;
-                                     }),
-                      accounts_.end());
-      break;
+  for (auto it = accounts_.begin(); it != accounts_.end(); /* empty */) {
+    if (base::Contains(keep_list, it->principal_name)) {
+      ++it;
+      continue;
+    }
 
-    case kerberos::CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS:
-      for (auto& account : accounts_) {
-        if (!account.is_managed)
-          account.password.clear();
-      }
-      break;
+    switch (DetermineWhatToRemove(request.mode(), *it)) {
+      case WhatToRemove::kNothing:
+        ++it;
+        continue;
+
+      case WhatToRemove::kPassword:
+        it->password.clear();
+        ++it;
+        continue;
+
+      case WhatToRemove::kAccount:
+        it = accounts_.erase(it);
+        continue;
+    }
   }
+
   PostResponse(std::move(callback), kerberos::ERROR_NONE);
 }
 
@@ -305,4 +311,25 @@
   return !(*this == other);
 }
 
+// static
+FakeKerberosClient::WhatToRemove FakeKerberosClient::DetermineWhatToRemove(
+    kerberos::ClearMode mode,
+    const AccountData& data) {
+  switch (mode) {
+    case kerberos::CLEAR_ALL:
+      return WhatToRemove::kAccount;
+
+    case kerberos::CLEAR_ONLY_MANAGED_ACCOUNTS:
+      return data.is_managed ? WhatToRemove::kAccount : WhatToRemove::kNothing;
+
+    case kerberos::CLEAR_ONLY_UNMANAGED_ACCOUNTS:
+      return !data.is_managed ? WhatToRemove::kAccount : WhatToRemove::kNothing;
+
+    case kerberos::CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS:
+      return !data.is_managed ? WhatToRemove::kPassword
+                              : WhatToRemove::kNothing;
+  }
+  return WhatToRemove::kNothing;
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.h b/chromeos/dbus/kerberos/fake_kerberos_client.h
index 0616ff5..668dded 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.h
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 #include "chromeos/dbus/kerberos/kerberos_client.h"
@@ -72,13 +73,18 @@
     bool operator!=(const AccountData& other) const;
   };
 
+  enum class WhatToRemove { kNothing, kPassword, kAccount };
+
+  // Determines what data to remove, depending on |mode| and |data|.
+  static WhatToRemove DetermineWhatToRemove(kerberos::ClearMode mode,
+                                            const AccountData& data);
+
   // Returns the AccountData for |principal_name| if available or nullptr
   // otherwise.
   AccountData* GetAccountData(const std::string& principal_name);
 
   // Maps principal name (user@REALM.COM) to account data.
-  using AccountsMap = std::vector<AccountData>;
-  AccountsMap accounts_;
+  std::vector<AccountData> accounts_;
 
   KerberosFilesChangedCallback kerberos_files_changed_callback_;
   KerberosTicketExpiringCallback kerberos_ticket_expiring_callback_;
diff --git a/chromeos/network/network_configuration_handler_unittest.cc b/chromeos/network/network_configuration_handler_unittest.cc
index 46e38c5..16675fa 100644
--- a/chromeos/network/network_configuration_handler_unittest.cc
+++ b/chromeos/network/network_configuration_handler_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chromeos/network/network_configuration_handler.h"
+
 #include <stddef.h>
 
 #include <map>
@@ -12,17 +14,16 @@
 #include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/values.h"
 #include "chromeos/dbus/shill/shill_clients.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "chromeos/dbus/shill/shill_profile_client.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
-#include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_configuration_observer.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state.h"
@@ -309,7 +310,8 @@
   std::unique_ptr<NetworkConfigurationHandler> network_configuration_handler_;
   std::unique_ptr<TestNetworkStateHandlerObserver>
       network_state_handler_observer_;
-  base::MessageLoopForUI message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
   std::string success_callback_name_;
   std::string get_properties_path_;
   std::unique_ptr<base::DictionaryValue> get_properties_;
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 064f19cc..d0a1f94 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -216,6 +216,12 @@
       return false;
     onc_source_ = ui_data->onc_source();
     return true;
+  } else if (key == shill::kProbeUrlProperty) {
+    std::string probe_url_string;
+    if (!GetStringValue(key, value, &probe_url_string))
+      return false;
+    probe_url_ = GURL(probe_url_string);
+    return true;
   }
   return false;
 }
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h
index 0b3e40ea..489489e 100644
--- a/chromeos/network/network_state.h
+++ b/chromeos/network/network_state.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -77,6 +78,7 @@
   const std::string& device_path() const { return device_path_; }
   const std::string& guid() const { return guid_; }
   const std::string& profile_path() const { return profile_path_; }
+  const GURL& probe_url() const { return probe_url_; }
   ::onc::ONCSource onc_source() const { return onc_source_; }
 
   // Provides the error for the last attempt to connect/configure the network
@@ -272,6 +274,7 @@
   std::string connection_state_;
   std::string last_connection_state_;
   std::string profile_path_;
+  GURL probe_url_;
   std::vector<uint8_t> raw_ssid_;  // Unknown encoding. Not necessarily UTF-8.
   int priority_ = 0;  // kPriority, used for organizing known networks.
   ::onc::ONCSource onc_source_ = ::onc::ONC_SOURCE_UNKNOWN;
diff --git a/chromeos/profiles/OWNERS b/chromeos/profiles/OWNERS
new file mode 100644
index 0000000..0d73e4e
--- /dev/null
+++ b/chromeos/profiles/OWNERS
@@ -0,0 +1 @@
+per-file orderfile.newest.txt=*
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
new file mode 100644
index 0000000..9e996d0
--- /dev/null
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -0,0 +1 @@
+chromeos-chrome-orderfile-77.0.3849.0_rc-r1.orderfile.xz
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index faf98acd..2e60ef2a 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -206,6 +206,8 @@
       override {}
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   void UpdateInternalMediaPlayerStatus(
       media_session::mojom::MediaSessionAction action);
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
index f0a3a1a..a1d9b78 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
@@ -62,10 +62,11 @@
       android_sms_app_helper_delegate_(android_sms_app_helper_delegate) {
   host_status_provider_->AddObserver(this);
   feature_state_manager_->AddObserver(this);
-  InstallPwaIfNeeded();
+  UpdatePwaInstallationState();
 }
 
-bool AndroidSmsAppInstallingStatusObserver::IsPwaNeeded() {
+bool AndroidSmsAppInstallingStatusObserver::
+    DoesFeatureStateAllowInstallation() {
   mojom::FeatureState feature_state =
       feature_state_manager_->GetFeatureStates()[mojom::Feature::kMessages];
   if (feature_state != mojom::FeatureState::kEnabledByUser &&
@@ -84,26 +85,35 @@
   return true;
 }
 
-void AndroidSmsAppInstallingStatusObserver::InstallPwaIfNeeded() {
-  // If PWA is not needed, clear default to persist cookie that was set
-  // during the last installation.
-  if (!IsPwaNeeded()) {
+void AndroidSmsAppInstallingStatusObserver::UpdatePwaInstallationState() {
+  if (!DoesFeatureStateAllowInstallation()) {
+    // The feature is disabled, ensure that the integration cookie is removed.
     android_sms_app_helper_delegate_->TearDownAndroidSmsApp();
     return;
   }
 
+  if (android_sms_app_helper_delegate_->HasAppBeenManuallyUninstalledByUser()) {
+    feature_state_manager_->SetFeatureEnabledState(mojom::Feature::kMessages,
+                                                   false);
+
+    // The feature is now disabled, clear the cookie and pref.
+    android_sms_app_helper_delegate_->TearDownAndroidSmsApp();
+
+    return;
+  }
+
   // Otherwise, set the default to persist cookie and install the PWA.
   android_sms_app_helper_delegate_->SetUpAndroidSmsApp();
 }
 
 void AndroidSmsAppInstallingStatusObserver::OnHostStatusChange(
     const HostStatusProvider::HostStatusWithDevice& host_status_with_device) {
-  InstallPwaIfNeeded();
+  UpdatePwaInstallationState();
 }
 
 void AndroidSmsAppInstallingStatusObserver::OnFeatureStatesChange(
     const FeatureStateManager::FeatureStatesMap& feature_states_map) {
-  InstallPwaIfNeeded();
+  UpdatePwaInstallationState();
 }
 
 }  // namespace multidevice_setup
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
index ff0500afc..c2d0ccb 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
@@ -52,8 +52,8 @@
   void OnFeatureStatesChange(
       const FeatureStateManager::FeatureStatesMap& feature_states_map) override;
 
-  bool IsPwaNeeded();
-  void InstallPwaIfNeeded();
+  bool DoesFeatureStateAllowInstallation();
+  void UpdatePwaInstallationState();
 
   HostStatusProvider* host_status_provider_;
   FeatureStateManager* feature_state_manager_;
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
index caa9e26..c9a0a28 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
@@ -122,6 +122,19 @@
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
+       DoesNotInstallAfterHostVerifiedIfUninstalledByUser) {
+  fake_app_helper_delegate()->Reset();
+  fake_app_helper_delegate()->set_has_app_been_manually_uninstalled(true);
+
+  SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
+                    base::nullopt /* host_device */);
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
+
+  SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
+}
+
+TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallsAfterHostVerifiedIfNotSupportedByPhone) {
   SetMessagesFeatureState(mojom::FeatureState::kNotSupportedByPhone);
   fake_app_helper_delegate()->Reset();
diff --git a/chromeos/services/multidevice_setup/feature_state_manager.cc b/chromeos/services/multidevice_setup/feature_state_manager.cc
index 97eda105..46fa83e 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager.cc
+++ b/chromeos/services/multidevice_setup/feature_state_manager.cc
@@ -18,7 +18,9 @@
 
   // Changing the state is only allowed when changing from enabled to disabled
   // or disabled to enabled.
-  if ((state == mojom::FeatureState::kEnabledByUser && !enabled) ||
+  if ((((state == mojom::FeatureState::kEnabledByUser) ||
+        (state == mojom::FeatureState::kFurtherSetupRequired)) &&
+       !enabled) ||
       (state == mojom::FeatureState::kDisabledByUser && enabled)) {
     PerformSetFeatureEnabledState(feature, enabled);
     return true;
diff --git a/chromeos/services/multidevice_setup/feature_state_manager.h b/chromeos/services/multidevice_setup/feature_state_manager.h
index 98c259e..08447c752 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager.h
+++ b/chromeos/services/multidevice_setup/feature_state_manager.h
@@ -37,7 +37,8 @@
 
   // Attempts to enable or disable the feature; returns whether this operation
   // succeeded. A feature can only be changed via this function if the current
-  // state is mojom::FeatureState::kEnabledByUser or
+  // state is mojom::FeatureState::kEnabledByUser,
+  // mojom::FeatureState::kFurtherSetupRequired or
   // mojom::FeatureState::kDisabledByUser.
   bool SetFeatureEnabledState(mojom::Feature feature, bool enabled);
 
diff --git a/chromeos/services/multidevice_setup/feature_state_manager_impl.cc b/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
index ea9545e1..04964f3 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
+++ b/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
@@ -385,6 +385,11 @@
   if (feature != mojom::Feature::kMessages)
     return false;
 
+  if (GetEnabledOrDisabledState(feature) ==
+      mojom::FeatureState::kDisabledByUser) {
+    return false;
+  }
+
   return !android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete();
 }
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h b/chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h
index 49841c3..e288793c 100644
--- a/chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h
+++ b/chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h
@@ -25,6 +25,9 @@
   // Cleans up previously setup Messages for Web PWA. This does not uninstall
   // the PWA but only clears state that was setup for the PWA.
   virtual void TearDownAndroidSmsApp() = 0;
+  // Returns true if the app was ever installed successfully since the feature
+  // was enabled and then been manually uninstalled by the user.
+  virtual bool HasAppBeenManuallyUninstalledByUser() = 0;
 
  protected:
   AndroidSmsAppHelperDelegate() = default;
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
index d32d0ec..c7378feec 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
@@ -35,6 +35,10 @@
   is_default_to_persist_cookie_set_ = false;
 }
 
+bool FakeAndroidSmsAppHelperDelegate::HasAppBeenManuallyUninstalledByUser() {
+  return has_app_been_manually_uninstalled_;
+}
+
 }  // namespace multidevice_setup
 
 }  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
index e0e28f90..a184d015 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
@@ -24,6 +24,11 @@
     return is_default_to_persist_cookie_set_;
   }
 
+  void set_has_app_been_manually_uninstalled(
+      bool has_app_been_manually_uninstalled) {
+    has_app_been_manually_uninstalled_ = has_app_been_manually_uninstalled;
+  }
+
   // Sets all booleans representing recorded actions to false.
   void Reset();
 
@@ -32,10 +37,12 @@
   void SetUpAndroidSmsApp() override;
   void SetUpAndLaunchAndroidSmsApp() override;
   void TearDownAndroidSmsApp() override;
+  bool HasAppBeenManuallyUninstalledByUser() override;
 
   bool has_installed_app_ = false;
   bool has_launched_app_ = false;
   bool is_default_to_persist_cookie_set_ = false;
+  bool has_app_been_manually_uninstalled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeAndroidSmsAppHelperDelegate);
 };
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 7c611803..4754a74e 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -295,7 +295,6 @@
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
       "//components/invalidation/impl",
       "//components/invalidation/impl:java",
-      "//components/journey:unit_tests",
       "//components/policy/android:policy_java",
       "//components/signin/core/browser",
       "//components/signin/core/browser/android:java",
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 0cd272f0..823105c 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1874,6 +1874,9 @@
       card.GetMatchingTypes(value, app_locale, &matching_types);
     }
 
+    if (IsUPIVirtualPaymentAddress(value))
+      matching_types.insert(UPI_VPA);
+
     if (matching_types.empty()) {
       matching_types.insert(UNKNOWN_TYPE);
       ServerFieldTypeValidityStateMap matching_types_validities;
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index d855b77..35472453 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -630,6 +630,7 @@
   FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
                            DeterminePossibleFieldTypesForUploadStressTest);
   FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DisambiguateUploadTypes);
+  FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, CrowdsourceUPIVPA);
   FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
                            DisabledAutofillDispatchesError);
   FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index f17b106..853c5c1e 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -83,6 +83,8 @@
 using testing::_;
 using testing::AnyOf;
 using testing::AtLeast;
+using testing::Contains;
+using testing::ElementsAre;
 using testing::HasSubstr;
 using testing::Not;
 using testing::Return;
@@ -5390,6 +5392,28 @@
   }
 }
 
+// When a field contains fields with UPI/VPA values, a crowdsourcing vote should
+// be uploaded.
+TEST_F(AutofillManagerTest, CrowdsourceUPIVPA) {
+  std::vector<AutofillProfile> profiles;
+  std::vector<CreditCard> credit_cards;
+
+  FormData form;
+  FormFieldData field;
+  test::CreateTestFormField("", "name1", "1234@indianbank", "text", &field);
+  form.fields.push_back(field);
+  test::CreateTestFormField("", "name2", "not-upi@gmail.com", "text", &field);
+  form.fields.push_back(field);
+  FormStructure form_structure(form);
+
+  AutofillManager::DeterminePossibleFieldTypesForUpload(
+      profiles, credit_cards, "en-us", &form_structure);
+
+  EXPECT_THAT(form_structure.field(0)->possible_types(), ElementsAre(UPI_VPA));
+  EXPECT_THAT(form_structure.field(1)->possible_types(),
+              Not(Contains(UPI_VPA)));
+}
+
 TEST_F(AutofillManagerTest, RemoveProfile) {
   // Add and remove an Autofill profile.
   AutofillProfile profile;
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 89d06bf7..a019c24 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -107,6 +107,7 @@
     case FIELD_WITH_DEFAULT_VALUE:
     case MERCHANT_EMAIL_SIGNUP:
     case MERCHANT_PROMO_CODE:
+    case UPI_VPA:
       return NO_GROUP;
 
     case MAX_VALID_FIELD_TYPE:
@@ -419,15 +420,14 @@
     case HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR:
       return CREDIT_CARD_EXP_4_DIGIT_YEAR;
 
+    case HTML_TYPE_UPI_VPA:
+      return UPI_VPA;
+
     // These types aren't stored; they're transient.
     case HTML_TYPE_TRANSACTION_AMOUNT:
     case HTML_TYPE_TRANSACTION_CURRENCY:
       return UNKNOWN_TYPE;
 
-    // TODO(crbug/702223): Add autofill support for UPI-VPA.
-    case HTML_TYPE_UPI_VPA:
-      return UNKNOWN_TYPE;
-
     case HTML_TYPE_UNRECOGNIZED:
       return UNKNOWN_TYPE;
   }
@@ -783,6 +783,8 @@
       return "SINGLE_USERNAME";
     case NOT_USERNAME:
       return "NOT_USERNAME";
+    case UPI_VPA:
+      return "UPI_VPA";
     case AMBIGUOUS_TYPE:
       return "AMBIGUOUS_TYPE";
     case MAX_VALID_FIELD_TYPE:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index 8be69f9..3080ba1 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -185,9 +185,13 @@
   // Text-type fields which are not usernames.
   NOT_USERNAME = 101,
 
+  // UPI/VPA is a payment method, which is stored and filled. See
+  // https://en.wikipedia.org/wiki/Unified_Payments_Interface
+  UPI_VPA = 102,
+
   // No new types can be added without a corresponding change to the Autofill
   // server.
-  MAX_VALID_FIELD_TYPE = 102,
+  MAX_VALID_FIELD_TYPE = 103,
 };
 
 // The list of all HTML autocomplete field type hints supported by Chrome.
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 9483f6a..1f4246d 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -454,12 +454,16 @@
   const FormSignature form_signature = form.form_signature();
   constexpr FieldSignature kNullFieldSignature =
       0;  // Not relevant for form level metadata.
-  EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature,
-                        RandomizedEncoder::FORM_ID, form.id_attribute(),
-                        metadata->mutable_id());
-  EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature,
-                        RandomizedEncoder::FORM_NAME, form.name_attribute(),
-                        metadata->mutable_name());
+  if (!form.id_attribute().empty()) {
+    EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature,
+                          RandomizedEncoder::FORM_ID, form.id_attribute(),
+                          metadata->mutable_id());
+  }
+  if (!form.name_attribute().empty()) {
+    EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature,
+                          RandomizedEncoder::FORM_NAME, form.name_attribute(),
+                          metadata->mutable_name());
+  }
 }
 
 void PopulateRandomizedFieldMetadata(
@@ -469,32 +473,47 @@
     AutofillRandomizedFieldMetadata* metadata) {
   const FormSignature form_signature = form.form_signature();
   const FieldSignature field_signature = field.GetFieldSignature();
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_ID, field.id_attribute,
-                        metadata->mutable_id());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_NAME, field.name_attribute,
-                        metadata->mutable_name());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_CONTROL_TYPE,
-                        field.form_control_type, metadata->mutable_type());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_LABEL, field.label,
-                        metadata->mutable_label());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_ARIA_LABEL, field.aria_label,
-                        metadata->mutable_aria_label());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_ARIA_DESCRIPTION,
-                        field.aria_description,
-                        metadata->mutable_aria_description());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_CSS_CLASS, field.css_classes,
-                        metadata->mutable_css_class());
-  EncodeRandomizedValue(encoder, form_signature, field_signature,
-                        RandomizedEncoder::FIELD_PLACEHOLDER, field.placeholder,
-                        metadata->mutable_placeholder());
-  // TODO(rogerm): Add hash of initial value.
+  if (!field.id_attribute.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_ID, field.id_attribute,
+                          metadata->mutable_id());
+  }
+  if (!field.name_attribute.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_NAME, field.name_attribute,
+                          metadata->mutable_name());
+  }
+  if (!field.form_control_type.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_CONTROL_TYPE,
+                          field.form_control_type, metadata->mutable_type());
+  }
+  if (!field.label.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_LABEL, field.label,
+                          metadata->mutable_label());
+  }
+  if (!field.aria_label.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_ARIA_LABEL, field.aria_label,
+                          metadata->mutable_aria_label());
+  }
+  if (!field.aria_description.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_ARIA_DESCRIPTION,
+                          field.aria_description,
+                          metadata->mutable_aria_description());
+  }
+  if (!field.css_classes.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_CSS_CLASS, field.css_classes,
+                          metadata->mutable_css_class());
+  }
+  if (!field.placeholder.empty()) {
+    EncodeRandomizedValue(encoder, form_signature, field_signature,
+                          RandomizedEncoder::FIELD_PLACEHOLDER,
+                          field.placeholder, metadata->mutable_placeholder());
+  }
 }
 
 void EncodeFormMetadataForQuery(const FormStructure& form,
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index e9e4ee7..7ebf1c720 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -4487,9 +4487,12 @@
       {"email_id", "email_name", "Email:", "Please enter your email address",
        "Type your email address", "You can type your email address here",
        "blah"},
+      {"id_only", "", "", "", "", "", ""},
+      {"", "name_only", "", "", "", "", ""},
   };
 
   FormData form;
+  form.id_attribute = ASCIIToUTF16("form-id");
   form.url = GURL("http://www.foo.com/");
   for (const auto& f : kFieldMetadata) {
     FormFieldData field;
@@ -4518,49 +4521,91 @@
       &upload));
 
   const auto form_signature = form_structure.form_signature();
-  EXPECT_EQ(upload.randomized_form_metadata().id().encoded_bits(),
-            encoder.Encode(form_signature, 0, RandomizedEncoder::FORM_ID,
-                           form_structure.id_attribute()));
-  EXPECT_EQ(upload.randomized_form_metadata().name().encoded_bits(),
-            encoder.Encode(form_signature, 0, RandomizedEncoder::FORM_NAME,
-                           form_structure.name_attribute()));
 
+  if (form.id_attribute.empty()) {
+    EXPECT_FALSE(upload.randomized_form_metadata().has_id());
+  } else {
+    EXPECT_EQ(upload.randomized_form_metadata().id().encoded_bits(),
+              encoder.Encode(form_signature, 0, RandomizedEncoder::FORM_ID,
+                             form_structure.id_attribute()));
+  }
+
+  if (form.name_attribute.empty()) {
+    EXPECT_FALSE(upload.randomized_form_metadata().has_name());
+  } else {
+    EXPECT_EQ(upload.randomized_form_metadata().name().encoded_bits(),
+              encoder.Encode(form_signature, 0, RandomizedEncoder::FORM_NAME,
+                             form_structure.name_attribute()));
+  }
   ASSERT_EQ(static_cast<size_t>(upload.field_size()),
             base::size(kFieldMetadata));
   for (int i = 0; i < upload.field_size(); ++i) {
     const auto& metadata = upload.field(i).randomized_field_metadata();
     const auto& field = *form_structure.field(i);
     const auto field_signature = field.GetFieldSignature();
-    EXPECT_EQ(metadata.id().encoded_bits(),
-              encoder.Encode(form_signature, field_signature,
-                             RandomizedEncoder::FIELD_ID, field.id_attribute));
-    EXPECT_EQ(
-        metadata.name().encoded_bits(),
-        encoder.Encode(form_signature, field_signature,
-                       RandomizedEncoder::FIELD_NAME, field.name_attribute));
-    EXPECT_EQ(metadata.type().encoded_bits(),
-              encoder.Encode(form_signature, field_signature,
-                             RandomizedEncoder::FIELD_CONTROL_TYPE,
-                             field.form_control_type));
-    EXPECT_EQ(metadata.label().encoded_bits(),
-              encoder.Encode(form_signature, field_signature,
-                             RandomizedEncoder::FIELD_LABEL, field.label));
-    EXPECT_EQ(
-        metadata.aria_label().encoded_bits(),
-        encoder.Encode(form_signature, field_signature,
-                       RandomizedEncoder::FIELD_ARIA_LABEL, field.aria_label));
-    EXPECT_EQ(metadata.aria_description().encoded_bits(),
-              encoder.Encode(form_signature, field_signature,
-                             RandomizedEncoder::FIELD_ARIA_DESCRIPTION,
-                             field.aria_description));
-    EXPECT_EQ(
-        metadata.css_class().encoded_bits(),
-        encoder.Encode(form_signature, field_signature,
-                       RandomizedEncoder::FIELD_CSS_CLASS, field.css_classes));
-    EXPECT_EQ(metadata.placeholder().encoded_bits(),
-              encoder.Encode(form_signature, field_signature,
-                             RandomizedEncoder::FIELD_PLACEHOLDER,
-                             field.placeholder));
+    if (field.id_attribute.empty()) {
+      EXPECT_FALSE(metadata.has_id());
+    } else {
+      EXPECT_EQ(
+          metadata.id().encoded_bits(),
+          encoder.Encode(form_signature, field_signature,
+                         RandomizedEncoder::FIELD_ID, field.id_attribute));
+    }
+    if (field.name.empty()) {
+      EXPECT_FALSE(metadata.has_name());
+    } else {
+      EXPECT_EQ(
+          metadata.name().encoded_bits(),
+          encoder.Encode(form_signature, field_signature,
+                         RandomizedEncoder::FIELD_NAME, field.name_attribute));
+    }
+    if (field.form_control_type.empty()) {
+      EXPECT_FALSE(metadata.has_type());
+    } else {
+      EXPECT_EQ(metadata.type().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_CONTROL_TYPE,
+                               field.form_control_type));
+    }
+    if (field.label.empty()) {
+      EXPECT_FALSE(metadata.has_label());
+    } else {
+      EXPECT_EQ(metadata.label().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_LABEL, field.label));
+    }
+    if (field.aria_label.empty()) {
+      EXPECT_FALSE(metadata.has_aria_label());
+    } else {
+      EXPECT_EQ(metadata.aria_label().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_ARIA_LABEL,
+                               field.aria_label));
+    }
+    if (field.aria_description.empty()) {
+      EXPECT_FALSE(metadata.has_aria_description());
+    } else {
+      EXPECT_EQ(metadata.aria_description().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_ARIA_DESCRIPTION,
+                               field.aria_description));
+    }
+    if (field.css_classes.empty()) {
+      EXPECT_FALSE(metadata.has_css_class());
+    } else {
+      EXPECT_EQ(metadata.css_class().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_CSS_CLASS,
+                               field.css_classes));
+    }
+    if (field.placeholder.empty()) {
+      EXPECT_FALSE(metadata.has_placeholder());
+    } else {
+      EXPECT_EQ(metadata.placeholder().encoded_bits(),
+                encoder.Encode(form_signature, field_signature,
+                               RandomizedEncoder::FIELD_PLACEHOLDER,
+                               field.placeholder));
+    }
   }
 }
 
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index b138227a..1b376c7 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -54,7 +54,7 @@
 
 const base::Feature kAutofillDoNotUploadSaveUnsupportedCards{
     "AutofillDoNotUploadSaveUnsupportedCards",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether the credit card downstream keyboard accessory shows
 // the Google Pay logo animation on iOS.
diff --git a/components/autofill/core/common/autofill_regexes.cc b/components/autofill/core/common/autofill_regexes.cc
index 3c289e3..c4402bc 100644
--- a/components/autofill/core/common/autofill_regexes.cc
+++ b/components/autofill/core/common/autofill_regexes.cc
@@ -8,11 +8,11 @@
 #include <unordered_map>
 #include <utility>
 
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/strings/string16.h"
-#include "base/threading/thread_local.h"
+#include "base/synchronization/lock.h"
 #include "third_party/icu/source/i18n/unicode/regex.h"
 
 namespace {
@@ -24,14 +24,13 @@
 // singleton instance (https://crbug.com/812182).
 class AutofillRegexes {
  public:
-  static AutofillRegexes* ThreadSpecificInstance();
+  AutofillRegexes() = default;
 
   // Returns the compiled regex matcher corresponding to |pattern|.
   icu::RegexMatcher* GetMatcher(const base::string16& pattern);
 
  private:
-  AutofillRegexes();
-  ~AutofillRegexes();
+  ~AutofillRegexes() = default;
 
   // Maps patterns to their corresponding regex matchers.
   std::unordered_map<base::string16, std::unique_ptr<icu::RegexMatcher>>
@@ -40,24 +39,6 @@
   DISALLOW_COPY_AND_ASSIGN(AutofillRegexes);
 };
 
-base::LazyInstance<base::ThreadLocalPointer<AutofillRegexes>>::Leaky
-    g_autofill_regexes_tls = LAZY_INSTANCE_INITIALIZER;
-
-// static
-AutofillRegexes* AutofillRegexes::ThreadSpecificInstance() {
-  if (g_autofill_regexes_tls.Pointer()->Get())
-    return g_autofill_regexes_tls.Pointer()->Get();
-  return new AutofillRegexes;
-}
-
-AutofillRegexes::AutofillRegexes() {
-  g_autofill_regexes_tls.Pointer()->Set(this);
-}
-
-AutofillRegexes::~AutofillRegexes() {
-  g_autofill_regexes_tls.Pointer()->Set(nullptr);
-}
-
 icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) {
   auto it = matchers_.find(pattern);
   if (it == matchers_.end()) {
@@ -82,8 +63,11 @@
 
 bool MatchesPattern(const base::string16& input,
                     const base::string16& pattern) {
-  icu::RegexMatcher* matcher =
-      AutofillRegexes::ThreadSpecificInstance()->GetMatcher(pattern);
+  static base::NoDestructor<AutofillRegexes> g_autofill_regexes;
+  static base::NoDestructor<base::Lock> g_lock;
+  base::AutoLock lock(*g_lock);
+
+  icu::RegexMatcher* matcher = g_autofill_regexes->GetMatcher(pattern);
   icu::UnicodeString icu_input(FALSE, input.data(), input.length());
   matcher->reset(icu_input);
 
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index d00a57e..0001d5b 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -284,6 +284,11 @@
       std::unique_ptr<FormProto> form,
       base::RepeatingCallback<void(const FormProto::Result*)> callback) = 0;
 
+  // Force showing the UI if no UI is shown. This is useful when executing a
+  // direct action which realizes it needs to interact with the user. Once
+  // shown, the UI stays up until the end of the flow.
+  virtual void RequireUI() = 0;
+
  protected:
   ActionDelegate() = default;
 };
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 6e564f309..1197df8 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
@@ -57,6 +57,9 @@
   payment_options->additional_actions_callback =
       base::BindOnce(&GetPaymentInformationAction::OnAdditionalActionTriggered,
                      weak_ptr_factory_.GetWeakPtr());
+  payment_options->terms_link_callback = base::BindOnce(
+      &GetPaymentInformationAction::OnTermsAndConditionsLinkClicked,
+      weak_ptr_factory_.GetWeakPtr());
 
   // Gather info for UMA histograms.
   if (!presented_to_user_) {
@@ -156,13 +159,21 @@
   std::move(callback_).Run(std::move(processed_action_proto_));
 }
 
+void GetPaymentInformationAction::OnTermsAndConditionsLinkClicked(int link) {
+  if (!callback_)
+    return;
+
+  UpdateProcessedAction(ACTION_APPLIED);
+  processed_action_proto_->mutable_payment_details()->set_terms_link(link);
+  action_successful_ = true;
+  std::move(callback_).Run(std::move(processed_action_proto_));
+}
+
 std::unique_ptr<PaymentRequestOptions>
 GetPaymentInformationAction::CreateOptionsFromProto() const {
   auto payment_options = std::make_unique<PaymentRequestOptions>();
   auto get_payment_information = proto_.get_payment_information();
 
-  payment_options->request_terms_and_conditions =
-      get_payment_information.request_terms_and_conditions();
   if (get_payment_information.has_contact_details()) {
     auto contact_details = get_payment_information.contact_details();
     payment_options->request_payer_email =
@@ -208,6 +219,19 @@
       payment_options->initial_terms_and_conditions = REQUIRES_REVIEW;
       break;
   }
+
+  if (get_payment_information.request_terms_and_conditions()) {
+    payment_options->show_terms_as_checkbox =
+        get_payment_information.show_terms_as_checkbox();
+    payment_options->accept_terms_and_conditions_text =
+        get_payment_information.accept_terms_and_conditions_text();
+    if (payment_options->accept_terms_and_conditions_text.empty()) {
+      payment_options->accept_terms_and_conditions_text =
+          l10n_util::GetStringUTF8(
+              IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_ACCEPT);
+    }
+  }
+
   return payment_options;
 }
 
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.h b/components/autofill_assistant/browser/actions/get_payment_information_action.h
index 6a21aea..2de78be 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.h
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.h
@@ -37,6 +37,7 @@
       const GetPaymentInformationProto& get_payment_information,
       std::unique_ptr<PaymentInformation> payment_information);
   void OnAdditionalActionTriggered(int index);
+  void OnTermsAndConditionsLinkClicked(int link);
 
   // Creates a new instance of |PaymentRequestOptions| from |proto_|.
   std::unique_ptr<PaymentRequestOptions> CreateOptionsFromProto() const;
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index c725031e..04225f2 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -186,6 +186,8 @@
   MOCK_METHOD1(OnWaitForWindowHeightChange,
                void(base::OnceCallback<void(const ClientStatus&)>& callback));
 
+  MOCK_METHOD0(RequireUI, void());
+
   const ClientSettings& GetSettings() override { return client_settings_; }
 
   ClientSettings client_settings_;
diff --git a/components/autofill_assistant/browser/actions/tell_action.cc b/components/autofill_assistant/browser/actions/tell_action.cc
index 4ed6eba8..c2f6260 100644
--- a/components/autofill_assistant/browser/actions/tell_action.cc
+++ b/components/autofill_assistant/browser/actions/tell_action.cc
@@ -21,6 +21,10 @@
 void TellAction::InternalProcessAction(ProcessActionCallback callback) {
   // tell.message in the proto is localized.
   delegate_->SetStatusMessage(proto_.tell().message());
+
+  if (proto_.tell().needs_ui())
+    delegate_->RequireUI();
+
   UpdateProcessedAction(ACTION_APPLIED);
   std::move(callback).Run(std::move(processed_action_proto_));
 }
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 3577b638..a8fff0c 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -41,6 +41,49 @@
 // Parameter that allows setting the color of the overlay.
 static const char* const kOverlayColorParameterName = "OVERLAY_COLORS";
 
+// Returns true if the state requires a UI to be shown.
+//
+// Note that the UI might be shown in RUNNING state, even if it doesn't require
+// it.
+bool StateNeedsUI(AutofillAssistantState state) {
+  switch (state) {
+    case AutofillAssistantState::STARTING:
+    case AutofillAssistantState::PROMPT:
+    case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
+    case AutofillAssistantState::MODAL_DIALOG:
+      return true;
+
+    case AutofillAssistantState::INACTIVE:
+    case AutofillAssistantState::TRACKING:
+    case AutofillAssistantState::STOPPED:
+    case AutofillAssistantState::RUNNING:
+      return false;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// Returns true if reaching that state signals the end of a flow.
+bool StateEndsFlow(AutofillAssistantState state) {
+  switch (state) {
+    case AutofillAssistantState::TRACKING:
+    case AutofillAssistantState::STOPPED:
+      return true;
+
+    case AutofillAssistantState::INACTIVE:
+    case AutofillAssistantState::STARTING:
+    case AutofillAssistantState::PROMPT:
+    case AutofillAssistantState::RUNNING:
+    case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
+    case AutofillAssistantState::MODAL_DIALOG:
+      return false;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 }  // namespace
 
 Controller::Controller(content::WebContents* web_contents,
@@ -217,6 +260,14 @@
   return navigation_error_;
 }
 
+void Controller::RequireUI() {
+  if (needs_ui_)
+    return;
+
+  needs_ui_ = true;
+  client_->AttachUI();
+}
+
 void Controller::AddListener(ScriptExecutorDelegate::Listener* listener) {
   auto found = std::find(listeners_.begin(), listeners_.end(), listener);
   if (found == listeners_.end())
@@ -446,15 +497,17 @@
   DCHECK(state_ != AutofillAssistantState::STOPPED ||
          (state == AutofillAssistantState::TRACKING && tracking_));
 
-  bool old_needs_ui = NeedsUI();
   state_ = state;
 
   for (ControllerObserver& observer : observers_) {
     observer.OnStateChanged(state);
   }
 
-  if (!old_needs_ui && NeedsUI())
-    client_->AttachUI();
+  if (!needs_ui_ && StateNeedsUI(state)) {
+    RequireUI();
+  } else if (needs_ui_ && StateEndsFlow(state)) {
+    needs_ui_ = false;
+  }
 
   if (ShouldCheckScripts()) {
     GetOrCheckScripts();
@@ -538,7 +591,8 @@
     std::string script_path = autostart_timeout_script_path_;
     autostart_timeout_script_path_.clear();
     periodic_script_check_scheduled_ = false;
-    ExecuteScript(script_path, TriggerContext::CreateEmpty(), state_);
+    ExecuteScript(script_path, /* start_message= */ "", /* needs_ui= */ false,
+                  TriggerContext::CreateEmpty(), state_);
     return;
   }
 
@@ -622,11 +676,18 @@
 }
 
 void Controller::ExecuteScript(const std::string& script_path,
+                               const std::string& start_message,
+                               bool needs_ui,
                                std::unique_ptr<TriggerContext> context,
                                AutofillAssistantState end_state) {
   DCHECK(!script_tracker()->running());
 
+  if (!start_message.empty())
+    SetStatusMessage(start_message);
+
   EnterState(AutofillAssistantState::RUNNING);
+  if (needs_ui)
+    RequireUI();
 
   touchable_element_area()->Clear();
 
@@ -712,20 +773,30 @@
   if (!allow_autostart())
     return false;
 
-  int autostart_count = 0;
-  std::string autostart_path;
-  for (const auto& script : runnable_scripts) {
-    if (script.autostart) {
-      autostart_count++;
-      autostart_path = script.path;
+  int autostart_index = -1;
+  for (size_t i = 0; i < runnable_scripts.size(); i++) {
+    if (runnable_scripts[i].autostart) {
+      if (autostart_index != -1) {
+        // To many autostartable scripts.
+        return false;
+      }
+      autostart_index = i;
     }
   }
-  if (autostart_count == 1) {
-    ExecuteScript(autostart_path, TriggerContext::CreateEmpty(),
-                  AutofillAssistantState::PROMPT);
-    return true;
-  }
-  return false;
+
+  if (autostart_index == -1)
+    return false;
+
+  // Copying the strings is necessary, as ExecuteScript will invalidate
+  // runnable_scripts by calling ScriptTracker::ClearRunnableScripts.
+  //
+  // TODO(b/138367403): Cleanup this dangerous issue.
+  std::string path = runnable_scripts[autostart_index].path;
+  std::string start_message = runnable_scripts[autostart_index].start_message;
+  bool needs_ui = runnable_scripts[autostart_index].needs_ui;
+  ExecuteScript(path, start_message, needs_ui, TriggerContext::CreateEmpty(),
+                AutofillAssistantState::PROMPT);
+  return true;
 }
 
 void Controller::InitFromParameters() {
@@ -772,12 +843,6 @@
   }
 }
 
-bool Controller::NeedsUI() const {
-  return state_ != AutofillAssistantState::INACTIVE &&
-         state_ != AutofillAssistantState::TRACKING &&
-         state_ != AutofillAssistantState::STOPPED;
-}
-
 bool Controller::Start(const GURL& deeplink_url,
                        std::unique_ptr<TriggerContext> trigger_context) {
   if (state_ != AutofillAssistantState::INACTIVE &&
@@ -803,10 +868,10 @@
   return state_;
 }
 
-void Controller::OnScriptSelected(const std::string& script_path,
+void Controller::OnScriptSelected(const ScriptHandle& handle,
                                   std::unique_ptr<TriggerContext> context) {
-  DCHECK(!script_path.empty());
-  ExecuteScript(script_path, std::move(context),
+  ExecuteScript(handle.path, handle.start_message, handle.needs_ui,
+                std::move(context),
                 state_ == AutofillAssistantState::TRACKING
                     ? AutofillAssistantState::TRACKING
                     : AutofillAssistantState::PROMPT);
@@ -880,6 +945,15 @@
   std::move(callback).Run(index);
 }
 
+void Controller::OnTermsAndConditionsLinkClicked(int link) {
+  if (!payment_request_info_)
+    return;
+
+  auto callback = std::move(payment_request_options_->terms_link_callback);
+  SetPaymentRequestOptions(nullptr);
+  std::move(callback).Run(link);
+}
+
 void Controller::SetShippingAddress(
     std::unique_ptr<autofill::AutofillProfile> address) {
   if (!payment_request_info_)
@@ -948,6 +1022,7 @@
   // should update the action buttons only if there are use cases of PR +
   // suggestions.
   if (!payment_request_options_ || !payment_request_info_) {
+    SetUserActions(nullptr);
     return;
   }
 
@@ -964,8 +1039,9 @@
   bool payment_method_ok = !payment_request_options_->request_payment_method ||
                            payment_request_info_->card;
 
-  bool terms_ok = payment_request_info_->terms_and_conditions != NOT_SELECTED ||
-                  !payment_request_options_->request_terms_and_conditions;
+  bool terms_ok =
+      payment_request_info_->terms_and_conditions != NOT_SELECTED ||
+      payment_request_options_->accept_terms_and_conditions_text.empty();
 
   bool confirm_button_enabled =
       contact_info_ok && shipping_address_ok && payment_method_ok && terms_ok;
@@ -1014,6 +1090,7 @@
   if (state_ == AutofillAssistantState::STOPPED)
     return;
 
+  RequireUI();
   SetStatusMessage(error_message);
   EnterStoppedState();
 
@@ -1140,9 +1217,8 @@
     if (!user_action.has_triggers())
       continue;
 
-    user_action.SetCallback(base::BindOnce(&Controller::OnScriptSelected,
-                                           weak_ptr_factory_.GetWeakPtr(),
-                                           script.path));
+    user_action.SetCallback(base::BindOnce(
+        &Controller::OnScriptSelected, weak_ptr_factory_.GetWeakPtr(), script));
     user_actions->emplace_back(std::move(user_action));
   }
 
@@ -1264,7 +1340,8 @@
 void Controller::SetPaymentRequestOptions(
     std::unique_ptr<PaymentRequestOptions> options) {
   DCHECK(!options ||
-         (options->confirm_callback && options->additional_actions_callback));
+         (options->confirm_callback && options->additional_actions_callback &&
+          options->terms_link_callback));
 
   if (payment_request_options_ == nullptr && options == nullptr)
     return;
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 4450a74..38325f9 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -85,7 +85,7 @@
              std::unique_ptr<TriggerContext> trigger_context);
 
   // Returns true if the controller is in a state where UI is necessary.
-  bool NeedsUI() const;
+  bool NeedsUI() const { return needs_ui_; }
 
   // Overrides ScriptExecutorDelegate:
   const ClientSettings& GetSettings() override;
@@ -116,6 +116,11 @@
       override;
   bool IsNavigatingToNewDocument() override;
   bool HasNavigationError() override;
+
+  // Show the UI if it's not already shown. This is only meaningful while in
+  // states where showing the UI is optional, such as RUNNING, in tracking mode.
+  void RequireUI() override;
+
   void AddListener(ScriptExecutorDelegate::Listener* listener) override;
   void RemoveListener(ScriptExecutorDelegate::Listener* listener) override;
 
@@ -150,6 +155,7 @@
   void SetCreditCard(std::unique_ptr<autofill::CreditCard> card) override;
   void SetTermsAndConditions(
       TermsAndConditionsState terms_and_conditions) override;
+  void OnTermsAndConditionsLinkClicked(int link) override;
   void GetTouchableArea(std::vector<RectF>* area) const override;
   void GetRestrictedArea(std::vector<RectF>* area) const override;
   void GetVisualViewport(RectF* visual_viewport) const override;
@@ -189,6 +195,8 @@
   // Execute |script_path| and, if execution succeeds, enter |end_state| and
   // call |on_success|.
   void ExecuteScript(const std::string& script_path,
+                     const std::string& start_message,
+                     bool needs_ui,
                      std::unique_ptr<TriggerContext> context,
                      AutofillAssistantState end_state);
   void OnScriptExecuted(const std::string& script_path,
@@ -213,7 +221,7 @@
   void InitFromParameters();
 
   // Called when a script is selected.
-  void OnScriptSelected(const std::string& script_path,
+  void OnScriptSelected(const ScriptHandle& handle,
                         std::unique_ptr<TriggerContext> context);
 
   void UpdatePaymentRequestActions();
@@ -364,6 +372,9 @@
   // This is set by Track().
   bool tracking_ = false;
 
+  // Whether the controller is in a state in which a UI should be shown.
+  bool needs_ui_ = false;
+
   // True once the controller has run the first set of scripts and have either
   // declared it invalid - and entered stopped state - or have processed its
   // result - and updated the state and set of available actions.
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 920e4a9..fbedd27b 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -462,6 +462,26 @@
   EXPECT_EQ("script3 prompt", controller_->GetStatusMessage());
 }
 
+TEST_F(ControllerTest, ScriptStartMessage) {
+  SupportsScriptResponseProto script_response;
+  auto* script = AddRunnableScript(&script_response, "script");
+  script->mutable_presentation()->set_start_message("Starting Script...");
+  SetNextScriptResponse(script_response);
+
+  ActionsResponseProto script_actions;
+  script_actions.add_actions()->mutable_tell()->set_message("Script running.");
+  SetupActionsForScript("script", script_actions);
+
+  Start("http://a.example.com/path");
+
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer_, OnStatusMessageChanged("Starting Script..."));
+    EXPECT_CALL(mock_observer_, OnStatusMessageChanged("Script running."));
+  }
+  EXPECT_TRUE(controller_->PerformUserAction(0));
+}
+
 TEST_F(ControllerTest, Stop) {
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "stop");
@@ -1019,6 +1039,100 @@
   SimulateNavigateToUrl(GURL("http://c.example.com/"));
 }
 
+TEST_F(ControllerTest, TrackScriptWithNoUI) {
+  // The UI is never shown during this test.
+  EXPECT_CALL(fake_client_, AttachUI()).Times(0);
+
+  SupportsScriptResponseProto script_response;
+  auto* script = AddRunnableScript(&script_response, "runnable");
+  script->mutable_presentation()->set_needs_ui(false);
+  SetupScripts(script_response);
+
+  // Script does nothing
+  ActionsResponseProto runnable_script;
+  auto* hidden_tell = runnable_script.add_actions()->mutable_tell();
+  hidden_tell->set_message("optional message");
+  hidden_tell->set_needs_ui(false);
+  runnable_script.add_actions()->mutable_stop();
+  SetupActionsForScript("runnable", runnable_script);
+
+  // Start tracking at example.com, with one script matching
+  SetLastCommittedUrl(GURL("http://example.com/"));
+
+  controller_->Track(TriggerContext::CreateEmpty(), base::DoNothing());
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
+
+  EXPECT_TRUE(controller_->PerformUserAction(0));
+  EXPECT_EQ(AutofillAssistantState::TRACKING, controller_->GetState());
+
+  // Check the full history of state transitions.
+  EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::TRACKING,
+                                   AutofillAssistantState::RUNNING,
+                                   AutofillAssistantState::TRACKING));
+}
+
+TEST_F(ControllerTest, TrackScriptShowUIOnTell) {
+  SupportsScriptResponseProto script_response;
+  auto* script = AddRunnableScript(&script_response, "runnable");
+  script->mutable_presentation()->set_needs_ui(false);
+  SetupScripts(script_response);
+
+  ActionsResponseProto runnable_script;
+  runnable_script.add_actions()->mutable_tell()->set_message("error");
+  SetupActionsForScript("runnable", runnable_script);
+
+  // Start tracking at example.com, with one script matching
+  SetLastCommittedUrl(GURL("http://example.com/"));
+
+  controller_->Track(TriggerContext::CreateEmpty(), base::DoNothing());
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
+
+  EXPECT_FALSE(controller_->NeedsUI());
+  EXPECT_CALL(fake_client_, AttachUI());
+  EXPECT_TRUE(controller_->PerformUserAction(0));
+  EXPECT_EQ(AutofillAssistantState::TRACKING, controller_->GetState());
+
+  // As the controller is back in tracking mode; A UI is not needed anymore.
+  EXPECT_FALSE(controller_->NeedsUI());
+
+  // Check the full history of state transitions.
+  EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::TRACKING,
+                                   AutofillAssistantState::RUNNING,
+                                   AutofillAssistantState::TRACKING));
+}
+
+TEST_F(ControllerTest, TrackScriptShowUIOnError) {
+  SupportsScriptResponseProto script_response;
+  auto* script = AddRunnableScript(&script_response, "runnable");
+  script->mutable_presentation()->set_needs_ui(false);
+  SetupScripts(script_response);
+
+  // Running the script fails, due to a backend issue. The error message should
+  // be shown.
+  EXPECT_CALL(*mock_service_, OnGetActions(_, _, _, _, _, _))
+      .WillOnce(RunOnceCallback<5>(false, ""));
+
+  // Start tracking at example.com, with one script matching
+  SetLastCommittedUrl(GURL("http://example.com/"));
+
+  controller_->Track(TriggerContext::CreateEmpty(), base::DoNothing());
+  ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
+
+  EXPECT_FALSE(controller_->NeedsUI());
+  EXPECT_CALL(fake_client_, AttachUI());
+  EXPECT_TRUE(controller_->PerformUserAction(0));
+  EXPECT_EQ(AutofillAssistantState::TRACKING, controller_->GetState());
+
+  // As the controller is back in tracking mode; A UI is not needed anymore.
+  EXPECT_FALSE(controller_->NeedsUI());
+
+  // Check the full history of state transitions.
+  EXPECT_THAT(states_, ElementsAre(AutofillAssistantState::TRACKING,
+                                   AutofillAssistantState::RUNNING,
+                                   AutofillAssistantState::STOPPED,
+                                   AutofillAssistantState::TRACKING));
+}
+
 TEST_F(ControllerTest, TrackContinuesAfterScriptError) {
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "runnable");
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 16615caa..dd6874d 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -124,6 +124,10 @@
   return navigating_to_new_document_;
 }
 
+void FakeScriptExecutorDelegate::RequireUI() {
+  require_ui_ = true;
+}
+
 void FakeScriptExecutorDelegate::AddListener(Listener* listener) {
   listeners_.insert(listener);
 }
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index 989897e..2d1e66ef 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -58,6 +58,7 @@
       override;
   bool HasNavigationError() override;
   bool IsNavigatingToNewDocument() override;
+  void RequireUI() override;
   void AddListener(Listener* listener) override;
   void RemoveListener(Listener* listener) override;
 
@@ -96,6 +97,8 @@
 
   bool HasListeners() { return !listeners_.empty(); }
 
+  bool IsUIRequired() { return require_ui_; }
+
  private:
   ClientSettings client_settings_;
   GURL current_url_;
@@ -115,6 +118,7 @@
   bool resize_viewport_ = false;
   ConfigureBottomSheetProto::PeekMode peek_mode_ =
       ConfigureBottomSheetProto::HANDLE;
+  bool require_ui_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeScriptExecutorDelegate);
 };
diff --git a/components/autofill_assistant/browser/payment_request.h b/components/autofill_assistant/browser/payment_request.h
index 2e1c2ac..c2b3c151 100644
--- a/components/autofill_assistant/browser/payment_request.h
+++ b/components/autofill_assistant/browser/payment_request.h
@@ -54,7 +54,11 @@
   bool request_payer_phone = false;
   bool request_shipping = false;
   bool request_payment_method = false;
-  bool request_terms_and_conditions = true;
+
+  // If empty, terms and conditions should not be shown.
+  std::string accept_terms_and_conditions_text;
+  bool show_terms_as_checkbox = false;
+
   std::vector<std::string> supported_basic_card_networks;
   std::string default_email;
   UserActionProto confirm_action;
@@ -64,6 +68,7 @@
   base::OnceCallback<void(std::unique_ptr<PaymentInformation>)>
       confirm_callback;
   base::OnceCallback<void(int)> additional_actions_callback;
+  base::OnceCallback<void(int)> terms_link_callback;
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 2223653..2cff627 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -100,6 +100,8 @@
     script->handle.chip = Chip(presentation.chip());
   }
   script->handle.direct_action = DirectAction(presentation.direct_action());
+  script->handle.start_message = presentation.start_message();
+  script->handle.needs_ui = presentation.needs_ui();
   scripts->emplace_back(std::move(script));
 }
 
diff --git a/components/autofill_assistant/browser/script.cc b/components/autofill_assistant/browser/script.cc
index 1efb8fe..4a233eb0 100644
--- a/components/autofill_assistant/browser/script.cc
+++ b/components/autofill_assistant/browser/script.cc
@@ -6,13 +6,13 @@
 
 namespace autofill_assistant {
 
-ScriptHandle::ScriptHandle() : autostart(false) {}
+ScriptHandle::ScriptHandle() {}
 
 ScriptHandle::ScriptHandle(const ScriptHandle& orig) = default;
 
 ScriptHandle::~ScriptHandle() = default;
 
-Script::Script() : priority(0) {}
+Script::Script() {}
 
 Script::~Script() = default;
 
diff --git a/components/autofill_assistant/browser/script.h b/components/autofill_assistant/browser/script.h
index 78b3345..f5878a6 100644
--- a/components/autofill_assistant/browser/script.h
+++ b/components/autofill_assistant/browser/script.h
@@ -25,14 +25,16 @@
   DirectAction direct_action;
   std::string path;
   std::string initial_prompt;
+  std::string start_message;
+  bool needs_ui = false;
 
   // When set to true this script can be run in 'autostart mode'. Script won't
   // be shown.
-  bool autostart;
+  bool autostart = false;
 
   // If set, the script might be run during WaitForDom actions with
   // allow_interrupt=true.
-  bool interrupt;
+  bool interrupt = false;
 };
 
 // Script represents a sequence of actions.
@@ -45,7 +47,7 @@
   // Display priority of the script. Lowest number has highest priority, which
   // means a script with priority 0 should be displayed before a script with
   // priority 1.
-  int priority;
+  int priority = 0;
 
   std::unique_ptr<ScriptPrecondition> precondition;
 };
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 83a4ecf..276b967 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -216,6 +216,9 @@
       base::BindOnce(&ScriptExecutor::OnAdditionalActionTriggered,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(options->additional_actions_callback));
+  options->terms_link_callback = base::BindOnce(
+      &ScriptExecutor::OnTermsAndConditionsLinkClicked,
+      weak_ptr_factory_.GetWeakPtr(), std::move(options->terms_link_callback));
   delegate_->SetPaymentRequestOptions(std::move(options));
   delegate_->EnterState(AutofillAssistantState::PROMPT);
 }
@@ -234,6 +237,13 @@
   std::move(callback).Run(index);
 }
 
+void ScriptExecutor::OnTermsAndConditionsLinkClicked(
+    base::OnceCallback<void(int)> callback,
+    int link) {
+  delegate_->EnterState(AutofillAssistantState::RUNNING);
+  std::move(callback).Run(link);
+}
+
 void ScriptExecutor::GetFullCard(GetFullCardCallback callback) {
   DCHECK(GetClientMemory()->selected_card());
 
@@ -514,6 +524,10 @@
   return delegate_->SetForm(std::move(form), std::move(callback));
 }
 
+void ScriptExecutor::RequireUI() {
+  delegate_->RequireUI();
+}
+
 void ScriptExecutor::OnGetActions(bool result, const std::string& response) {
   bool success = result && ProcessNextActionResponse(response);
   DVLOG(2) << __func__ << " result=" << result;
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 706eee7..d732ae4b 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -193,6 +193,7 @@
   bool SetForm(std::unique_ptr<FormProto> form,
                base::RepeatingCallback<void(const FormProto::Result*)> callback)
       override;
+  void RequireUI() override;
 
  private:
   // Helper for WaitForElementVisible that keeps track of the state required to
@@ -328,6 +329,8 @@
       std::unique_ptr<PaymentInformation> result);
   void OnAdditionalActionTriggered(base::OnceCallback<void(int)> callback,
                                    int index);
+  void OnTermsAndConditionsLinkClicked(base::OnceCallback<void(int)> callback,
+                                       int link);
   void OnGetFullCard(GetFullCardCallback callback,
                      std::unique_ptr<autofill::CreditCard> card,
                      const base::string16& cvc);
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index f2eadd6..333678c 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -105,6 +105,11 @@
   // Changes to this value is reported to Listener::OnNavigationStateChanged()
   virtual bool HasNavigationError() = 0;
 
+  // Force showing the UI, if necessary. This is useful when executing a direct
+  // action which realizes it needs to interact with the user. The UI stays up
+  // until the end of the flow.
+  virtual void RequireUI() = 0;
+
   // Register a listener that can be told about changes. Duplicate calls are
   // ignored.
   virtual void AddListener(Listener* listener) = 0;
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 2a9a70b..ded2d9b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -146,6 +146,14 @@
     // When set to true this script will be run from WaitForDom actions with
     // allow_interrupt=true.
     optional bool interrupt = 9;
+
+    // Message to show once the script has been started. This is shown while
+    // loading the actions.
+    optional string start_message = 10;
+
+    // Show the UI if it's not shown yet. Setting this to false is useful for
+    // scripts started by direct actions.
+    optional bool needs_ui = 11 [default = true];
   }
   optional PresentationProto presentation = 2;
 }
@@ -414,6 +422,9 @@
   // |GetPaymentInformationProto.additional_actions| that has index
   // |additional_action_index|.
   optional int32 additional_action_index = 4;
+  // If set, this means that the user clicked on one of the terms and conditions
+  // links.
+  optional int32 terms_link = 5;
 }
 
 message ProcessedActionProto {
@@ -719,6 +730,10 @@
 // Contain a localized text message from the server.
 message TellProto {
   optional string message = 1;
+
+  // Show the UI if it's not shown yet, such as when a script has been started
+  // by a direct action.
+  optional bool needs_ui = 2 [default = true];
 }
 
 // Contain all arguments to focus on an element.
@@ -1034,6 +1049,17 @@
   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];
+  // Whether the terms and conditions should be displayed as a single checkbox
+  // with |accept_terms_and_conditions_text| as text. If false, the accept terms
+  // will be displayed as a radio button next to an additional "Read and agree
+  // later on domain.com" choice.
+  optional bool show_terms_as_checkbox = 12;
+  // The text for the terms and conditions "I accept..." choice. The text is
+  // formatted such that '<b>text</b>' will be bold and '<link0>clickable
+  // link</link0>', '<link1>other link</link1>', etc will be clickable links
+  // that will finish this action and return the clicked link in the action
+  // result.
+  optional string accept_terms_and_conditions_text = 13;
   // Optionally allows confiriming through the given direct actions.
   optional DirectActionProto confirm_direct_action = 10;
   // Additional actions available to the user. This can be used for instance to
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index cb4ad765..3788ad1 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -118,6 +118,9 @@
   virtual void SetTermsAndConditions(
       TermsAndConditionsState terms_and_conditions) = 0;
 
+  // Called when the user clicks a link on the terms & conditions message.
+  virtual void OnTermsAndConditionsLinkClicked(int link) = 0;
+
   // Adds the rectangles that correspond to the current touchable area to the
   // given vector.
   //
diff --git a/components/autofill_assistant_strings.grdp b/components/autofill_assistant_strings.grdp
index f18b0a7..3c096bd 100644
--- a/components/autofill_assistant_strings.grdp
+++ b/components/autofill_assistant_strings.grdp
@@ -27,5 +27,8 @@
     <message name="IDS_AUTOFILL_ASSISTANT_STOPPED" desc="Text label that is shown when stopping the Autofill Assistant.">
       Google Assistant in Chrome stopping
     </message>
+    <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_ACCEPT" desc="Message that indicates that the user agrees to the terms and conditions of a 3rd party's domain, e.g., 'odeon.co.uk'.">
+      I agree to the terms &amp; conditions, privacy policy, and right of withdrawal of <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>
+    </message>
   </if>
 </grit-part>
diff --git a/components/captive_portal/BUILD.gn b/components/captive_portal/BUILD.gn
index 49ebbfb..93a263cb 100644
--- a/components/captive_portal/BUILD.gn
+++ b/components/captive_portal/BUILD.gn
@@ -21,6 +21,10 @@
     "//services/network/public/cpp",
     "//url",
   ]
+
+  if (is_chromeos) {
+    deps += [ "//chromeos/network" ]
+  }
 }
 
 static_library("test_support") {
diff --git a/components/captive_portal/DEPS b/components/captive_portal/DEPS
index a1d15fd..7b5f1e0 100644
--- a/components/captive_portal/DEPS
+++ b/components/captive_portal/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chromeos/network",
   "+services/network/public/cpp",
   "+services/network/public/mojom",
   "+services/network/test",
diff --git a/components/captive_portal/captive_portal_detector.cc b/components/captive_portal/captive_portal_detector.cc
index e81b285f..f92dda9 100644
--- a/components/captive_portal/captive_portal_detector.cc
+++ b/components/captive_portal/captive_portal_detector.cc
@@ -4,14 +4,37 @@
 
 #include "components/captive_portal/captive_portal_detector.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task_runner_util.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 #include "net/url_request/url_request_status.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#endif
+
+namespace {
+#if defined(OS_CHROMEOS)
+GURL GetProbeUrl(const GURL& default_url) {
+  DCHECK_EQ(chromeos::NetworkHandler::Get()->task_runner(),
+            base::ThreadTaskRunnerHandle::Get().get());
+  const chromeos::NetworkState* network = chromeos::NetworkHandler::Get()
+                                              ->network_state_handler()
+                                              ->DefaultNetwork();
+  return network && !network->probe_url().is_empty() ? network->probe_url()
+                                                     : default_url;
+}
+#endif
+}  // namespace
+
 namespace captive_portal {
 
 const char CaptivePortalDetector::kDefaultURL[] =
@@ -19,7 +42,7 @@
 
 CaptivePortalDetector::CaptivePortalDetector(
     network::mojom::URLLoaderFactory* loader_factory)
-    : loader_factory_(loader_factory) {}
+    : loader_factory_(loader_factory), weak_factory_(this) {}
 
 CaptivePortalDetector::~CaptivePortalDetector() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -35,8 +58,25 @@
 
   detection_callback_ = std::move(detection_callback);
 
+#if defined(OS_CHROMEOS)
+  if (chromeos::NetworkHandler::IsInitialized()) {
+    base::PostTaskAndReplyWithResult(
+        chromeos::NetworkHandler::Get()->task_runner(), FROM_HERE,
+        base::BindOnce(&GetProbeUrl, url),
+        base::BindOnce(&CaptivePortalDetector::StartProbe,
+                       weak_factory_.GetWeakPtr(), traffic_annotation));
+    return;
+  }
+#endif
+  StartProbe(traffic_annotation, url);
+}
+
+void CaptivePortalDetector::StartProbe(
+    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+    const GURL& url) {
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = url;
+  probe_url_ = url;
 
   // Can't safely use net::LOAD_DISABLE_CERT_NETWORK_FETCHES here,
   // since then the connection may be reused without checking the cert.
diff --git a/components/captive_portal/captive_portal_detector.h b/components/captive_portal/captive_portal_detector.h
index 069f26d0..5642663 100644
--- a/components/captive_portal/captive_portal_detector.h
+++ b/components/captive_portal/captive_portal_detector.h
@@ -6,11 +6,13 @@
 #define COMPONENTS_CAPTIVE_PORTAL_CAPTIVE_PORTAL_DETECTOR_H_
 
 #include <memory>
+#include <string>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "components/captive_portal/captive_portal_export.h"
@@ -76,6 +78,10 @@
                                           net::HttpResponseHeaders* headers,
                                           Results* results) const;
 
+  // Starts portal detection probe after GetProbeUrl finishes running.
+  void StartProbe(const net::NetworkTrafficAnnotationTag& traffic_annotation,
+                  const GURL& url);
+
   // Returns the current time. Used only when determining time until a
   // Retry-After date.
   base::Time GetCurrentTime() const;
@@ -101,8 +107,13 @@
   // Test time used by unit tests.
   base::Time time_for_testing_;
 
+  // Probe URL accessed by tests.
+  GURL probe_url_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
+  base::WeakPtrFactory<CaptivePortalDetector> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(CaptivePortalDetector);
 };
 
diff --git a/components/captive_portal/captive_portal_testing_utils.h b/components/captive_portal/captive_portal_testing_utils.h
index 559e36d2..3559be8b 100644
--- a/components/captive_portal/captive_portal_testing_utils.h
+++ b/components/captive_portal/captive_portal_testing_utils.h
@@ -43,6 +43,8 @@
 
   CaptivePortalDetector* detector() { return detector_; }
 
+  const GURL& get_probe_url() { return detector_->probe_url_; }
+
  protected:
   CaptivePortalDetector* detector_;
   network::TestURLLoaderFactory test_loader_factory_;
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index d9c9d40..419b726 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -10,6 +10,8 @@
 import("//build/util/lastchange.gni")
 import("//build/util/process_version.gni")
 import("//build/util/version.gni")
+import("//components/cronet/native/include/headers.gni")
+import("//components/grpc_support/include/headers.gni")
 import("//testing/test.gni")
 import("//third_party/netty4/netty4.gni")
 import("//third_party/protobuf/proto_library.gni")
@@ -1306,6 +1308,14 @@
     ]
   }
 
+  copy("cronet_package_copy_native_headers") {
+    sources = cronet_native_public_headers + grpc_public_headers
+
+    outputs = [
+      "$_package_dir/include/{{source_file_part}}",
+    ]
+  }
+
   copy("cronet_package_copy_native_lib") {
     sources = [
       "$root_out_dir/" + _cronet_shared_lib_file_name,
@@ -1490,6 +1500,7 @@
       deps = [
         ":api_static_checks",
         ":cronet_package_copy",
+        ":cronet_package_copy_native_headers",
         ":cronet_package_copy_native_lib",
         ":cronet_package_copy_native_lib_unstripped",
         ":cronet_package_copy_resources",
diff --git a/components/feed/core/feed_logging_metrics.cc b/components/feed/core/feed_logging_metrics.cc
index e487ff43..dfac2ea 100644
--- a/components/feed/core/feed_logging_metrics.cc
+++ b/components/feed/core/feed_logging_metrics.cc
@@ -688,9 +688,10 @@
 
 void FeedLoggingMetrics::OnInternalError(int internal_error) {
   // TODO(https://crbug.com/935602): The max value here is fragile, figure out
-  // some way to test the @IntDef size.
+  // some way to test the @IntDef size. For now the count needs to be kept in
+  // sync with InternalFeedError.java and enums.xml.
   UMA_HISTOGRAM_ENUMERATION("ContentSuggestions.Feed.InternalError",
-                            internal_error, 13);
+                            internal_error, 18);
 }
 
 void FeedLoggingMetrics::OnTokenCompleted(bool was_synthetic,
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h
index af33a35..9abeea2 100644
--- a/components/gcm_driver/gcm_driver.h
+++ b/components/gcm_driver/gcm_driver.h
@@ -268,7 +268,7 @@
   // The InstanceIDHandler provides an implementation for the InstanceID system.
   virtual InstanceIDHandler* GetInstanceIDHandlerInternal() = 0;
   // Allows the InstanceID system to integrate with GCM encryption storage.
-  GCMEncryptionProvider* GetEncryptionProviderInternal();
+  virtual GCMEncryptionProvider* GetEncryptionProviderInternal();
 
   // Adds or removes a custom client requested heartbeat interval. If multiple
   // components set that setting, the lowest setting will be used. If the
diff --git a/components/gcm_driver/instance_id/instance_id.h b/components/gcm_driver/instance_id/instance_id.h
index 398fab33..dfe35b404 100644
--- a/components/gcm_driver/instance_id/instance_id.h
+++ b/components/gcm_driver/instance_id/instance_id.h
@@ -116,8 +116,8 @@
   // created.
   // |authorized_entity|: the authorized entity passed when obtaining the token.
   // |callback|: to be called once the asynchronous operation is done.
-  void GetEncryptionInfo(const std::string& authorized_entity,
-                         GetEncryptionInfoCallback callback);
+  virtual void GetEncryptionInfo(const std::string& authorized_entity,
+                                 GetEncryptionInfoCallback callback);
 
   // Revokes a granted token.
   // |authorized_entity|: the authorized entity passed when obtaining the token.
diff --git a/components/image_fetcher/core/BUILD.gn b/components/image_fetcher/core/BUILD.gn
index c34dbca..1e8c44aa 100644
--- a/components/image_fetcher/core/BUILD.gn
+++ b/components/image_fetcher/core/BUILD.gn
@@ -27,9 +27,9 @@
   ]
   deps = [
     ":metrics",
-    "cache",
   ]
   public_deps = [
+    "cache",
     "//base",
     "//components/keyed_service/core",
     "//net",
diff --git a/components/invalidation/impl/invalidation_switches.cc b/components/invalidation/impl/invalidation_switches.cc
index b70acc4..4da0bbb 100644
--- a/components/invalidation/impl/invalidation_switches.cc
+++ b/components/invalidation/impl/invalidation_switches.cc
@@ -20,7 +20,7 @@
     "sync-allow-insecure-xmpp-connection";
 
 const base::Feature kFCMInvalidations = {"FCMInvalidations",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kFCMInvalidationsConservativeEnabling = {
     "FCMInvalidationsConservativeEnabling", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/journey/BUILD.gn b/components/journey/BUILD.gn
deleted file mode 100644
index acd967d..0000000
--- a/components/journey/BUILD.gn
+++ /dev/null
@@ -1,40 +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.
-
-static_library("journey_info_fetcher") {
-  sources = [
-    "journey_info_fetcher.cc",
-    "journey_info_fetcher.h",
-    "journey_info_json_request.cc",
-    "journey_info_json_request.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/journey/proto",
-    "//components/signin/public/identity_manager",
-    "//components/variations/net:net",
-    "//net",
-    "//services/data_decoder/public/cpp",
-    "//services/network/public/cpp",
-    "//url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "journey_info_fetcher_unittest.cc",
-  ]
-
-  deps = [
-    ":journey_info_fetcher",
-    "//base/test:test_support",
-    "//components/signin/public/identity_manager:test_support",
-    "//services/data_decoder/public/cpp:test_support",
-    "//services/network:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/journey/DEPS b/components/journey/DEPS
deleted file mode 100644
index 8aa14497..0000000
--- a/components/journey/DEPS
+++ /dev/null
@@ -1,10 +0,0 @@
-include_rules = [
-  "+base",
-  "+components/signin/public",
-  "+components/variations",
-  "+net",
-  "+services/data_decoder/public/cpp",
-  "+services/network/public/cpp",
-  "+services/network/test",
-  "+url",
-]
diff --git a/components/journey/OWNERS b/components/journey/OWNERS
deleted file mode 100644
index bb69daa..0000000
--- a/components/journey/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-acolwell@chromium.org
-wychen@chromium.org
-yusufo@chromium.org
diff --git a/components/journey/README.md b/components/journey/README.md
deleted file mode 100644
index a6dcdc2..0000000
--- a/components/journey/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Overview
-This component deals with multiple tabs or pages navigation that starts with an
-explicit user action, and has similar context. The component contains tools to
-process these navigations and present them in a way that provides users a
-smoother UI flow within their tabs.
-
-# Understanding Terms
-This component introduces several terms, include:
-* Pageload: A load of a page by a given user at a moment in time, and they are
-derived from the user's Chrome History data.
-* User Journey: A set of connected pageloads that are determined based on an
-explicit user action, such as omnibox search, and the semantic relationship
-with others.
-
-# Provisions
-This component provides a `JourneyInfoFetcher`, an authenticated fetcher that
-fetches a list of pageload information in a json format from the user journey
-endpoint server. This fetcher can be used by any client that needs access to 
-these information from the server.
diff --git a/components/journey/journey_info_fetcher.cc b/components/journey/journey_info_fetcher.cc
deleted file mode 100644
index c1d6fcd..0000000
--- a/components/journey/journey_info_fetcher.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/journey/journey_info_fetcher.h"
-
-#include "base/bind.h"
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-#include "components/journey/journey_info_json_request.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
-#include "services/data_decoder/public/cpp/safe_json_parser.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace journey {
-
-namespace {
-const char kChromeSyncScope[] = "https://www.googleapis.com/auth/chromememex";
-}  // namespace
-
-JourneyInfoFetcher::JourneyInfoFetcher(
-    signin::IdentityManager* identity_manager,
-    const scoped_refptr<network::SharedURLLoaderFactory>& loader_factory)
-    : identity_manager_(identity_manager), loader_factory_(loader_factory) {}
-
-JourneyInfoFetcher::~JourneyInfoFetcher() = default;
-
-void JourneyInfoFetcher::FetchJourneyInfo(
-    std::vector<int64_t> timestamps,
-    FetchResponseAvailableCallback callback) {
-  if (!identity_manager_->HasPrimaryAccount()) {
-    FetchFinished(std::move(callback), /*result=*/base::nullopt,
-                  "Primary Account is not Available. Sign in is required");
-    return;
-  }
-  pending_requests_.emplace(std::move(timestamps), std::move(callback));
-  RequestOAuthTokenService();
-}
-
-void JourneyInfoFetcher::RequestOAuthTokenService() {
-  if (token_fetcher_)
-    return;
-
-  OAuth2AccessTokenManager::ScopeSet scopes{kChromeSyncScope};
-  token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
-      "journey_info", identity_manager_, scopes,
-      base::BindOnce(&JourneyInfoFetcher::AccessTokenFetchFinished,
-                     base::Unretained(this)),
-      signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
-}
-
-void JourneyInfoFetcher::AccessTokenFetchFinished(
-    GoogleServiceAuthError error,
-    signin::AccessTokenInfo access_token_info) {
-  DCHECK(token_fetcher_);
-  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
-      token_fetcher_deleter(std::move(token_fetcher_));
-
-  if (error.state() != GoogleServiceAuthError::NONE) {
-    AccessTokenError(error);
-    return;
-  }
-
-  DCHECK(!access_token_info.token.empty());
-
-  while (!pending_requests_.empty()) {
-    std::pair<std::vector<int64_t>, FetchResponseAvailableCallback>
-        timestamp_and_callback = std::move(pending_requests_.front());
-    pending_requests_.pop();
-    StartRequest(timestamp_and_callback.first,
-                 std::move(timestamp_and_callback.second),
-                 access_token_info.token);
-  }
-}
-
-void JourneyInfoFetcher::AccessTokenError(const GoogleServiceAuthError& error) {
-  DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
-  DLOG(WARNING) << "JourneyInfoFetcher::AccessTokenError "
-                   "Unable to get token: "
-                << error.ToString();
-
-  while (!pending_requests_.empty()) {
-    std::pair<std::vector<int64_t>, FetchResponseAvailableCallback>
-        timestamp_and_callback = std::move(pending_requests_.front());
-    pending_requests_.pop();
-
-    FetchFinished(std::move(timestamp_and_callback.second),
-                  /*result=*/base::nullopt, error.ToString());
-  }
-}
-
-void JourneyInfoFetcher::StartRequest(const std::vector<int64_t>& timestamps,
-                                      FetchResponseAvailableCallback callback,
-                                      const std::string& oauth_access_token) {
-  JourneyInfoJsonRequest::Builder builder;
-  builder.SetTimestamps(timestamps)
-      .SetAuthentication(
-          base::StringPrintf("bearer %s", oauth_access_token.c_str()))
-      .SetParseJsonCallback(base::BindRepeating(
-          &data_decoder::SafeJsonParser::Parse,
-          /*connector=*/nullptr));  // This is an Android-only component,
-                                    // connector is unused on Android.
-  auto json_request = builder.Build();
-  JourneyInfoJsonRequest* raw_request = json_request.get();
-  raw_request->Start(
-      base::BindOnce(&JourneyInfoFetcher::JsonRequestDone,
-                     base::Unretained(this), std::move(json_request),
-                     std::move(callback)),
-      loader_factory_);
-}
-
-const std::string& JourneyInfoFetcher::GetLastJsonForDebugging() const {
-  return last_fetch_json_;
-}
-
-void JourneyInfoFetcher::JsonRequestDone(
-    std::unique_ptr<JourneyInfoJsonRequest> request,
-    FetchResponseAvailableCallback callback,
-    base::Optional<base::Value> result,
-    const std::string& error_detail) {
-  DCHECK(request);
-
-  last_fetch_json_ = request->GetResponseString();
-  FetchFinished(std::move(callback), std::move(result), error_detail);
-}
-
-void JourneyInfoFetcher::FetchFinished(FetchResponseAvailableCallback callback,
-                                       base::Optional<base::Value> result,
-                                       const std::string& error_detail) {
-  DCHECK((result && !result->is_none()) || error_detail != "");
-
-  // Returned |result| can be empty while |error_detail| is "".
-  std::move(callback).Run(std::move(result), error_detail);
-}
-
-}  // namespace journey
diff --git a/components/journey/journey_info_fetcher.h b/components/journey/journey_info_fetcher.h
deleted file mode 100644
index bb1cf0bd..0000000
--- a/components/journey/journey_info_fetcher.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_JOURNEY_JOURNEY_INFO_FETCHER_H_
-#define COMPONENTS_JOURNEY_JOURNEY_INFO_FETCHER_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/containers/queue.h"
-#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
-
-namespace base {
-class Value;
-}  // namespace base
-
-namespace signin {
-class IdentityManager;
-}  // namespace signin
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace journey {
-
-class JourneyInfoJsonRequest;
-
-using FetchResponseAvailableCallback =
-    base::OnceCallback<void(base::Optional<base::Value>, const std::string&)>;
-
-// This class is used to fetch SwitcherJourney information from the server.
-class JourneyInfoFetcher {
- public:
-  JourneyInfoFetcher(
-      signin::IdentityManager* identity_manager,
-      const scoped_refptr<network::SharedURLLoaderFactory>& loader_factory);
-
-  ~JourneyInfoFetcher();
-
-  // This method fetches journey information based on |timestamps|,
-  // and calls back to |callback| when complete.
-  // TODO(meiliang): Add a parameter, GURL url, as the fetching url instead of
-  // hard code the url.
-  void FetchJourneyInfo(std::vector<int64_t> timestamps,
-                        FetchResponseAvailableCallback callback);
-
-  // This method gets last json response as a string.
-  const std::string& GetLastJsonForDebugging() const;
-
- private:
-  void RequestOAuthTokenService();
-
-  void StartRequest(const std::vector<int64_t>& timestamps,
-                    FetchResponseAvailableCallback callback,
-                    const std::string& oauth_access_token);
-
-  void AccessTokenFetchFinished(GoogleServiceAuthError error,
-                                signin::AccessTokenInfo access_token_info);
-
-  void AccessTokenError(const GoogleServiceAuthError& error);
-
-  void FetchFinished(FetchResponseAvailableCallback callback,
-                     base::Optional<base::Value> result,
-                     const std::string& error_detail);
-
-  void JsonRequestDone(std::unique_ptr<JourneyInfoJsonRequest> request,
-                       FetchResponseAvailableCallback callback,
-                       base::Optional<base::Value> result,
-                       const std::string& error_detail);
-
-  signin::IdentityManager* identity_manager_;
-
-  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> token_fetcher_;
-
-  const scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
-
-  std::string last_fetch_json_;
-
-  // This queues stores requests that wait for an access token.
-  base::queue<std::pair<std::vector<int64_t>, FetchResponseAvailableCallback>>
-      pending_requests_;
-
-  DISALLOW_COPY_AND_ASSIGN(JourneyInfoFetcher);
-};
-
-}  // namespace journey
-
-#endif  // COMPONENTS_JOURNEY_JOURNEY_INFO_FETCHER_H_
diff --git a/components/journey/journey_info_fetcher_unittest.cc b/components/journey/journey_info_fetcher_unittest.cc
deleted file mode 100644
index 789a619..0000000
--- a/components/journey/journey_info_fetcher_unittest.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/journey/journey_info_fetcher.h"
-
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/mock_callback.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/signin/public/identity_manager/identity_test_environment.h"
-#include "net/http/http_util.h"
-#include "services/data_decoder/public/cpp/testing_json_parser.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_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-
-namespace journey {
-const char kEmail[] = "foo_email";
-const char kJourneyServer[] =
-    "https://chrome-memex-dev.appspot.com/api/journey_from_pageload";
-const char kEmptyErrorString[] = "";
-const std::vector<int64_t> kTimestamps = {1532563271195406};
-
-using MockFetchResponseAvailableCallback =
-    base::MockCallback<FetchResponseAvailableCallback>;
-
-class JourneyInfoFetcherTest : public testing::Test {
- protected:
-  JourneyInfoFetcherTest() {}
-
-  ~JourneyInfoFetcherTest() override {}
-
-  void SetUp() override {
-    scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-    journey_info_fetcher_ = std::make_unique<JourneyInfoFetcher>(
-        identity_test_env_.identity_manager(), test_shared_loader_factory);
-
-    SignIn();
-  }
-
-  void TearDown() override {}
-
-  void SignIn() {
-    identity_test_env_.MakePrimaryAccountAvailable(kEmail);
-    identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
-  }
-
-  void SignOut() { identity_test_env_.ClearPrimaryAccount(); }
-
-  std::string GenerateJsonResponse(const std::vector<int64_t>& timestamps) {
-    int timestamps_size = timestamps.size();
-    std::string response_string = R"(
-      [
-        {
-          "status": "STATUS_OK",
-          "default_autotabs": {
-            "pageloads": [
-            {
-              "title": {
-                "weight": 1.0,
-                "title": "foo"
-              },
-              "url": "https://foo.com/",
-              "image": {
-                "snippet": "PRS_REPRESENTATIVE_IMAGE",
-                "confidence": 1.0,
-                "thumbnail_url": "https://foo-png"
-              },
-              "timestamp_us": "1532563271195406",
-              "is_pruned": false
-            }
-            ],
-            "selection_type": "SELECTION_TYPE_LEAVES_AND_TAB_AND_TASK"
-          },
-          "journey_id": "3021296114337328326",
-          "source_page_timestamp_usec": [
-    )";
-
-    for (int i = 0; i < timestamps_size; i++) {
-      response_string += "\"" + base::NumberToString(timestamps[i]) + "\"";
-      if (i < timestamps_size - 1)
-        response_string += ", ";
-    }
-
-    response_string += R"(
-          ]
-        }
-      ]
-    )";
-
-    return response_string;
-  }
-
-  void SetFakeResponse(const GURL& request_url,
-                       const std::string& response_data,
-                       net::HttpStatusCode response_code,
-                       net::Error error) {
-    network::ResourceResponseHead head;
-    std::string headers(base::StringPrintf(
-        "HTTP/1.1 %d %s\nContent-type: application/json\n\n",
-        static_cast<int>(response_code), GetHttpReasonPhrase(response_code)));
-    head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
-        net::HttpUtil::AssembleRawHeaders(headers));
-    head.mime_type = "application/json";
-    network::URLLoaderCompletionStatus status(error);
-    status.decoded_body_length = response_data.size();
-    test_url_loader_factory_.AddResponse(request_url, head, response_data,
-                                         status);
-  }
-
-  void SendAndAwaitResponse(std::vector<int64_t> timestamps) {
-    journey_info_fetcher()->FetchJourneyInfo(
-        timestamps, journey_info_available_callback().Get());
-    base::RunLoop().RunUntilIdle();
-  }
-
-  MockFetchResponseAvailableCallback& journey_info_available_callback() {
-    return mock_callback_;
-  }
-
-  JourneyInfoFetcher* journey_info_fetcher() {
-    return journey_info_fetcher_.get();
-  }
-
-  network::TestURLLoaderFactory* test_url_loader_factory() {
-    return &test_url_loader_factory_;
-  }
-
-  signin::IdentityTestEnvironment& identity_test_env() {
-    return identity_test_env_;
-  }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
-  signin::IdentityTestEnvironment identity_test_env_;
-  MockFetchResponseAvailableCallback mock_callback_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  std::unique_ptr<JourneyInfoFetcher> journey_info_fetcher_;
-
-  DISALLOW_COPY_AND_ASSIGN(JourneyInfoFetcherTest);
-};
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfo) {
-  std::string json_response_string = GenerateJsonResponse(kTimestamps);
-  SetFakeResponse(GURL(kJourneyServer), json_response_string, net::HTTP_OK,
-                  net::OK);
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Ne(base::nullopt), kEmptyErrorString));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_THAT(journey_info_fetcher()->GetLastJsonForDebugging(),
-              testing::Eq(json_response_string));
-}
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoOAuthError) {
-  identity_test_env().SetAutomaticIssueOfAccessTokens(false);
-
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Eq(base::nullopt), testing::Ne(kEmptyErrorString)));
-
-  journey_info_fetcher()->FetchJourneyInfo(
-      kTimestamps, journey_info_available_callback().Get());
-
-  identity_test_env().WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
-      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
-
-  base::RunLoop().RunUntilIdle();
-}
-
-#if !defined(OS_CHROMEOS)
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoUserNotSignedIn) {
-  SignOut();
-
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Eq(base::nullopt), testing::Ne(kEmptyErrorString)));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_EQ(journey_info_fetcher()->GetLastJsonForDebugging(), "");
-}
-#endif
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoWithNonParsableResponse) {
-  std::string json_response_string = "[";
-  SetFakeResponse(GURL(kJourneyServer), json_response_string, net::HTTP_OK,
-                  net::OK);
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Eq(base::nullopt), testing::Ne(kEmptyErrorString)));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_THAT(journey_info_fetcher()->GetLastJsonForDebugging(),
-              testing::Eq(json_response_string));
-}
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoWithBadJSONResponse) {
-  std::string json_response_string = "[]";
-  SetFakeResponse(GURL(kJourneyServer), json_response_string, net::HTTP_OK,
-                  net::OK);
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Ne(base::nullopt), kEmptyErrorString));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_THAT(journey_info_fetcher()->GetLastJsonForDebugging(),
-              testing::Eq(json_response_string));
-}
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoNetworkError) {
-  std::string json_response_string = "[]";
-  SetFakeResponse(GURL(kJourneyServer), json_response_string, net::HTTP_OK,
-                  net::ERR_FAILED);
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Eq(base::nullopt), testing::Ne(kEmptyErrorString)));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_EQ(journey_info_fetcher()->GetLastJsonForDebugging(), "");
-}
-
-TEST_F(JourneyInfoFetcherTest, FetchJourneyInfoHttpError) {
-  std::string json_response_string = "[]";
-  SetFakeResponse(GURL(kJourneyServer), json_response_string,
-                  net::HTTP_BAD_REQUEST, net::OK);
-  EXPECT_CALL(journey_info_available_callback(),
-              Run(testing::Eq(base::nullopt), testing::Ne(kEmptyErrorString)));
-
-  SendAndAwaitResponse(kTimestamps);
-
-  EXPECT_EQ(journey_info_fetcher()->GetLastJsonForDebugging(), "");
-}
-
-}  // namespace journey
diff --git a/components/journey/journey_info_json_request.cc b/components/journey/journey_info_json_request.cc
deleted file mode 100644
index b8cba83..0000000
--- a/components/journey/journey_info_json_request.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/journey/journey_info_json_request.h"
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-#include "components/journey/proto/batch_get_switcher_journey_from_pageload_request.pb.h"
-#include "components/variations/net/variations_http_headers.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_request_headers.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/cpp/simple_url_loader.h"
-
-namespace journey {
-
-namespace {
-
-const int k5xxRetries = 2;
-
-std::string GetSerializedJourneyRequest(
-    const std::vector<int64_t>& timestamps) {
-  BatchGetSwitcherJourneyFromPageloadRequest request;
-
-  for (const auto timestamp : timestamps) {
-    request.add_page_timestamp_usec(timestamp);
-  }
-
-  return request.SerializeAsString();
-}
-
-}  // namespace
-
-JourneyInfoJsonRequest::JourneyInfoJsonRequest(
-    const ParseJSONCallback& callback)
-    : parse_json_callback_(callback), weak_ptr_factory_(this) {}
-
-JourneyInfoJsonRequest::~JourneyInfoJsonRequest() {}
-
-void JourneyInfoJsonRequest::Start(
-    CompletedCallback callback,
-    const scoped_refptr<network::SharedURLLoaderFactory>& loader_factory) {
-  completed_callback_ = std::move(callback);
-  last_response_string_.clear();
-
-  simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      loader_factory.get(),
-      base::BindOnce(&JourneyInfoJsonRequest::OnSimpleURLLoaderComplete,
-                     base::Unretained(this)));
-}
-
-const std::string& JourneyInfoJsonRequest::GetResponseString() const {
-  return last_response_string_;
-}
-
-void JourneyInfoJsonRequest::OnSimpleURLLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  DCHECK(simple_url_loader_);
-
-  int response_code = -1;
-  if (simple_url_loader_->ResponseInfo() &&
-      simple_url_loader_->ResponseInfo()->headers) {
-    response_code =
-        simple_url_loader_->ResponseInfo()->headers->response_code();
-  }
-
-  int net_error = simple_url_loader_->NetError();
-  simple_url_loader_.reset();
-
-  if (net_error != net::OK) {
-    std::move(completed_callback_)
-        .Run(base::nullopt,
-             base::StringPrintf("Network error code: %d", net_error));
-  } else if (response_code / 100 != 2) {
-    std::move(completed_callback_)
-        .Run(base::nullopt,
-             base::StringPrintf("Http response error code: %d", response_code));
-  } else {
-    last_response_string_ = std::move(*response_body);
-    parse_json_callback_.Run(
-        last_response_string_,
-        base::BindRepeating(&JourneyInfoJsonRequest::OnJsonParsed,
-                            weak_ptr_factory_.GetWeakPtr()),
-        base::BindRepeating(&JourneyInfoJsonRequest::OnJsonError,
-                            weak_ptr_factory_.GetWeakPtr()));
-  }
-}
-
-void JourneyInfoJsonRequest::OnJsonParsed(base::Value result) {
-  std::move(completed_callback_).Run(std::move(result), std::string());
-}
-
-void JourneyInfoJsonRequest::OnJsonError(const std::string& error) {
-  DLOG(WARNING) << "Received invalid JSON (" << error
-                << "): " << last_response_string_;
-  std::move(completed_callback_).Run(base::nullopt, error);
-}
-
-JourneyInfoJsonRequest::Builder::Builder()
-    : url_(GURL(
-          "https://chrome-memex-dev.appspot.com/api/journey_from_pageload")) {}
-
-JourneyInfoJsonRequest::Builder::~Builder() = default;
-
-std::unique_ptr<JourneyInfoJsonRequest> JourneyInfoJsonRequest::Builder::Build()
-    const {
-  auto request = std::make_unique<JourneyInfoJsonRequest>(parse_json_callback_);
-  request->simple_url_loader_ = BuildSimpleURLLoader();
-
-  return request;
-}
-
-JourneyInfoJsonRequest::Builder&
-JourneyInfoJsonRequest::Builder::SetAuthentication(
-    const std::string& auth_header) {
-  DVLOG(0) << "Authorization header " << auth_header;
-  auth_header_ = auth_header;
-  return *this;
-}
-
-JourneyInfoJsonRequest::Builder& JourneyInfoJsonRequest::Builder::SetTimestamps(
-    const std::vector<int64_t>& timestamps) {
-  body_ = GetSerializedJourneyRequest(timestamps);
-  return *this;
-}
-
-JourneyInfoJsonRequest::Builder&
-JourneyInfoJsonRequest::Builder::SetParseJsonCallback(
-    ParseJSONCallback callback) {
-  parse_json_callback_ = std::move(callback);
-  return *this;
-}
-
-net::HttpRequestHeaders
-JourneyInfoJsonRequest::Builder::BuildSimpleURLLoaderHeaders() const {
-  net::HttpRequestHeaders headers;
-  headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
-  if (!auth_header_.empty()) {
-    headers.SetHeader("Authorization", auth_header_);
-  }
-  return headers;
-}
-
-std::unique_ptr<network::SimpleURLLoader>
-JourneyInfoJsonRequest::Builder::BuildSimpleURLLoader() const {
-  // TODO(meiliang): update the policy section with correct setting and
-  // chrome_policy
-  net::NetworkTrafficAnnotationTag traffic_annotation = NO_TRAFFIC_ANNOTATION_YET;
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = GURL(url_);
-  resource_request->allow_credentials = false;
-  resource_request->headers = BuildSimpleURLLoaderHeaders();
-  variations::AppendVariationsHeader(url_, variations::InIncognito::kNo,
-                                     variations::SignedIn::kNo,
-                                     resource_request.get());
-  resource_request->method = "POST";
-
-  auto simple_loader = network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-  simple_loader->SetAllowHttpErrorResults(true);
-  simple_loader->AttachStringForUpload(body_,
-                                       "application/json; charset=UTF-8");
-  simple_loader->SetRetryOptions(
-      k5xxRetries, network::SimpleURLLoader::RetryMode::RETRY_ON_5XX);
-
-  return simple_loader;
-}
-
-}  // namespace journey
diff --git a/components/journey/journey_info_json_request.h b/components/journey/journey_info_json_request.h
deleted file mode 100644
index b1a643a..0000000
--- a/components/journey/journey_info_json_request.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_JOURNEY_JOURNEY_INFO_JSON_REQUEST_H_
-#define COMPONENTS_JOURNEY_JOURNEY_INFO_JSON_REQUEST_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "url/gurl.h"
-
-namespace base {
-class Value;
-}  // namespace base
-
-namespace net {
-class HttpRequestHeaders;
-}
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace journey {
-
-// This class represents a request of journey info. It encapsulates the
-// request-response lifecycle. It is also responsible for building and
-// serializing the request body protos.
-class JourneyInfoJsonRequest {
-  // Callbacks for JSON parsing to allow injecting platform-dependent code.
-  using SuccessCallback = base::OnceCallback<void(base::Value result)>;
-  using ErrorCallback = base::OnceCallback<void(const std::string& error)>;
-  using ParseJSONCallback =
-      base::RepeatingCallback<void(const std::string& raw_json_string,
-                                   SuccessCallback success_callback,
-                                   ErrorCallback error_callback)>;
-  using CompletedCallback =
-      base::OnceCallback<void(base::Optional<base::Value> result,
-                              const std::string& error_detail)>;
-
- public:
-  // This class is used to build authenticated and non-authenticated
-  // JourneyInfoJsonRequest.
-  class Builder {
-   public:
-    Builder();
-    ~Builder();
-
-    // This method is used to builds a Request object that contains all
-    // data to fetch new snippets.
-    std::unique_ptr<JourneyInfoJsonRequest> Build() const;
-
-    Builder& SetAuthentication(const std::string& auth_header);
-    Builder& SetParseJsonCallback(ParseJSONCallback callback);
-    Builder& SetTimestamps(const std::vector<int64_t>& timestamps);
-
-   private:
-    std::unique_ptr<network::SimpleURLLoader> BuildSimpleURLLoader() const;
-    net::HttpRequestHeaders BuildSimpleURLLoaderHeaders() const;
-
-    // The url for which we're fetching journey info.
-    GURL url_;
-    std::string auth_header_;
-    std::string body_;
-    ParseJSONCallback parse_json_callback_;
-
-    DISALLOW_COPY_AND_ASSIGN(Builder);
-  };
-
-  explicit JourneyInfoJsonRequest(const ParseJSONCallback& callback);
-  ~JourneyInfoJsonRequest();
-
-  // This method is used to start fetching journey info using |loader_factory|
-  // to create a URLLoader, and call |callback| when finished.
-  void Start(
-      CompletedCallback callback,
-      const scoped_refptr<network::SharedURLLoaderFactory>& loader_factory);
-
-  // Get the last response as a string
-  const std::string& GetResponseString() const;
-
- private:
-  void OnSimpleURLLoaderComplete(std::unique_ptr<std::string> response_body);
-  void OnJsonParsed(base::Value result);
-  void OnJsonError(const std::string& error);
-
-  // This callback is called to parse a json string. It contains callbacks for
-  // error and success cases.
-  ParseJSONCallback parse_json_callback_;
-
-  // The loader for downloading the snippets. Only non-null if a fetch is
-  // currently ongoing.
-  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
-
-  std::string last_response_string_;
-
-  // The callback to call when journey info are available.
-  CompletedCallback completed_callback_;
-
-  base::WeakPtrFactory<JourneyInfoJsonRequest> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(JourneyInfoJsonRequest);
-};
-
-}  // namespace journey
-
-#endif  // COMPONENTS_JOURNEY_JOURNEY_INFO_JSON_REQUEST_H_
diff --git a/components/journey/proto/BUILD.gn b/components/journey/proto/BUILD.gn
deleted file mode 100644
index 0556af1..0000000
--- a/components/journey/proto/BUILD.gn
+++ /dev/null
@@ -1,11 +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.
-
-import("//third_party/protobuf/proto_library.gni")
-
-proto_library("proto") {
-  sources = [
-    "batch_get_switcher_journey_from_pageload_request.proto",
-  ]
-}
diff --git a/components/journey/proto/batch_get_switcher_journey_from_pageload_request.proto b/components/journey/proto/batch_get_switcher_journey_from_pageload_request.proto
deleted file mode 100644
index 4f56565..0000000
--- a/components/journey/proto/batch_get_switcher_journey_from_pageload_request.proto
+++ /dev/null
@@ -1,91 +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.
-
-// Protocol buffer definition for a delta file.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-package journey;
-
-message BatchGetSwitcherJourneyFromPageloadRequest {
-  // Next ID to use: 5
-
-  // The timestamps of the pageloads to request journeys from. This will be
-  // ignored if task_id is present.
-  // TODO(ghyde): Deprecate this once client moves over.
-  repeated int64 page_timestamp_usec = 1;
-
-  // Override journey creation parameters.
-  optional JourneyCreationOverrides journey_overrides = 2;
-
-  // The maximum number of pageloads allowed per autotabs.
-  optional int64 max_pageloads = 3 [default = 50];
-
-  // The task ids frokm the client to request journeys from.
-  repeated int64 task_id = 4;
-}
-
-message JourneyCreationOverrides {
-  optional JourneyAlgorithmParameters journey_algorithm_parameters = 1;
-}
-
-message JourneyAlgorithmParameters {
-  // Next ID to use: 11
-
-  // Similarity threshold in [0,1]. Clusters with similarity greater
-  // then this threshold and which were done in short timespans will be
-  // merged. If set to 1, non temporal merging is performed.
-  optional float temporal_merge_threshold = 1 [default = 1];
-
-  // Similarity threshold in [0,1]. Threshold above which journeys are
-  // considered to be related to each other.
-  optional float semantically_related_threshold = 2 [default = 0.1];
-
-  // Similarity threshold in [0, 1]. Threshold above which journeys appear in
-  // searches for a given query.
-  optional float search_threshold = 3 [default = 0.1];
-
-  // If true, dedupe pageloads in journeys by URL. remove_duplicate_titles
-  // and remove_duplicates_between_tasks will only be used if this value is
-  // true.
-  optional bool remove_duplicate_urls = 6 [default = true];
-
-  // If true, remove URLs as duplicates if they are from the same domain and
-  // have the same title. Otherwise, only remove duplicate URLs that have
-  // identical URLs.
-  // This value is only used if remove_duplicate_urls is also true.
-  optional bool remove_duplicate_titles = 4 [default = true];
-
-  // If true, remove duplicates across the whole journey. Otherwise,
-  // only remove duplicates within each task.
-  // Note: This value is only used if remove_duplicate_urls is also true.
-  optional bool remove_duplicates_between_tasks = 7 [default = false];
-
-  // Type of labels to use for subtitles.
-  enum SubtitleTermsType {
-    TYPE_UNKNOWN = 0;
-
-    // Use knowledge entities as subtitle terms.
-    TYPE_KE = 1;
-
-    // Use petacat as subtitle terms.
-    TYPE_PETACAT = 2;
-  }
-  optional SubtitleTermsType subtitle_type = 5 [default = TYPE_KE];
-
-  // If set to true, bookmarks will be fetched and pageloads will be annotated
-  // with them.
-  optional bool get_bookmarks = 8 [default = false];
-
-  // The number of days between tasks in the same cluster required in order
-  // to break into a new journey.
-  optional int32 journey_break_days = 9 [default = 7];
-
-  // The max number of pageloads to be added to a journey.
-  // NOTE: We will actually add tasks as long as we are under this limit, so
-  // it is possible that the last task pushes us over.
-  optional int32 max_pages_in_journey = 10 [default = 500];
-}
\ No newline at end of file
diff --git a/components/media_message_center/media_notification_item.h b/components/media_message_center/media_notification_item.h
index 1911694a..08f817e6 100644
--- a/components/media_message_center/media_notification_item.h
+++ b/components/media_message_center/media_notification_item.h
@@ -62,6 +62,8 @@
       override;
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   // media_session::mojom::MediaControllerImageObserver:
   void MediaControllerImageChanged(
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index d2653b7..965ddde 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -213,9 +213,16 @@
 
   if (OmniboxFieldTrial::IsGroupSuggestionsBySearchVsUrlFeatureEnabled() &&
       matches_.size() > 2) {
-    // "Bunch" first by search type, then all others.
-    std::stable_sort(std::next(matches_.begin()), matches_.end(),
-                     CompareBySearchVsUrl());
+    // Skip over default match.
+    auto next = std::next(matches_.begin());
+    // If it has submatches, skip them too.
+    if (matches_.front().subrelevance != 0) {
+      while (next != matches_.end() &&
+             AutocompleteMatch::IsSameFamily(matches_.front().subrelevance,
+                                             next->subrelevance))
+        next = std::next(next);
+    }
+    GroupSuggestionsBySearchVsURL(next, matches_.end());
   }
 
   // There is no default match for chromeOS launcher zero prefix query
@@ -770,3 +777,63 @@
                      }),
       matches_.end());
 }
+
+// static
+void AutocompleteResult::GroupSuggestionsBySearchVsURL(iterator begin,
+                                                       iterator end) {
+  // The following routine implements a semi-stateful stable partition. It
+  // moves all search-type matches towards the beginning of the range, (and
+  // non-search-type towards the end) but has to keep any submatches of a
+  // match together with that match.
+  //
+  // It does this by iterating through the list, top to bottom, so that it
+  // can associate submatches with their match simply by position. It finds
+  // a section of matches to be moved down, then finds the following section
+  // of matches to be moved up:
+  //
+  // Search-type              (a search-type in the correct position, skipped)
+  // Navigation-type          <- begin
+  // submatch Search-type
+  // Navigation-type
+  // Search-type              <- mid
+  // submatch Navigation-type
+  // ...                      <- temp_end
+  //
+  // We then call rotate() with those 3 iterators, then repeat the search,
+  // starting with where the navigation-types landed.
+
+  // Find the first element to be moved downwards. Skip matches in correct
+  // position.
+  while (begin != end &&
+         (AutocompleteMatch::IsSearchType(begin->type) ||
+         // Any submatch present would belong to the previous search-type
+         // match.
+         begin->IsSubMatch())) {
+    begin = std::next(begin);
+  }
+  while (begin != end) {
+    // Find the last element (technically, the end of the range) to be moved
+    // downwards.
+    auto mid = begin;
+    while (mid != end &&
+           (!AutocompleteMatch::IsSearchType(mid->type) || mid->IsSubMatch())) {
+      mid = std::next(mid);
+    }
+    // Find the last element (technically, the end of the range) to be moved
+    // upwards.
+    auto temp_end = mid;
+    while (temp_end != end &&
+           (AutocompleteMatch::IsSearchType(temp_end->type) ||
+            temp_end->IsSubMatch())) {
+      temp_end = std::next(temp_end);
+    }
+    if (mid != end) {
+      std::rotate(begin, mid, temp_end);
+      // Advance |begin| iterator over the elements now in correct position.
+      // |begin += temp_end - mid;|
+      begin = std::next(begin, std::distance(mid, temp_end));
+    } else {
+      break;
+    }
+  }
+}
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 69079ea..e83c103 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -145,6 +145,8 @@
   FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest, ConvertsOpenTabsCorrectly);
   FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest,
                            PedalSuggestionsRemainUnique);
+  FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest,
+                           TestGroupSuggestionsBySearchVsURL);
 
   typedef std::map<AutocompleteProvider*, ACMatches> ProviderToMatches;
 
@@ -203,6 +205,12 @@
       size_t max_url_count,
       const CompareWithDemoteByType<AutocompleteMatch>& comparing_object);
 
+  // This method implements a stateful stable partition. Matches which are
+  // search types, and their submatches regardless of type, are shifted
+  // earlier in the range, while non-search types and their submatches
+  // are shifted later.
+  static void GroupSuggestionsBySearchVsURL(iterator begin, iterator end);
+
   ACMatches matches_;
 
   const_iterator default_match_;
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 05dbd5f5..edc9f596 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -1480,3 +1480,43 @@
   EXPECT_EQ(result.match_at(4)->relevance, 500);
   EXPECT_EQ(result.match_at(4)->pedal, nullptr);
 }
+
+TEST_F(AutocompleteResultTest, TestGroupSuggestionsBySearchVsURL) {
+  ACMatches matches;
+  matches.resize(10);
+  // A search-type to stay.
+  matches[0].type = AutocompleteMatchType::SEARCH_SUGGEST;
+  // A non-search-type to move down.
+  matches[1].type = AutocompleteMatchType::HISTORY_URL;
+  // A search-type to move up.
+  matches[2].type = AutocompleteMatchType::SEARCH_SUGGEST;
+  // It's submatch to move up with it.
+  matches[3].type = AutocompleteMatchType::HISTORY_URL;
+  matches[3].subrelevance = 4 + 1;
+  // A non-search-type to move down.
+  matches[4].type = AutocompleteMatchType::HISTORY_URL;
+  // It's submatch to move down with it.
+  matches[5].type = AutocompleteMatchType::SEARCH_SUGGEST;
+  matches[5].subrelevance = 8 + 1;
+  // A search-type to move up.
+  matches[6].type = AutocompleteMatchType::SEARCH_SUGGEST;
+  // It's submatch to move up with it.
+  matches[7].type = AutocompleteMatchType::HISTORY_URL;
+  matches[7].subrelevance = 12 + 1;
+  // A non-search-type to "move down" (really, to stay).
+  matches[8].type = AutocompleteMatchType::HISTORY_URL;
+  // It's submatch to move down with it.
+  matches[9].type = AutocompleteMatchType::SEARCH_SUGGEST;
+  matches[9].subrelevance = 16 + 1;
+
+  AutocompleteResult::GroupSuggestionsBySearchVsURL(matches.begin(),
+                                                    matches.end());
+  for (size_t i = 0; i < 5; ++i) {
+    EXPECT_TRUE(AutocompleteMatch::IsSearchType(matches[i].type) ||
+                matches[i].IsSubMatch());
+  }
+  for (size_t i = 5; i < 10; ++i) {
+    EXPECT_TRUE(!AutocompleteMatch::IsSearchType(matches[i].type) ||
+                matches[i].IsSubMatch());
+  }
+}
diff --git a/components/omnibox/browser/match_compare.h b/components/omnibox/browser/match_compare.h
index 4842cfae..7cb6b5f8 100644
--- a/components/omnibox/browser/match_compare.h
+++ b/components/omnibox/browser/match_compare.h
@@ -71,15 +71,4 @@
   CompareWithDemoteByType<Match> demote_by_type_;
 };
 
-// Such a simple comparison should only be used by std::stable_sort().
-class CompareBySearchVsUrl {
- public:
-  bool operator()(const AutocompleteMatch& elem1,
-                  const AutocompleteMatch& elem2) {
-    // Put search matches first, followed by all other types.
-    return AutocompleteMatch::IsSearchType(elem1.type) &&
-           !AutocompleteMatch::IsSearchType(elem2.type);
-  }
-};
-
 #endif  // COMPONENTS_OMNIBOX_BROWSER_MATCH_COMPARE_H_
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 01c71de..11fb26e 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -546,11 +546,11 @@
   if (client()->IsOffTheRecord())
     return false;
 
-  // Check if ZeroSuggest is allowed in an empty state.
+  // When the omnibox is empty, only allow zero suggest for the ChromeOS
+  // Launcher and NTP.
   if (input_type == metrics::OmniboxInputType::EMPTY &&
       !(page_class == metrics::OmniboxEventProto::CHROMEOS_APP_LIST ||
-        (IsNTPPage(page_class) &&
-         base::FeatureList::IsEnabled(omnibox::kZeroSuggestionsOnNTP)))) {
+        IsNTPPage(page_class))) {
     return false;
   }
 
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 9a79a47..495e2f5 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -86,11 +86,6 @@
 #endif
 };
 
-// Feature used to enable enhanced presentation showing larger images.
-// This is currently only used on Android.
-const base::Feature kOmniboxNewAnswerLayout{"OmniboxNewAnswerLayout",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kOmniboxPreserveDefaultMatchScore{
     "OmniboxPreserveDefaultMatchScore", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index a63a17b..19edd216 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -20,7 +20,6 @@
 extern const base::Feature kOmniboxLocalEntitySuggestions;
 extern const base::Feature kOmniboxMaxURLMatches;
 extern const base::Feature kOmniboxRichEntitySuggestions;
-extern const base::Feature kOmniboxNewAnswerLayout;
 extern const base::Feature kOmniboxReverseAnswers;
 extern const base::Feature kOmniboxShortBookmarkSuggestions;
 extern const base::Feature kOmniboxTailSuggestions;
diff --git a/components/optimization_guide/hints_component_util.cc b/components/optimization_guide/hints_component_util.cc
index 646222cc..4e89c40 100644
--- a/components/optimization_guide/hints_component_util.cc
+++ b/components/optimization_guide/hints_component_util.cc
@@ -29,6 +29,9 @@
 
 }  // namespace
 
+const char kComponentHintsUpdatedResultHistogramString[] =
+    "OptimizationGuide.UpdateComponentHints.Result";
+
 void RecordProcessHintsComponentResult(ProcessHintsComponentResult result) {
   UMA_HISTOGRAM_ENUMERATION(kProcessHintsComponentResultHistogramString,
                             result);
diff --git a/components/optimization_guide/hints_component_util.h b/components/optimization_guide/hints_component_util.h
index 97c3b3a..620c936e 100644
--- a/components/optimization_guide/hints_component_util.h
+++ b/components/optimization_guide/hints_component_util.h
@@ -13,6 +13,10 @@
 
 struct HintsComponentInfo;
 
+// The local histogram used to record that the component hints are stored in
+// the cache and are ready for use.
+extern const char kComponentHintsUpdatedResultHistogramString[];
+
 // Enumerates the possible outcomes of processing the hints component.
 //
 // Used in UMA histograms, so the order of enumerators should not be changed.
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index 452d023..83c53df 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -109,9 +109,12 @@
   // True if field->form_control_type == "password".
   bool is_password = false;
 
-  // True if the server predicts this field not to be a password.
+  // True if the server predicts that this field is not a password field.
   bool server_hints_not_password = false;
 
+  // True if the server predicts that this field is not a username field.
+  bool server_hints_not_username = false;
+
   Interactability interactability = Interactability::kUnlikely;
 };
 
@@ -135,6 +138,11 @@
          field.autocomplete_flag == AutocompleteFlag::kCreditCard;
 }
 
+// Returns true if the |field| is suspected to be not the username field.
+bool IsNotUsernameField(const ProcessedField& field) {
+  return field.server_hints_not_username;
+}
+
 // Returns true iff |field_type| is one of password types.
 bool IsPasswordPrediction(const CredentialFieldType field_type) {
   switch (field_type) {
@@ -359,11 +367,14 @@
   // For the use of basic heuristics, also mark CVC fields and NOT_PASSWORD
   // fields as such.
   for (const PasswordFieldPrediction& prediction : predictions) {
+    ProcessedField* current_field = FindField(processed_fields, prediction);
+    if (!current_field)
+      continue;
     if (prediction.type == autofill::CREDIT_CARD_VERIFICATION_CODE ||
         prediction.type == autofill::NOT_PASSWORD) {
-      ProcessedField* processed_field = FindField(processed_fields, prediction);
-      if (processed_field)
-        processed_field->server_hints_not_password = true;
+      current_field->server_hints_not_password = true;
+    } else if (prediction.type == autofill::NOT_USERNAME) {
+      current_field->server_hints_not_username = true;
     }
   }
 }
@@ -388,7 +399,8 @@
     }
     switch (processed_field.autocomplete_flag) {
       case AutocompleteFlag::kUsername:
-        if (processed_field.is_password || result->username)
+        if (processed_field.is_password || result->username ||
+            processed_field.server_hints_not_username)
           continue;
         username_fields_found++;
         field_marked_as_username = processed_field.field;
@@ -605,6 +617,7 @@
 
   const FormFieldData* focusable_username = nullptr;
   const FormFieldData* username = nullptr;
+
   // Do reverse search to find the closest candidates preceding the password.
   for (auto it = std::make_reverse_iterator(first_relevant_password);
        it != processed_fields.rend(); ++it) {
@@ -616,6 +629,9 @@
       continue;
     if (!is_fallback && IsNotPasswordField(*it))
       continue;
+    if (!is_fallback && IsNotUsernameField(*it)) {
+      continue;
+    }
     if (!username)
       username = it->field;
     if (it->field->is_focusable) {
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 72ea19d6..059fbb4 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -418,13 +418,15 @@
 TEST(FormParserTest, NotPasswordForm) {
   CheckTestData({
       {
-          "No fields", {},
+          "No fields",
+          {},
       },
       {
           .description_for_logging = "No password fields",
           .fields =
               {
-                  {.form_control_type = "text"}, {.form_control_type = "text"},
+                  {.form_control_type = "text"},
+                  {.form_control_type = "text"},
               },
           .number_of_all_possible_passwords = 0,
           .number_of_all_possible_usernames = 0,
@@ -1097,7 +1099,6 @@
                   {.role_saving = ElementRole::CURRENT_PASSWORD,
                    .form_control_type = "password"},
                   {.role_filling = ElementRole::NEW_PASSWORD,
-
                    .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
                    .form_control_type = "password"},
                   {.role_filling = ElementRole::CONFIRMATION_PASSWORD,
@@ -1120,7 +1121,6 @@
                .form_control_type = "password"},
           },
       },
-
   });
 }
 
@@ -1520,6 +1520,62 @@
   });
 }
 
+// The parser should avoid identifying NOT_USERNAME fields as usernames.
+TEST(FormParserTest, NotUsernameField) {
+  CheckTestData({
+      {
+          "Server hints: NOT_USERNAME.",
+          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
+           {.role = ElementRole::NONE,
+            .form_control_type = "text",
+            .prediction = {.type = autofill::NOT_USERNAME}},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password",
+            .prediction = {.type = autofill::PASSWORD}}},
+          .fallback_only = false,
+      },
+      {
+          "Server hints: NOT_USERNAME on only username.",
+          {{.role = ElementRole::NONE,
+            .form_control_type = "text",
+            .prediction = {.type = autofill::NOT_USERNAME}},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+          .fallback_only = false,
+      },
+  });
+}
+
+// The parser should avoid identifying NOT_USERNAME fields as usernames despite
+// autocomplete attribute.
+TEST(FormParserTest, NotUsernameFieldDespiteAutocompelteAtrribute) {
+  CheckTestData({
+      {
+          "Server hints: NOT_USERNAME.",
+          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
+           {.form_control_type = "text",
+            .autocomplete_attribute = "username",
+            .prediction = {.type = autofill::NOT_USERNAME}},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password",
+            .prediction = {.type = autofill::PASSWORD}}},
+          .fallback_only = false,
+      },
+      {
+          "Server hints: NOT_USERNAME on only username.",
+          {
+              {.role = ElementRole::NONE,
+               .form_control_type = "text",
+               .autocomplete_attribute = "username",
+               .prediction = {.type = autofill::NOT_USERNAME}},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+          },
+          .fallback_only = false,
+      },
+  });
+}
+
 // The parser should avoid identifying NOT_PASSWORD fields as passwords.
 TEST(FormParserTest, NotPasswordFieldDespiteAutocompleteAttribute) {
   CheckTestData({
@@ -1968,8 +2024,7 @@
     // saving mode.
     SCOPED_TRACE(histogram_test_case.parsing_data.description_for_logging);
     tester.ExpectUniqueSample("PasswordManager.UsernameDetectionMethod",
-                              histogram_test_case.expected_method,
-                              2);
+                              histogram_test_case.expected_method, 2);
   }
 }
 
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 0735964..5e3dc0e 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -1459,6 +1459,12 @@
   return true;
 }
 
+bool LoginDatabase::IsEmpty() {
+  sql::Statement s(
+      db_.GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(*) FROM logins"));
+  return s.Step() && s.ColumnInt(0) == 0;
+}
+
 bool LoginDatabase::DeleteAndRecreateDatabaseFile() {
   DCHECK(db_.is_open());
   meta_table_.Reset();
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index 303210a..8934c46 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -152,6 +152,8 @@
   // whether further use of this login database will succeed is unspecified.
   bool DeleteAndRecreateDatabaseFile();
 
+  bool IsEmpty();
+
   // On MacOS, it deletes all logins from the database that cannot be decrypted
   // when encryption key from Keychain is available. If the Keychain is locked,
   // it does nothing and returns ENCRYPTION_UNAVAILABLE. If it's not running on
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index e150ca1..58721ec 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -283,6 +283,7 @@
   // Verify the database is empty.
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   EXPECT_EQ(0U, result.size());
+  EXPECT_TRUE(db().IsEmpty());
 
   EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
   EXPECT_EQ(0U, key_to_form_map.size());
@@ -299,6 +300,7 @@
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   ASSERT_EQ(1U, result.size());
   EXPECT_EQ(form, *result[0]);
+  EXPECT_FALSE(db().IsEmpty());
   result.clear();
 
   EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
@@ -391,6 +393,7 @@
   EXPECT_EQ(2, changes[0].primary_key());
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   EXPECT_EQ(0U, result.size());
+  EXPECT_TRUE(db().IsEmpty());
 }
 
 TEST_F(LoginDatabaseTest, AddLoginReturnsPrimaryKey) {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index a03cc3a..72e7f12 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -3349,7 +3349,7 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
-      'example_value': '*example.com,foobar.com,*baz',
+      'example_value': '*.example.com,example.com',
       'id': 29,
       'caption': '''Authentication server whitelist''',
       'tags': [],
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index 33b4c586..49a8a9ed 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -8,6 +8,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_macros_local.h"
 #include "base/rand_util.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
@@ -15,6 +16,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/optimization_guide/hint_cache_store.h"
 #include "components/optimization_guide/hints_component_info.h"
+#include "components/optimization_guide/hints_component_util.h"
 #include "components/optimization_guide/hints_fetcher.h"
 #include "components/optimization_guide/optimization_guide_features.h"
 #include "components/optimization_guide/optimization_guide_prefs.h"
@@ -328,6 +330,13 @@
     base::OnceClosure update_closure) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
   DCHECK(pref_service_);
+
+  // Record the result of updating the hints. This is used as a signal for the
+  // hints being fully processed in release tools and testing.
+  LOCAL_HISTOGRAM_BOOLEAN(
+      optimization_guide::kComponentHintsUpdatedResultHistogramString,
+      hints_ != nullptr);
+
   if (!update_closure.is_null())
     std::move(update_closure).Run();
 
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index c83f6fd4..6f7a58c 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -244,12 +244,21 @@
 
   void ProcessHints(const optimization_guide::proto::Configuration& config,
                     const std::string& version) {
+    base::HistogramTester histogram_tester;
+
     optimization_guide::HintsComponentInfo info(
         base::Version(version),
         temp_dir().Append(FILE_PATH_LITERAL("somefile.pb")));
     ASSERT_NO_FATAL_FAILURE(WriteConfigToFile(config, info.path));
+    base::RunLoop run_loop;
+    guide_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
     guide_->OnHintsComponentAvailable(info);
-    RunUntilIdle();
+    run_loop.Run();
+
+    // Check for histogram to ensure that we do not remove this histogram since
+    // it's relied on in release tools.
+    histogram_tester.ExpectTotalCount(
+        "OptimizationGuide.UpdateComponentHints.Result", 1);
   }
 
   void EnableDataSaver() {
diff --git a/components/safe_browsing/db/v4_database.cc b/components/safe_browsing/db/v4_database.cc
index cad85de..435b4b3 100644
--- a/components/safe_browsing/db/v4_database.cc
+++ b/components/safe_browsing/db/v4_database.cc
@@ -292,18 +292,8 @@
 
 bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
   const auto& store_pair = store_map_->find(identifier);
-  bool store_found = store_pair != store_map_->end();
-  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.V4Store.IsStoreAvailable.ValidStore",
-                        store_found);
-  if (!store_found) {
-    // Store not in our list
-    return false;
-  }
-  if (!store_pair->second->HasValidData()) {
-    // Store never properly populated
-    return false;
-  }
-  return true;
+  return (store_pair != store_map_->end()) &&
+         store_pair->second->HasValidData();
 }
 
 void V4Database::RecordFileSizeHistograms() {
diff --git a/components/signin/core/browser/android/BUILD.gn b/components/signin/core/browser/android/BUILD.gn
index 6365991..faf5325 100644
--- a/components/signin/core/browser/android/BUILD.gn
+++ b/components/signin/core/browser/android/BUILD.gn
@@ -39,8 +39,6 @@
     "java/src/org/chromium/components/signin/ChildAccountStatus.java",
     "java/src/org/chromium/components/signin/ChromeSigninController.java",
     "java/src/org/chromium/components/signin/ConsistencyCookieManager.java",
-    "java/src/org/chromium/components/signin/CoreAccountId.java",
-    "java/src/org/chromium/components/signin/CoreAccountInfo.java",
     "java/src/org/chromium/components/signin/GmsAvailabilityException.java",
     "java/src/org/chromium/components/signin/GmsJustUpdatedException.java",
     "java/src/org/chromium/components/signin/util/PatternMatcher.java",
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java
index 4a95792..a56464d 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java
@@ -4,24 +4,24 @@
 
 package org.chromium.components.signin;
 
+import com.google.android.gms.auth.GoogleAuthException;
+import com.google.android.gms.auth.GoogleAuthUtil;
 import com.google.android.gms.common.ConnectionResult;
 import com.google.android.gms.common.GoogleApiAvailability;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
  * Returns a stable id that can be used to identify a Google Account.  This
  * id does not change if the email address associated to the account changes,
  * nor does it change depending on whether the email has dots or varying
  * capitalization.
- *
- * TODO(https://crbug.com/831257): remove this class after all clients start using {@link
- * AccountManagerFacade} methods instead.
  */
 public class AccountIdProvider {
     private static AccountIdProvider sProvider;
@@ -40,16 +40,12 @@
      * @param accountName The email address of a Google account.
      */
     public String getAccountId(String accountName) {
-        List<CoreAccountInfo> accountInfos = AccountManagerFacade.get().tryGetAccounts();
-        if (accountInfos == null) {
+        try {
+            return GoogleAuthUtil.getAccountId(ContextUtils.getApplicationContext(), accountName);
+        } catch (IOException | GoogleAuthException ex) {
+            Log.e("cr.AccountIdProvider", "AccountIdProvider.getAccountId", ex);
             return null;
         }
-        for (CoreAccountInfo accountInfo : accountInfos) {
-            if (accountInfo.getName().equals(accountName)) {
-                return accountInfo.getId().getGaiaIdAsString();
-            }
-        }
-        return null;
     }
 
     /**
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
index 057d9ae0..a35a24a42 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
@@ -16,8 +16,6 @@
 
 import org.chromium.base.Callback;
 
-import java.util.List;
-
 /**
  * Abstraction of account management implementation.
  * Provides methods for getting accounts and managing auth tokens.
@@ -44,10 +42,10 @@
     void removeObserver(AccountsChangeObserver observer);
 
     /**
-     * Get information about all the accounts synchronously.
+     * Get all the accounts synchronously.
      */
     @WorkerThread
-    List<CoreAccountInfo> getAccountInfosSync() throws AccountManagerDelegateException;
+    Account[] getAccountsSync() throws AccountManagerDelegateException;
 
     /**
      * Get an auth token.
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
index 4c23d12..be552d5 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
@@ -33,6 +33,7 @@
 import org.chromium.components.signin.util.PatternMatcher;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -81,9 +82,9 @@
 
     // These two variables should be accessed from either UI thread or during initialization phase.
     private PatternMatcher[] mAccountRestrictionPatterns;
-    private AccountManagerResult<List<CoreAccountInfo>> mAllAccounts;
+    private AccountManagerResult<List<Account>> mAllAccounts;
 
-    private final AtomicReference<AccountManagerResult<List<CoreAccountInfo>>> mFilteredAccounts =
+    private final AtomicReference<AccountManagerResult<List<Account>>> mFilteredAccounts =
             new AtomicReference<>();
     private final CountDownLatch mPopulateAccountCacheLatch = new CountDownLatch(1);
     private final CachedMetrics.TimesHistogramSample mPopulateAccountCacheWaitingTimeHistogram =
@@ -227,14 +228,74 @@
     }
 
     /**
-     * Retrieves {@link CoreAccountInfo} for all Google accounts on the device.
+     * Retrieves a list of the Google account names on the device.
      *
      * @throws AccountManagerDelegateException if Google Play Services are out of date,
      *         Chrome lacks necessary permissions, etc.
      */
     @AnyThread
-    public List<CoreAccountInfo> getAccounts() throws AccountManagerDelegateException {
-        AccountManagerResult<List<CoreAccountInfo>> maybeAccounts = mFilteredAccounts.get();
+    public List<String> getGoogleAccountNames() throws AccountManagerDelegateException {
+        List<String> accountNames = new ArrayList<>();
+        for (Account account : getGoogleAccounts()) {
+            accountNames.add(account.name);
+        }
+        return accountNames;
+    }
+
+    /**
+     * Retrieves a list of the Google account names on the device.
+     * Returns an empty list if Google Play Services aren't available or out of date.
+     */
+    @AnyThread
+    public List<String> tryGetGoogleAccountNames() {
+        List<String> accountNames = new ArrayList<>();
+        List<Account> tryGetGoogleAccounts = tryGetGoogleAccounts();
+        for (int i = 0; i < tryGetGoogleAccounts.size(); i++) {
+            Account account = tryGetGoogleAccounts.get(i);
+            accountNames.add(account.name);
+        }
+        return accountNames;
+    }
+
+    /**
+     * Asynchronous version of {@link #tryGetGoogleAccountNames()}.
+     */
+    @MainThread
+    public void tryGetGoogleAccountNames(final Callback<List<String>> callback) {
+        runAfterCacheIsPopulated(() -> callback.onResult(tryGetGoogleAccountNames()));
+    }
+
+    /**
+     * Asynchronous version of {@link #tryGetGoogleAccountNames()}.
+     */
+    @MainThread
+    public void getGoogleAccountNames(
+            final Callback<AccountManagerResult<List<String>>> callback) {
+        runAfterCacheIsPopulated(() -> {
+            final AccountManagerResult<List<Account>> accounts = mFilteredAccounts.get();
+            final AccountManagerResult<List<String>> result;
+            if (accounts.hasValue()) {
+                List<String> accountNames = new ArrayList<>(accounts.getValue().size());
+                for (Account account : accounts.getValue()) {
+                    accountNames.add(account.name);
+                }
+                result = new AccountManagerResult<>(accountNames);
+            } else {
+                result = new AccountManagerResult<>(accounts.getException());
+            }
+            callback.onResult(result);
+        });
+    }
+
+    /**
+     * Retrieves all Google accounts on the device.
+     *
+     * @throws AccountManagerDelegateException if Google Play Services are out of date,
+     *         Chrome lacks necessary permissions, etc.
+     */
+    @AnyThread
+    public List<Account> getGoogleAccounts() throws AccountManagerDelegateException {
+        AccountManagerResult<List<Account>> maybeAccounts = mFilteredAccounts.get();
         if (maybeAccounts == null) {
             try {
                 // First call to update hasn't finished executing yet, should wait for it
@@ -253,148 +314,16 @@
     }
 
     /**
-     * Asynchronous version of {@link #getAccounts()}. The {@code callback} will be executed on the
-     * UI thread after the account cache is populated.
-     */
-    @MainThread
-    public void getAccounts(Callback<AccountManagerResult<List<CoreAccountInfo>>> callback) {
-        runAfterCacheIsPopulated(() -> callback.onResult(mFilteredAccounts.get()));
-    }
-
-    /**
-     * Retrieves {@link CoreAccountInfo} for all Google accounts on the device.
-     * Returns an empty array if an error occurs while getting account information list.
-     */
-    @AnyThread
-    public List<CoreAccountInfo> tryGetAccounts() {
-        try {
-            return getAccounts();
-        } catch (AccountManagerDelegateException e) {
-            return Collections.emptyList();
-        }
-    }
-
-    /**
-     * Asynchronous version of {@link #tryGetAccounts()}. The {@code callback} will be executed on
-     * the UI thread after the account cache is populated.
-     */
-    @MainThread
-    public void tryGetAccounts(final Callback<List<CoreAccountInfo>> callback) {
-        runAfterCacheIsPopulated(() -> callback.onResult(tryGetAccounts()));
-    }
-
-    /**
-     * Retrieves a list of the Google account names on the device.
-     * TODO(https://crbug.com/831257): Move all callers to getAccounts and remove this
-     * method.
-     *
-     * @throws AccountManagerDelegateException if Google Play Services are out of date,
-     *         Chrome lacks necessary permissions, etc.
-     */
-    @AnyThread
-    public List<String> getGoogleAccountNames() throws AccountManagerDelegateException {
-        List<String> accountNames = new ArrayList<>();
-        for (CoreAccountInfo account : getAccounts()) {
-            accountNames.add(account.getName());
-        }
-        return accountNames;
-    }
-
-    /**
-     * Asynchronous version of {@link #getGoogleAccountNames()}.
-     * TODO(https://crbug.com/831257): Move all callers to getAccounts and remove this
-     * method.
-     */
-    @MainThread
-    public void getGoogleAccountNames(
-            final Callback<AccountManagerResult<List<String>>> callback) {
-        runAfterCacheIsPopulated(() -> {
-            final AccountManagerResult<List<CoreAccountInfo>> accounts = mFilteredAccounts.get();
-            final AccountManagerResult<List<String>> result;
-            if (accounts.hasValue()) {
-                List<String> accountNames = new ArrayList<>(accounts.getValue().size());
-                for (CoreAccountInfo account : accounts.getValue()) {
-                    accountNames.add(account.getName());
-                }
-                result = new AccountManagerResult<>(accountNames);
-            } else {
-                result = new AccountManagerResult<>(accounts.getException());
-            }
-            callback.onResult(result);
-        });
-    }
-
-    /**
-     * Retrieves a list of the Google account names on the device.
-     * Returns an empty list if Google Play Services aren't available or out of date.
-     * TODO(https://crbug.com/831257): Move all callers to tryGetAccounts and remove this
-     * method.
-     */
-    @AnyThread
-    public List<String> tryGetGoogleAccountNames() {
-        try {
-            return getGoogleAccountNames();
-        } catch (AccountManagerDelegateException e) {
-            return Collections.emptyList();
-        }
-    }
-
-    /**
-     * Asynchronous version of {@link #tryGetGoogleAccountNames()}.
-     * TODO(https://crbug.com/831257): Move all callers to tryGetAccounts and remove this
-     * method.
-     */
-    @MainThread
-    public void tryGetGoogleAccountNames(final Callback<List<String>> callback) {
-        runAfterCacheIsPopulated(() -> callback.onResult(tryGetGoogleAccountNames()));
-    }
-
-    /**
-     * Retrieves all Google accounts on the device.
-     * TODO(https://crbug.com/831257): Move all callers to getAccounts and remove this
-     * method.
-     *
-     * @throws AccountManagerDelegateException if Google Play Services are out of date,
-     *         Chrome lacks necessary permissions, etc.
-     */
-    @AnyThread
-    public List<Account> getGoogleAccounts() throws AccountManagerDelegateException {
-        List<Account> accounts = new ArrayList<>();
-        for (CoreAccountInfo account : getAccounts()) {
-            accounts.add(account.getAccount());
-        }
-        return accounts;
-    }
-
-    /**
      * Asynchronous version of {@link #getGoogleAccounts()}.
-     * TODO(https://crbug.com/831257): Move all callers to getAccounts and remove this
-     * method.
      */
     @MainThread
     public void getGoogleAccounts(Callback<AccountManagerResult<List<Account>>> callback) {
-        runAfterCacheIsPopulated(() -> {
-            final AccountManagerResult<List<CoreAccountInfo>> accountInfos =
-                    mFilteredAccounts.get();
-            final AccountManagerResult<List<Account>> result;
-            if (accountInfos.hasValue()) {
-                List<Account> accounts = new ArrayList<>(accountInfos.getValue().size());
-                for (CoreAccountInfo account : accountInfos.getValue()) {
-                    accounts.add(account.getAccount());
-                }
-                result = new AccountManagerResult<>(accounts);
-            } else {
-                result = new AccountManagerResult<>(accountInfos.getException());
-            }
-            callback.onResult(result);
-        });
+        runAfterCacheIsPopulated(() -> callback.onResult(mFilteredAccounts.get()));
     }
 
     /**
      * Retrieves all Google accounts on the device.
      * Returns an empty array if an error occurs while getting account list.
-     * TODO(https://crbug.com/831257): Move all callers to tryGetAccounts and remove this
-     * method.
      */
     @AnyThread
     public List<Account> tryGetGoogleAccounts() {
@@ -407,8 +336,6 @@
 
     /**
      * Asynchronous version of {@link #tryGetGoogleAccounts()}.
-     * TODO(https://crbug.com/831257): Move all callers to tryGetAccounts and remove this
-     * method.
      */
     @MainThread
     public void tryGetGoogleAccounts(final Callback<List<Account>> callback) {
@@ -635,22 +562,22 @@
         ContextUtils.getApplicationContext().registerReceiver(receiver, filter);
     }
 
-    private AccountManagerResult<List<CoreAccountInfo>> getAllAccounts() {
+    private AccountManagerResult<List<Account>> getAllAccounts() {
         try {
-            List<CoreAccountInfo> accounts = mDelegate.getAccountInfosSync();
+            List<Account> accounts = Arrays.asList(mDelegate.getAccountsSync());
             return new AccountManagerResult<>(Collections.unmodifiableList(accounts));
         } catch (AccountManagerDelegateException ex) {
             return new AccountManagerResult<>(ex);
         }
     }
 
-    private AccountManagerResult<List<CoreAccountInfo>> getFilteredAccounts() {
+    private AccountManagerResult<List<Account>> getFilteredAccounts() {
         if (mAllAccounts.hasException() || mAccountRestrictionPatterns == null) return mAllAccounts;
-        ArrayList<CoreAccountInfo> filteredAccounts = new ArrayList<>();
-        for (CoreAccountInfo accountInfo : mAllAccounts.getValue()) {
+        ArrayList<Account> filteredAccounts = new ArrayList<>();
+        for (Account account : mAllAccounts.getValue()) {
             for (PatternMatcher pattern : mAccountRestrictionPatterns) {
-                if (pattern.matches(accountInfo.getName())) {
-                    filteredAccounts.add(accountInfo);
+                if (pattern.matches(account.name)) {
+                    filteredAccounts.add(account);
                     break; // Don't check other patterns
                 }
             }
@@ -694,7 +621,7 @@
         fireOnAccountsChangedNotification();
     }
 
-    private void setAllAccounts(AccountManagerResult<List<CoreAccountInfo>> allAccounts) {
+    private void setAllAccounts(AccountManagerResult<List<Account>> allAccounts) {
         mAllAccounts = allAccounts;
         mFilteredAccounts.set(getFilteredAccounts());
         fireOnAccountsChangedNotification();
@@ -771,20 +698,19 @@
         }
     }
 
-    private class UpdateAccountsTask
-            extends AsyncTask<AccountManagerResult<List<CoreAccountInfo>>> {
+    private class UpdateAccountsTask extends AsyncTask<AccountManagerResult<List<Account>>> {
         @Override
         protected void onPreExecute() {
             incrementUpdateCounter();
         }
 
         @Override
-        protected AccountManagerResult<List<CoreAccountInfo>> doInBackground() {
+        protected AccountManagerResult<List<Account>> doInBackground() {
             return getAllAccounts();
         }
 
         @Override
-        protected void onPostExecute(AccountManagerResult<List<CoreAccountInfo>> allAccounts) {
+        protected void onPostExecute(AccountManagerResult<List<Account>> allAccounts) {
             setAllAccounts(allAccounts);
             decrementUpdateCounter();
         }
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountId.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountId.java
deleted file mode 100644
index 2535f9a..0000000
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountId.java
+++ /dev/null
@@ -1,51 +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.
-
-package org.chromium.components.signin;
-
-import android.support.annotation.NonNull;
-
-/**
- * A wrapper around Gaia ID that represents a stable account identifier.
- *
- * This wrapper helps to make sure that code using accounts doesn't accidentally use account name in
- * place of Gaia ID and vice versa.
- *
- * This class has a native counterpart called CoreAccountId.
- */
-public class CoreAccountId {
-    private final String mGaiaId;
-
-    /**
-     * Constructs a new CoreAccountId from a String representation of Gaia ID.
-     */
-    public CoreAccountId(@NonNull String gaiaId) {
-        assert gaiaId != null;
-        // Check that a user email is not used by mistake.
-        assert !gaiaId.contains("@");
-
-        mGaiaId = gaiaId;
-    }
-
-    public String getGaiaIdAsString() {
-        return mGaiaId;
-    }
-
-    @Override
-    public String toString() {
-        return mGaiaId;
-    }
-
-    @Override
-    public int hashCode() {
-        return mGaiaId.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof CoreAccountId)) return false;
-        CoreAccountId other = (CoreAccountId) obj;
-        return mGaiaId.equals(other.mGaiaId);
-    }
-}
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountInfo.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountInfo.java
deleted file mode 100644
index 15e9731..0000000
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/CoreAccountInfo.java
+++ /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.
-
-package org.chromium.components.signin;
-
-import android.accounts.Account;
-import android.support.annotation.NonNull;
-
-/**
- * Structure storing the core information about a Google account that is always known. The {@link
- * CoreAccountInfo} for a given user is almost always the same but it may change in some rare cases.
- * For example, the {@link android.accounts.Account} will change if a user changes email.
- *
- * This class has a native counterpart called CoreAccountInfo. There are several differences between
- * these two classes:
- * - Android class doesn't store Gaia ID as a string because {@link CoreAccountId} already contains
- * it.
- * - Android class additionally exposes {@link android.accounts.Account} object for interactions
- * with the system.
- * - Android class has the "account name" whereas the native class has "email". This is the same
- * string, only the naming in different.
- */
-public class CoreAccountInfo {
-    private final CoreAccountId mId;
-    private final Account mAccount;
-
-    public CoreAccountInfo(@NonNull CoreAccountId id, @NonNull Account account) {
-        assert id != null;
-        assert account != null;
-
-        mId = id;
-        mAccount = account;
-    }
-
-    /**
-     * Returns a unique identifier of the current account.
-     */
-    public CoreAccountId getId() {
-        return mId;
-    }
-
-    /**
-     * Returns a name of the current account.
-     */
-    public String getName() {
-        return mAccount.name;
-    }
-
-    /**
-     * Returns {@link android.accounts.Account} object holding a name of the current account.
-     */
-    public Account getAccount() {
-        return mAccount;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("CoreAccountInfo{id[%s], name[%s]}", getId(), getName());
-    }
-
-    @Override
-    public int hashCode() {
-        return 31 * mId.hashCode() + mAccount.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof CoreAccountInfo)) return false;
-        CoreAccountInfo other = (CoreAccountInfo) obj;
-        return mId.equals(other.mId) && mAccount.equals(other.mAccount);
-    }
-}
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
index 32be60ee..d722020 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -40,8 +40,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Default implementation of {@link AccountManagerDelegate} which delegates all calls to the
@@ -113,64 +111,23 @@
     }
 
     @Override
-    public List<CoreAccountInfo> getAccountInfosSync() throws AccountManagerDelegateException {
-        // GoogleAuthUtil.getAccountId requires Google Play Services being up to date.
+    public Account[] getAccountsSync() throws AccountManagerDelegateException {
+        // Account seeding relies on GoogleAuthUtil.getAccountId to get GAIA ids,
+        // so don't report any accounts if Google Play Services are out of date.
         checkCanUseGooglePlayServices();
 
-        long startTime = SystemClock.elapsedRealtime();
-        Account[] accounts = getAccountsSync();
-        long accountsTime = SystemClock.elapsedRealtime();
-        List<CoreAccountInfo> accountInfos = buildAccountInfoList(accounts);
-        long endTime = SystemClock.elapsedRealtime();
-        recordElapsedTimeHistogram(
-                "Signin.AndroidGetAccountsTime_AccountManager.Accounts", accountsTime - startTime);
-        recordElapsedTimeHistogram(
-                "Signin.AndroidGetAccountsTime_AccountManager.Ids", endTime - accountsTime);
-        recordElapsedTimeHistogram(
-                "Signin.AndroidGetAccountsTime_AccountManager.Total", endTime - startTime);
-        return accountInfos;
-    }
-
-    // TODO(https://crbug.com/831257): remove this.
-    public Account[] getAccountsSync() throws AccountManagerDelegateException {
-        return getAccountsFromAccountManager();
-    }
-
-    private Account[] getAccountsFromAccountManager() {
         if (!hasGetAccountsPermission()) {
             return new Account[] {};
         }
-        return mAccountManager.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
-    }
-
-    protected final List<CoreAccountInfo> buildAccountInfoList(Account[] accounts)
-            throws AccountManagerDelegateException {
-        List<CoreAccountInfo> accountInfos = new ArrayList<>(accounts.length);
-        for (Account account : accounts) {
-            CoreAccountId accountId = getAccountId(account.name);
-            if (accountId != null) {
-                accountInfos.add(new CoreAccountInfo(accountId, account));
-            }
+        long now = SystemClock.elapsedRealtime();
+        Account[] accounts = mAccountManager.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
+        long elapsed = SystemClock.elapsedRealtime() - now;
+        recordElapsedTimeHistogram("Signin.AndroidGetAccountsTime_AccountManager", elapsed);
+        if (ThreadUtils.runningOnUiThread()) {
+            recordElapsedTimeHistogram(
+                    "Signin.AndroidGetAccountsTimeUiThread_AccountManager", elapsed);
         }
-        return accountInfos;
-    }
-
-    private CoreAccountId getAccountId(String accountName) throws AccountManagerDelegateException {
-        try {
-            String accountId =
-                    GoogleAuthUtil.getAccountId(ContextUtils.getApplicationContext(), accountName);
-            return new CoreAccountId(accountId);
-        } catch (GooglePlayServicesAvailabilityException ex) {
-            Log.e(TAG, "Availability error while getting an account id", ex);
-            throw new GmsAvailabilityException(
-                    String.format("Can't use Google Play Services: %s",
-                            GoogleApiAvailability.getInstance().getErrorString(
-                                    ex.getConnectionStatusCode())),
-                    ex.getConnectionStatusCode());
-        } catch (IOException | GoogleAuthException ex) {
-            Log.e(TAG, "Error while getting an account id", ex);
-            return null;
-        }
+        return accounts;
     }
 
     @Override
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java
index 53d0f4f..4c1ed67 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java
@@ -27,11 +27,7 @@
 import org.chromium.components.signin.AccountManagerDelegateException;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountsChangeObserver;
-import org.chromium.components.signin.CoreAccountId;
-import org.chromium.components.signin.CoreAccountInfo;
 
-import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -53,15 +49,14 @@
         public void removeObserver(AccountsChangeObserver observer) {}
 
         @Override
-        public List<CoreAccountInfo> getAccountInfosSync() {
+        public Account[] getAccountsSync() {
             // Block background thread that's trying to get accounts from the delegate.
             try {
                 mBlockGetAccounts.await();
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
-            return Arrays.asList(new CoreAccountInfo(new CoreAccountId("testid"),
-                    AccountManagerFacade.createAccountFromName("test@gmail.com")));
+            return new Account[] {AccountManagerFacade.createAccountFromName("test@gmail.com")};
         }
 
         void unblockGetAccounts() {
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountHolder.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountHolder.java
index 4578550..42df492 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountHolder.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountHolder.java
@@ -7,9 +7,6 @@
 import android.accounts.Account;
 import android.support.annotation.NonNull;
 
-import org.chromium.components.signin.CoreAccountId;
-import org.chromium.components.signin.CoreAccountInfo;
-
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -20,28 +17,28 @@
  * account, such as its password and set of granted auth tokens.
  */
 public class AccountHolder {
-    private final CoreAccountInfo mAccountInfo;
+    private final Account mAccount;
     private final Map<String, String> mAuthTokens;
     private final Map<String, Boolean> mHasBeenAccepted;
     private final boolean mAlwaysAccept;
     private final Set<String> mFeatures;
 
-    private AccountHolder(CoreAccountInfo accountInfo, Map<String, String> authTokens,
+    private AccountHolder(Account account, Map<String, String> authTokens,
             Map<String, Boolean> hasBeenAccepted, boolean alwaysAccept, Set<String> features) {
-        assert accountInfo != null;
+        assert account != null;
         assert authTokens != null;
         assert hasBeenAccepted != null;
         assert features != null;
 
-        mAccountInfo = accountInfo;
+        mAccount = account;
         mAuthTokens = authTokens;
         mHasBeenAccepted = hasBeenAccepted;
         mAlwaysAccept = alwaysAccept;
         mFeatures = features;
     }
 
-    public CoreAccountInfo getAccountInfo() {
-        return mAccountInfo;
+    public Account getAccount() {
+        return mAccount;
     }
 
     public boolean hasAuthTokenRegistered(String authTokenType) {
@@ -86,13 +83,13 @@
 
     @Override
     public int hashCode() {
-        return mAccountInfo.hashCode();
+        return mAccount.hashCode();
     }
 
     @Override
     public boolean equals(Object that) {
         return that instanceof AccountHolder
-                && mAccountInfo.equals(((AccountHolder) that).getAccountInfo());
+                && mAccount.equals(((AccountHolder) that).getAccount());
     }
 
     public static Builder builder(@NonNull Account account) {
@@ -116,8 +113,7 @@
     }
 
     private Builder copy() {
-        return builder(mAccountInfo.getAccount())
-                .accountId(mAccountInfo.getId())
+        return builder(mAccount)
                 .authTokens(mAuthTokens)
                 .hasBeenAcceptedMap(mHasBeenAccepted)
                 .alwaysAccept(mAlwaysAccept);
@@ -128,7 +124,6 @@
      */
     public static class Builder {
         private Account mAccount;
-        private CoreAccountId mAccountId;
         private Map<String, String> mAuthTokens = new HashMap<>();
         private Map<String, Boolean> mHasBeenAccepted = new HashMap<>();
         private boolean mAlwaysAccept;
@@ -143,11 +138,6 @@
             return this;
         }
 
-        public Builder accountId(@NonNull CoreAccountId accountId) {
-            mAccountId = accountId;
-            return this;
-        }
-
         public Builder authToken(String authTokenType, String authToken) {
             mAuthTokens.put(authTokenType, authToken);
             return this;
@@ -190,13 +180,8 @@
         }
 
         public AccountHolder build() {
-            if (mAccountId == null) {
-                // CoreAccountId rejects strings containing '@' symbol.
-                String fakeGaiaId = "gaia-id-" + mAccount.name.replace("@", "[at]");
-                mAccountId = new CoreAccountId(fakeGaiaId);
-            }
-            return new AccountHolder(new CoreAccountInfo(mAccountId, mAccount), mAuthTokens,
-                    mHasBeenAccepted, mAlwaysAccept, mFeatures);
+            return new AccountHolder(
+                    mAccount, mAuthTokens, mHasBeenAccepted, mAlwaysAccept, mFeatures);
         }
     }
 }
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
index 6eb1d2d..5009bcc 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
@@ -22,14 +22,12 @@
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.AuthException;
-import org.chromium.components.signin.CoreAccountInfo;
 import org.chromium.components.signin.ProfileDataSource;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
@@ -105,18 +103,18 @@
     }
 
     @Override
-    public List<CoreAccountInfo> getAccountInfosSync() throws AccountManagerDelegateException {
+    public Account[] getAccountsSync() throws AccountManagerDelegateException {
         return getAccountsSyncNoThrow();
     }
 
-    public List<CoreAccountInfo> getAccountsSyncNoThrow() {
-        List<CoreAccountInfo> result = new ArrayList<>();
+    public Account[] getAccountsSyncNoThrow() {
+        ArrayList<Account> result = new ArrayList<>();
         synchronized (mAccounts) {
             for (AccountHolder ah : mAccounts) {
-                result.add(ah.getAccountInfo());
+                result.add(ah.getAccount());
             }
         }
-        return result;
+        return result.toArray(new Account[0]);
     }
 
     /**
@@ -220,7 +218,7 @@
                 // No authtoken registered. Need to create one.
                 String authToken = UUID.randomUUID().toString();
                 Log.d(TAG,
-                        "Created new auth token for " + ah.getAccountInfo() + ": authTokenScope = "
+                        "Created new auth token for " + ah.getAccount() + ": authTokenScope = "
                                 + authTokenScope + ", authToken = " + authToken);
                 ah = ah.withAuthToken(authTokenScope, authToken);
                 mAccounts.add(ah);
@@ -293,7 +291,7 @@
         }
         synchronized (mAccounts) {
             for (AccountHolder accountHolder : mAccounts) {
-                if (account.equals(accountHolder.getAccountInfo().getAccount())) {
+                if (account.equals(accountHolder.getAccount())) {
                     return accountHolder;
                 }
             }
diff --git a/components/sync/base/pref_names.cc b/components/sync/base/pref_names.cc
index 4e1df29..1677615 100644
--- a/components/sync/base/pref_names.cc
+++ b/components/sync/base/pref_names.cc
@@ -101,12 +101,6 @@
 // that we only want to use once.
 const char kSyncPassphrasePrompted[] = "sync.passphrase_prompted";
 
-// Stores how many times received MEMORY_PRESSURE_LEVEL_CRITICAL.
-const char kSyncMemoryPressureWarningCount[] = "sync.memory_warning_count";
-
-// Stores if sync shutdown cleanly.
-const char kSyncShutdownCleanly[] = "sync.shutdown_cleanly";
-
 // Dictionary of last seen invalidation versions for each model type.
 const char kSyncInvalidationVersions[] = "sync.invalidation_versions";
 
@@ -121,20 +115,32 @@
 // flag is present.
 const char kLocalSyncBackendDir[] = "sync.local_sync_backend_dir";
 
-// Stores birth year of the syncing user that is provided by the sync server.
-// Should not be logged to UMA directly.
-const char kSyncDemographicsBirthYear[] = "sync.demographics.birth_year";
+// Root dictionary pref to store user demographics provided by the sync server.
+// This is a read-only syncable priority pref, sent from the sync server to the
+// client.
+const char kSyncDemographics[] = "sync.demographics";
 
-// Stores offset that is used to randomize the birth year for metrics reporting.
-// Should not be logged to UMA directly.
-const char kSyncDemographicsBirthYearNoiseOffset[] =
-    "sync.demographics.birth_year_offset";
+// This pref value is subordinate to the kSyncDemographics dictionary pref and
+// is synced to the client. It stores the self-reported birth year of the
+// syncing user. as provided by the sync server. This value should not be logged
+// to UMA directly; instead, it should be summed with the
+// kSyncDemographicsBirthYearNoiseOffset.
+const char kSyncDemographics_BirthYearPath[] = "birth_year";
 
-// Stores the encoded gender of the syncing user that is provided by the sync
-// server. The gender is encoded using the Gender enum defined in
-// metrics::UserDemographicsProto
-// (third_party/metrics_proto/user_demographics.proto).
-const char kSyncDemographicsGender[] = "sync.demographics.gender";
+// This pref value is subordinate to the kSyncDemographics dictionary pref and
+// is synced to the client. It stores the self-reported gender of the syncing
+// user, as provided by the sync server. The gender is encoded using the Gender
+// enum defined in metrics::UserDemographicsProto
+// (see third_party/metrics_proto/user_demographics.proto).
+const char kSyncDemographics_GenderPath[] = "gender";
+
+// Stores a "secret" offset that is used to randomize the birth year for metrics
+// reporting. This value should not be logged to UMA directly; instead, it
+// should be summed with the kSyncDemographicsBirthYear. This value is both
+// generated and stored locally on the client and is not known outside of the
+// client. It is not synced.
+const char kSyncDemographicsBirthYearOffset[] =
+    "sync.demographics_birth_year_offset";
 
 }  // namespace prefs
 
diff --git a/components/sync/base/pref_names.h b/components/sync/base/pref_names.h
index 542ed1c..6c4bd2b6 100644
--- a/components/sync/base/pref_names.h
+++ b/components/sync/base/pref_names.h
@@ -70,9 +70,6 @@
 
 extern const char kSyncPassphrasePrompted[];
 
-extern const char kSyncMemoryPressureWarningCount[];
-extern const char kSyncShutdownCleanly[];
-
 extern const char kSyncInvalidationVersions[];
 
 extern const char kSyncLastRunVersion[];
@@ -80,9 +77,12 @@
 extern const char kEnableLocalSyncBackend[];
 extern const char kLocalSyncBackendDir[];
 
-extern const char kSyncDemographicsBirthYear[];
-extern const char kSyncDemographicsBirthYearNoiseOffset[];
-extern const char kSyncDemographicsGender[];
+extern const char kSyncDemographics[];
+extern const char kSyncDemographicsBirthYearOffset[];
+
+// These are not prefs, they are paths inside of kSyncDemographics.
+extern const char kSyncDemographics_BirthYearPath[];
+extern const char kSyncDemographics_GenderPath[];
 
 }  // namespace prefs
 
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 11af5eb..5ba3d8c 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -48,6 +48,13 @@
 // kSyncRequested.
 const char kSyncSuppressStart[] = "sync.suppress_start";
 
+// Obsolete pref that stored how many times sync received memory pressure
+// warnings.
+const char kSyncMemoryPressureWarningCount[] = "sync.memory_warning_count";
+
+// Obsolete pref that stored if sync shutdown cleanly.
+const char kSyncShutdownCleanly[] = "sync.shutdown_cleanly";
+
 std::vector<std::string> GetObsoleteUserTypePrefs() {
   return {prefs::kSyncAutofillProfile,
           prefs::kSyncAutofillWallet,
@@ -121,62 +128,76 @@
 // prefs.
 int GetBirthYearOffset(PrefService* pref_service) {
   int offset =
-      pref_service->GetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset);
+      pref_service->GetInteger(prefs::kSyncDemographicsBirthYearOffset);
   if (offset == kUserDemographicsBirthYearNoiseOffsetDefaultValue) {
     // Generate a random offset when not cached in prefs.
     offset = base::RandInt(-kUserDemographicsBirthYearNoiseOffsetRange,
                            kUserDemographicsBirthYearNoiseOffsetRange);
-    pref_service->SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset,
-                             offset);
+    pref_service->SetInteger(prefs::kSyncDemographicsBirthYearOffset, offset);
   }
   return offset;
 }
 
-// Determines whether the user can provide birth year considering that: (1) it
+// Determines whether the user can provide demographics considering that: (1) it
 // is not possible to infer the month and the day of the birth date when the
 // user is in an age transition, and (2) only users of at least 18 years old can
 // report demographics.
-bool CanUserProvideBirthYear(base::Time now, int user_birth_year) {
+bool CanProvideDemographics(base::Time now, int user_birth_year, int offset) {
+  // Compute user age.
   base::Time::Exploded exploded_now_time;
   now.LocalExplode(&exploded_now_time);
-  // Use > rather than >= because we want to be sure that the user is at
-  // least |kUserDemographicsMinAgeInYears| without disclosing their birth date,
-  // which requires to add an extra year margin to minimal age to be safe. For
-  // example, if we are in 2019-07-10 (now) and the user was born in 1999-08-10,
-  // the user is not yet 20 years old (minimal age) but we cannot know that
-  // because we only have access to the year of the dates (2019 and 1999
-  // respectively). If we make sure that the minimal age is at least 21, we are
-  // 100% sure that the user will be at least 20 years old when reporting
-  // demographics.
-  return exploded_now_time.year - user_birth_year >
-         kUserDemographicsMinAgeInYears;
+  int user_age = exploded_now_time.year - (user_birth_year + offset);
+
+  // Verify if the user's age has a population size in the age distribution of
+  // the society that is big enough to not rise entropy of the user. At a
+  // certain point, as the age increase, the size of the population starts
+  // declining sharply as you can see in this rough representation of the age
+  // distribution:
+  // |       ________         max age
+  // |______/        \_________ |
+  // |                          |\
+  // |                          | \
+  // +--------------------------|---------
+  //  0 10 20 30 40 50 60 70 80 90 100+
+  if (user_age > kUserDemographicsMaxAgeInYears)
+    return false;
+
+  // Verify if user is old enough. Use > rather than >= because we want to be
+  // sure that the user is at least |kUserDemographicsMinAgeInYears| without
+  // disclosing their birth date, which requires to add an extra year margin to
+  // minimal age to be safe. For example, if we are in 2019-07-10 (now) and the
+  // user was born in 1999-08-10, the user is not yet 20 years old (minimal age)
+  // but we cannot know that because we only have access to the year of the
+  // dates (2019 and 1999 respectively). If we make sure that the minimal age is
+  // at least 21, we are 100% sure that the user will be at least 20 years old
+  // when reporting demographics.
+  return user_age > kUserDemographicsMinAgeInYears;
 }
 
 // Gets user's birth year from prefs.
-base::Optional<int> GetUserBirthYear(PrefService* pref_service,
-                                     base::Time now) {
-  int birth_year = pref_service->GetInteger(prefs::kSyncDemographicsBirthYear);
+base::Optional<int> GetUserBirthYear(
+    const base::DictionaryValue* demographics) {
+  const base::Value* value =
+      demographics->FindPath(prefs::kSyncDemographics_BirthYearPath);
+  int birth_year = (value != nullptr && value->is_int())
+                       ? value->GetInt()
+                       : kUserDemographicsBirthYearDefaultValue;
 
   // Verify that there is a birth year.
   if (birth_year == kUserDemographicsBirthYearDefaultValue)
     return base::nullopt;
 
-  // Add noise to birth year.
-  birth_year += GetBirthYearOffset(pref_service);
-
-  DCHECK(!now.is_null());
-
-  // Verify that the user is old enough to provide demographics.
-  if (!CanUserProvideBirthYear(now, birth_year))
-    return base::nullopt;
-
   return birth_year;
 }
 
 // Gets user's gender from prefs.
 base::Optional<metrics::UserDemographicsProto_Gender> GetUserGender(
-    const PrefService& pref_service) {
-  int gender_int = pref_service.GetInteger(prefs::kSyncDemographicsGender);
+    const base::DictionaryValue* demographics) {
+  const base::Value* value =
+      demographics->FindPath(prefs::kSyncDemographics_GenderPath);
+  int gender_int = (value != nullptr && value->is_int())
+                       ? value->GetInt()
+                       : kUserDemographicsGenderDefaultValue;
 
   // Verify gender is not default.
   if (gender_int == kUserDemographicsGenderDefaultValue)
@@ -256,23 +277,18 @@
   registry->RegisterStringPref(prefs::kSyncKeystoreEncryptionBootstrapToken,
                                std::string());
   registry->RegisterBooleanPref(prefs::kSyncPassphrasePrompted, false);
-  registry->RegisterIntegerPref(prefs::kSyncMemoryPressureWarningCount, -1);
-  registry->RegisterBooleanPref(prefs::kSyncShutdownCleanly, false);
   registry->RegisterDictionaryPref(prefs::kSyncInvalidationVersions);
   registry->RegisterStringPref(prefs::kSyncLastRunVersion, std::string());
   registry->RegisterBooleanPref(prefs::kEnableLocalSyncBackend, false);
   registry->RegisterFilePathPref(prefs::kLocalSyncBackendDir, base::FilePath());
 
   // Demographic prefs.
-  registry->RegisterIntegerPref(
-      prefs::kSyncDemographicsBirthYear, kUserDemographicsBirthYearDefaultValue,
+  registry->RegisterDictionaryPref(
+      prefs::kSyncDemographics,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
   registry->RegisterIntegerPref(
-      prefs::kSyncDemographicsBirthYearNoiseOffset,
+      prefs::kSyncDemographicsBirthYearOffset,
       kUserDemographicsBirthYearNoiseOffsetDefaultValue);
-  registry->RegisterIntegerPref(
-      prefs::kSyncDemographicsGender, kUserDemographicsGenderDefaultValue,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
 
   // Obsolete prefs that will be removed after a grace period.
   RegisterObsoleteUserTypePrefs(registry);
@@ -287,6 +303,8 @@
   registry->RegisterStringPref(kSyncSpareBootstrapToken, "");
 #endif
   registry->RegisterBooleanPref(kSyncSuppressStart, false);
+  registry->RegisterIntegerPref(kSyncMemoryPressureWarningCount, -1);
+  registry->RegisterBooleanPref(kSyncShutdownCleanly, false);
 }
 
 void SyncPrefs::AddSyncPrefObserver(SyncPrefObserver* sync_pref_observer) {
@@ -303,9 +321,11 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Clear user demographics.
-  pref_service_->ClearPref(prefs::kSyncDemographicsBirthYear);
-  pref_service_->ClearPref(prefs::kSyncDemographicsBirthYearNoiseOffset);
-  pref_service_->ClearPref(prefs::kSyncDemographicsGender);
+  // Note that we retain kSyncDemographicsBirthYearOffset. If the user resumes
+  // syncing, causing these prefs to be recreated, we don't want them to start
+  // reporting a different randomized birth year as this could narrow down or
+  // even reveal their true birth year.
+  pref_service_->ClearPref(prefs::kSyncDemographics);
 
   ClearDirectoryConsistencyPreferences();
 
@@ -315,8 +335,6 @@
   pref_service_->ClearPref(prefs::kSyncEncryptionBootstrapToken);
   pref_service_->ClearPref(prefs::kSyncKeystoreEncryptionBootstrapToken);
   pref_service_->ClearPref(prefs::kSyncPassphrasePrompted);
-  pref_service_->ClearPref(prefs::kSyncMemoryPressureWarningCount);
-  pref_service_->ClearPref(prefs::kSyncShutdownCleanly);
   pref_service_->ClearPref(prefs::kSyncInvalidationVersions);
   pref_service_->ClearPref(prefs::kSyncLastRunVersion);
   // No need to clear kManaged, kEnableLocalSyncBackend or kLocalSyncBackendDir,
@@ -544,22 +562,6 @@
   pref_service_->SetBoolean(prefs::kSyncPassphrasePrompted, value);
 }
 
-int SyncPrefs::GetMemoryPressureWarningCount() const {
-  return pref_service_->GetInteger(prefs::kSyncMemoryPressureWarningCount);
-}
-
-void SyncPrefs::SetMemoryPressureWarningCount(int value) {
-  pref_service_->SetInteger(prefs::kSyncMemoryPressureWarningCount, value);
-}
-
-bool SyncPrefs::DidSyncShutdownCleanly() const {
-  return pref_service_->GetBoolean(prefs::kSyncShutdownCleanly);
-}
-
-void SyncPrefs::SetCleanShutdown(bool value) {
-  pref_service_->SetBoolean(prefs::kSyncShutdownCleanly, value);
-}
-
 void SyncPrefs::GetInvalidationVersions(
     std::map<ModelType, int64_t>* invalidation_versions) const {
   const base::DictionaryValue* invalidation_dictionary =
@@ -609,19 +611,31 @@
   if (now.is_null())
     return base::nullopt;
 
-  // Get birth year and gender.
-  base::Optional<int> birth_year = GetUserBirthYear(pref_service_, now);
+  // Get the pref that contains the demographic info.
+  const base::DictionaryValue* demographics =
+      pref_service_->GetDictionary(prefs::kSyncDemographics);
+  DCHECK(demographics != nullptr);
+
+  // Get the user's birth year.
+  base::Optional<int> birth_year = GetUserBirthYear(demographics);
   if (!birth_year.has_value())
     return base::nullopt;
+
+  // Get the user's gender.
   base::Optional<metrics::UserDemographicsProto_Gender> gender =
-      GetUserGender(*pref_service_);
+      GetUserGender(demographics);
   if (!gender.has_value())
     return base::nullopt;
 
-  // Set birth year and gender in demographics.
+  // Get the offset and do one last check that demographics are allowed.
+  int offset = GetBirthYearOffset(pref_service_);
+  if (!CanProvideDemographics(now, *birth_year, offset))
+    return base::nullopt;
+
+  // Set gender and offset birth year in demographics.
   UserDemographics user_demographics;
-  user_demographics.birth_year = *birth_year;
   user_demographics.gender = *gender;
+  user_demographics.birth_year = *birth_year + offset;
 
   return user_demographics;
 }
@@ -701,4 +715,9 @@
   // profile.
 }
 
+void ClearObsoleteMemoryPressurePrefs(PrefService* pref_service) {
+  pref_service->ClearPref(kSyncMemoryPressureWarningCount);
+  pref_service->ClearPref(kSyncShutdownCleanly);
+}
+
 }  // namespace syncer
diff --git a/components/sync/base/sync_prefs.h b/components/sync/base/sync_prefs.h
index a22f91f..20ba54d 100644
--- a/components/sync/base/sync_prefs.h
+++ b/components/sync/base/sync_prefs.h
@@ -158,16 +158,6 @@
   // For testing.
   void SetManagedForTest(bool is_managed);
 
-  // Get/Set number of memory warnings received.
-  int GetMemoryPressureWarningCount() const;
-  void SetMemoryPressureWarningCount(int value);
-
-  // Check if the previous shutdown was clean.
-  bool DidSyncShutdownCleanly() const;
-
-  // Set whether the last shutdown was clean.
-  void SetCleanShutdown(bool value);
-
   // Get/set for the last known sync invalidation versions.
   void GetInvalidationVersions(
       std::map<ModelType, int64_t>* invalidation_versions) const;
@@ -226,6 +216,7 @@
 void ClearObsoleteSyncSpareBootstrapToken(PrefService* pref_service);
 #endif  // defined(OS_CHROMEOS)
 void MigrateSyncSuppressedPref(PrefService* pref_service);
+void ClearObsoleteMemoryPressurePrefs(PrefService* pref_service);
 
 }  // namespace syncer
 
diff --git a/components/sync/base/sync_prefs_unittest.cc b/components/sync/base/sync_prefs_unittest.cc
index fa5f78c3..427be733 100644
--- a/components/sync/base/sync_prefs_unittest.cc
+++ b/components/sync/base/sync_prefs_unittest.cc
@@ -39,6 +39,15 @@
     sync_prefs_ = std::make_unique<SyncPrefs>(&pref_service_);
   }
 
+  void SetDemographics(int birth_year,
+                       metrics::UserDemographicsProto::Gender gender) {
+    base::DictionaryValue dict;
+    dict.SetIntPath(prefs::kSyncDemographics_BirthYearPath, birth_year);
+    dict.SetIntPath(prefs::kSyncDemographics_GenderPath,
+                    static_cast<int>(gender));
+    pref_service_.Set(prefs::kSyncDemographics, dict);
+  }
+
   base::test::ScopedTaskEnvironment task_environment_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   std::unique_ptr<SyncPrefs> sync_prefs_;
@@ -151,10 +160,7 @@
       metrics::UserDemographicsProto::GENDER_MALE;
 
   // Set user demographic prefs.
-  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYear,
-                           user_demographics_birth_year);
-  pref_service_.SetInteger(prefs::kSyncDemographicsGender,
-                           static_cast<int>(user_demographics_gender));
+  SetDemographics(user_demographics_birth_year, user_demographics_gender);
 
   int provided_birth_year;
   {
@@ -172,8 +178,8 @@
   // Verify that the offset is cached and that the randomized birth year is the
   // same when doing more that one read of the birth year.
   {
-    ASSERT_TRUE(pref_service_.HasPrefPath(
-        prefs::kSyncDemographicsBirthYearNoiseOffset));
+    ASSERT_TRUE(
+        pref_service_.HasPrefPath(prefs::kSyncDemographicsBirthYearOffset));
     base::Optional<UserDemographics> demographics =
         sync_prefs_->GetUserDemographics(GetNowTime());
     ASSERT_TRUE(demographics.has_value());
@@ -188,12 +194,10 @@
   // Set demographic prefs directly from the pref service interface because
   // demographic prefs will only be set on the server-side. The SyncPrefs
   // interface cannot set demographic prefs.
-  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYear, 1983);
-  pref_service_.SetInteger(
-      prefs::kSyncDemographicsGender,
-      static_cast<int>(metrics::UserDemographicsProto::GENDER_FEMALE));
+  SetDemographics(1983, metrics::UserDemographicsProto::GENDER_FEMALE);
+
   // Set birth year noise offset to not have it randomized.
-  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset, 2);
+  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYearOffset, 2);
 
   // Verify that demographics are provided.
   {
@@ -204,12 +208,15 @@
 
   sync_prefs_->ClearPreferences();
 
-  // Verify that demographics are not provided and all their prefs are cleared.
+  // Verify that demographics are not provided and kSyncDemographics is cleared.
+  // Note that we retain kSyncDemographicsBirthYearOffset. If the user resumes
+  // syncing, causing these prefs to be recreated, we don't want them to start
+  // reporting a different randomized birth year as this could narrow down or
+  // even reveal their true birth year.
   EXPECT_FALSE(sync_prefs_->GetUserDemographics(GetNowTime()).has_value());
-  EXPECT_FALSE(pref_service_.HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  EXPECT_FALSE(
-      pref_service_.HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  EXPECT_FALSE(pref_service_.HasPrefPath(prefs::kSyncDemographicsGender));
+  EXPECT_FALSE(pref_service_.HasPrefPath(prefs::kSyncDemographics));
+  EXPECT_TRUE(
+      pref_service_.HasPrefPath(prefs::kSyncDemographicsBirthYearOffset));
 }
 
 TEST_F(SyncPrefsTest, Basic) {
@@ -485,29 +492,19 @@
   bool should_return_demographics = false;
 };
 
-// Test fixture for parameterized tests on demographics.
+// Extend SyncPrefsTest fixture for parameterized tests on demographics.
 class SyncPrefsDemographicsTest
-    : public testing::TestWithParam<DemographicsTestParam> {
- protected:
-  SyncPrefsDemographicsTest() {
-    SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
-    sync_prefs_ = std::make_unique<SyncPrefs>(&pref_service_);
-  }
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  sync_preferences::TestingPrefServiceSyncable pref_service_;
-  std::unique_ptr<SyncPrefs> sync_prefs_;
-};
+    : public SyncPrefsTest,
+      public testing::WithParamInterface<DemographicsTestParam> {};
 
 TEST_P(SyncPrefsDemographicsTest, ReadDemographics_OffsetIsNotRandom) {
   DemographicsTestParam param = GetParam();
 
   // Set user demographic prefs.
-  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYear, param.birth_year);
-  pref_service_.SetInteger(prefs::kSyncDemographicsGender,
-                           static_cast<int>(param.gender));
+  SetDemographics(param.birth_year, param.gender);
+
   // Set birth year noise offset to not have it randomized.
-  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset,
+  pref_service_.SetInteger(prefs::kSyncDemographicsBirthYearOffset,
                            param.birth_year_offset);
 
   // Verify provided demographics for the different parameterized test cases.
@@ -545,6 +542,13 @@
             /*birth_year_offset=*/-2,
             /*gender=*/metrics::UserDemographicsProto::GENDER_FEMALE,
             /*should_return_demographics=*/false},
+        // Test where birth year should not be provided because age of user is
+        // |kUserDemographicsMaxAge| + 1, which is over the max age.
+        DemographicsTestParam{
+            /*birth_year=*/1933,
+            /*birth_year_offset=*/0,
+            /*gender=*/metrics::UserDemographicsProto::GENDER_FEMALE,
+            /*should_return_demographics=*/false},
         // Test where gender should not be provided because it has a low
         // population that can have their privacy compromised because of high
         // entropy.
diff --git a/components/sync/base/user_demographics.h b/components/sync/base/user_demographics.h
index f87a0a72..73ef0b7e 100644
--- a/components/sync/base/user_demographics.h
+++ b/components/sync/base/user_demographics.h
@@ -33,6 +33,9 @@
 // Minimal user age in years to provide demographics for.
 constexpr int kUserDemographicsMinAgeInYears = 20;
 
+// Max user age to provide demopgrahics for.
+constexpr int kUserDemographicsMaxAgeInYears = 85;
+
 // Container of user demographics.
 struct UserDemographics {
   int birth_year = 0;
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc
index 964d8e1..302d8a7 100644
--- a/components/sync/driver/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -732,66 +732,6 @@
       fake_manager_->GetTypesWithEmptyProgressMarkerToken(error_types).Empty());
 }
 
-// Ensure that redundant invalidations are ignored and that the most recent
-// set of invalidation version is persisted across restarts.
-TEST_F(SyncEngineImplTest, IgnoreOldInvalidations) {
-  // Set up some old persisted invalidations.
-  std::map<ModelType, int64_t> invalidation_versions;
-  invalidation_versions[BOOKMARKS] = 20;
-  sync_prefs_->UpdateInvalidationVersions(invalidation_versions);
-  InitializeBackend(true);
-  EXPECT_EQ(0, fake_manager_->GetInvalidationCount());
-
-  // Receiving an invalidation with an old version should do nothing.
-  ObjectIdInvalidationMap invalidation_map;
-  std::string notification_type;
-  RealModelTypeToNotificationType(BOOKMARKS, &notification_type);
-  invalidation_map.Insert(Invalidation::Init(
-      invalidation::ObjectId(0, notification_type), 10, "payload"));
-  backend_->OnIncomingInvalidation(invalidation_map);
-  fake_manager_->WaitForSyncThread();
-  EXPECT_EQ(0, fake_manager_->GetInvalidationCount());
-
-  // Invalidations with new versions should be acted upon.
-  invalidation_map.Insert(Invalidation::Init(
-      invalidation::ObjectId(0, notification_type), 30, "payload"));
-  backend_->OnIncomingInvalidation(invalidation_map);
-  fake_manager_->WaitForSyncThread();
-  EXPECT_EQ(1, fake_manager_->GetInvalidationCount());
-
-  // Invalidation for new data types should be acted on.
-  RealModelTypeToNotificationType(SESSIONS, &notification_type);
-  invalidation_map.Insert(Invalidation::Init(
-      invalidation::ObjectId(0, notification_type), 10, "payload"));
-  backend_->OnIncomingInvalidation(invalidation_map);
-  fake_manager_->WaitForSyncThread();
-  EXPECT_EQ(2, fake_manager_->GetInvalidationCount());
-
-  // But redelivering that same invalidation should be ignored.
-  backend_->OnIncomingInvalidation(invalidation_map);
-  fake_manager_->WaitForSyncThread();
-  EXPECT_EQ(2, fake_manager_->GetInvalidationCount());
-
-  // If an invalidation with an unknown version is received, it should be
-  // acted on, but should not affect the persisted versions.
-  invalidation_map.Insert(Invalidation::InitUnknownVersion(
-      invalidation::ObjectId(0, notification_type)));
-  backend_->OnIncomingInvalidation(invalidation_map);
-  fake_manager_->WaitForSyncThread();
-  EXPECT_EQ(3, fake_manager_->GetInvalidationCount());
-
-  // Verify that the invalidation versions were updated in the prefs.
-  invalidation_versions[BOOKMARKS] = 30;
-  invalidation_versions[SESSIONS] = 10;
-  std::map<ModelType, int64_t> persisted_invalidation_versions;
-  sync_prefs_->GetInvalidationVersions(&persisted_invalidation_versions);
-  EXPECT_EQ(invalidation_versions.size(),
-            persisted_invalidation_versions.size());
-  for (auto iter : persisted_invalidation_versions) {
-    EXPECT_EQ(invalidation_versions[iter.first], iter.second);
-  }
-}
-
 // Tests that SyncEngineImpl retains ModelTypeConnector after call to
 // StopSyncingForShutdown. This is needed for datatype deactivation during
 // DataTypeManager shutdown.
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 5b6581c..2484f3f 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -195,10 +195,6 @@
 
   if (identity_manager_)
     identity_manager_->AddObserver(this);
-
-  memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
-      base::BindRepeating(&ProfileSyncService::OnMemoryPressure,
-                          sync_enabled_weak_factory_.GetWeakPtr()));
 }
 
 ProfileSyncService::~ProfileSyncService() {
@@ -510,8 +506,6 @@
   }
 
   engine_->Initialize(std::move(params));
-
-  ReportPreviousSessionMemoryWarningCount();
 }
 
 void ProfileSyncService::Shutdown() {
@@ -598,9 +592,6 @@
   }
 
   NotifyObservers();
-
-  // Mark this as a clean shutdown(without crash).
-  sync_prefs_.SetCleanShutdown(true);
 }
 
 void ProfileSyncService::StopImpl(SyncStopDataFate data_fate) {
@@ -1833,34 +1824,6 @@
   }
 }
 
-void ProfileSyncService::OnMemoryPressure(
-    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  if (memory_pressure_level ==
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
-    sync_prefs_.SetMemoryPressureWarningCount(
-        sync_prefs_.GetMemoryPressureWarningCount() + 1);
-  }
-}
-
-void ProfileSyncService::ReportPreviousSessionMemoryWarningCount() {
-  int warning_received = sync_prefs_.GetMemoryPressureWarningCount();
-
-  if (-1 != warning_received) {
-    // -1 means it is new client.
-    if (!sync_prefs_.DidSyncShutdownCleanly()) {
-      UMA_HISTOGRAM_COUNTS_1M("Sync.MemoryPressureWarningBeforeUncleanShutdown",
-                              warning_received);
-    } else {
-      UMA_HISTOGRAM_COUNTS_1M("Sync.MemoryPressureWarningBeforeCleanShutdown",
-                              warning_received);
-    }
-  }
-  sync_prefs_.SetMemoryPressureWarningCount(0);
-  // Will set to true during a clean shutdown, so crash or something else will
-  // remain this as false.
-  sync_prefs_.SetCleanShutdown(false);
-}
-
 void ProfileSyncService::RecordMemoryUsageHistograms() {
   ModelTypeSet active_types = GetActiveDataTypes();
   for (ModelType type : active_types) {
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index 43f9f1d48..7491b7a0 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -12,7 +12,6 @@
 
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/memory/memory_pressure_listener.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -344,10 +343,6 @@
   // Tell the sync server that this client has disabled sync.
   void RemoveClientFromServer() const;
 
-  // Called when the system is under memory pressure.
-  void OnMemoryPressure(
-      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-
   // Check if previous shutdown is shutdown cleanly.
   void ReportPreviousSessionMemoryWarningCount();
 
@@ -481,9 +476,6 @@
 
   std::unique_ptr<SyncStoppedReporter> sync_stopped_reporter_;
 
-  // Listens for the system being under memory pressure.
-  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
-
   // Whether the major version has changed since the last time Chrome ran,
   // and therefore a passphrase required state should result in prompting
   // the user. This logic is only enabled on platforms that consume the
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index 852fae37..6536737 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -291,6 +291,31 @@
     return profile_sync_service_bundle_.component_factory();
   }
 
+  void SetDemographics(int birth_year,
+                       metrics::UserDemographicsProto_Gender gender) {
+    base::DictionaryValue dict;
+    dict.SetIntPath(prefs::kSyncDemographics_BirthYearPath, birth_year);
+    dict.SetIntPath(prefs::kSyncDemographics_GenderPath,
+                    static_cast<int>(gender));
+    prefs()->Set(prefs::kSyncDemographics, dict);
+  }
+
+  static bool HasBirthYearDemographic(const PrefService* pref_service) {
+    return pref_service->HasPrefPath(prefs::kSyncDemographics) &&
+           pref_service->GetDictionary(prefs::kSyncDemographics)
+               ->FindIntPath(prefs::kSyncDemographics_BirthYearPath);
+  }
+
+  static bool HasGenderDemographic(const PrefService* pref_service) {
+    return pref_service->HasPrefPath(prefs::kSyncDemographics) &&
+           pref_service->GetDictionary(prefs::kSyncDemographics)
+               ->FindIntPath(prefs::kSyncDemographics_GenderPath);
+  }
+
+  static bool HasBirthYearOffset(const PrefService* pref_service) {
+    return pref_service->HasPrefPath(prefs::kSyncDemographicsBirthYearOffset);
+  }
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   ProfileSyncServiceBundle profile_sync_service_bundle_;
@@ -834,19 +859,19 @@
   base::Time last_synced_time = service()->GetLastSyncedTimeForDebugging();
   ASSERT_LT(base::Time::Now() - last_synced_time,
             base::TimeDelta::FromMinutes(1));
+
   // Set demographic prefs that are normally fetched from server when syncing.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYear,
-                      kOldEnoughForDemographicsUserBirthYear);
-  prefs()->SetInteger(
-      prefs::kSyncDemographicsGender,
-      static_cast<int>(metrics::UserDemographicsProto_Gender_GENDER_FEMALE));
+  SetDemographics(kOldEnoughForDemographicsUserBirthYear,
+                  metrics::UserDemographicsProto_Gender_GENDER_FEMALE);
+
   // Set the birth year offset pref that would be normally set when calling
   // SyncPrefs::GetUserDemographics.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset, 2);
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  ASSERT_TRUE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearOffset, 2);
+
+  // Verify that the demographics prefs exist (i.e., that the test is set up).
+  ASSERT_TRUE(HasBirthYearDemographic(prefs()));
+  ASSERT_TRUE(HasGenderDemographic(prefs()));
+  ASSERT_TRUE(HasBirthYearOffset(prefs()));
 
   // Sign out.
   service()->StopAndClear();
@@ -860,10 +885,14 @@
   EXPECT_NE(service()->GetLastSyncedTimeForDebugging(), last_synced_time);
 
   // Check that the demographic prefs are cleared.
-  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  EXPECT_FALSE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographics));
+  EXPECT_FALSE(HasBirthYearDemographic(prefs()));
+  EXPECT_FALSE(HasGenderDemographic(prefs()));
+
+  // Verify that the random offset is preserved. If the user signs in again,
+  // we don't want them to start reporting a different randomized birth year
+  // as this could narrow down or ever reveal their true birth year.
+  EXPECT_TRUE(HasBirthYearOffset(prefs()));
 }
 
 // Verify that demographic prefs are cleared when the service is initializing
@@ -872,18 +901,17 @@
   // Set demographic prefs that are leftovers from previous sync. We can imagine
   // that due to some crash, sync service did not clear demographics when
   // account was signed out.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYear,
-                      kOldEnoughForDemographicsUserBirthYear);
-  prefs()->SetInteger(
-      prefs::kSyncDemographicsGender,
-      static_cast<int>(metrics::UserDemographicsProto_Gender_GENDER_FEMALE));
+  SetDemographics(kOldEnoughForDemographicsUserBirthYear,
+                  metrics::UserDemographicsProto_Gender_GENDER_FEMALE);
+
   // Set the birth year offset pref that would be normally set when calling
   // SyncPrefs::GetUserDemographics.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset, 2);
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  ASSERT_TRUE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearOffset, 2);
+
+  // Verify that the demographics prefs exist (i.e., that the test is set up).
+  ASSERT_TRUE(HasBirthYearDemographic(prefs()));
+  ASSERT_TRUE(HasGenderDemographic(prefs()));
+  ASSERT_TRUE(HasBirthYearOffset(prefs()));
 
   // Don't sign-in before creating the service.
   CreateService(ProfileSyncService::AUTO_START);
@@ -891,10 +919,14 @@
   InitializeForNthSync();
 
   // Verify that the demographic prefs are cleared.
-  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  EXPECT_FALSE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  EXPECT_FALSE(prefs()->HasPrefPath(prefs::kSyncDemographics));
+  EXPECT_FALSE(HasBirthYearDemographic(prefs()));
+  EXPECT_FALSE(HasGenderDemographic(prefs()));
+
+  // Verify that the random offset is preserved. If the user signs in again,
+  // we don't want them to start reporting a different randomized birth year
+  // as this could narrow down or ever reveal their true birth year.
+  EXPECT_TRUE(HasBirthYearOffset(prefs()));
 }
 
 TEST_F(ProfileSyncServiceTest, CancelSyncAfterSignOut) {
@@ -1063,43 +1095,6 @@
   EXPECT_TRUE(switches::IsSyncAllowedByFlag());
 }
 
-// Test Sync will stop after receive memory pressure
-TEST_F(ProfileSyncServiceTest, MemoryPressureRecording) {
-  CreateService(ProfileSyncService::AUTO_START);
-  SignIn();
-  InitializeForNthSync();
-
-  ASSERT_TRUE(prefs()->GetBoolean(prefs::kSyncRequested));
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-
-  testing::Mock::VerifyAndClearExpectations(component_factory());
-
-  SyncPrefs sync_prefs(prefs());
-
-  ASSERT_EQ(prefs()->GetInteger(prefs::kSyncMemoryPressureWarningCount), 0);
-  ASSERT_FALSE(sync_prefs.DidSyncShutdownCleanly());
-
-  // Simulate memory pressure notification.
-  base::MemoryPressureListener::NotifyMemoryPressure(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  base::RunLoop().RunUntilIdle();
-
-  // Verify memory pressure recorded.
-  EXPECT_EQ(prefs()->GetInteger(prefs::kSyncMemoryPressureWarningCount), 1);
-  EXPECT_FALSE(sync_prefs.DidSyncShutdownCleanly());
-
-  // Simulate memory pressure notification.
-  base::MemoryPressureListener::NotifyMemoryPressure(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  base::RunLoop().RunUntilIdle();
-  ShutdownAndDeleteService();
-
-  // Verify memory pressure and shutdown recorded.
-  EXPECT_EQ(prefs()->GetInteger(prefs::kSyncMemoryPressureWarningCount), 2);
-  EXPECT_TRUE(sync_prefs.DidSyncShutdownCleanly());
-}
-
 // Test that the passphrase prompt due to version change logic gets triggered
 // on a datatype type requesting startup, but only happens once.
 TEST_F(ProfileSyncServiceTest, PassphrasePromptDueToVersion) {
@@ -1286,13 +1281,11 @@
       metrics::UserDemographicsProto_Gender_GENDER_FEMALE;
 
   // Set demographic prefs that are normally fetched from server when syncing.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYear,
-                      user_demographics_birth_year);
-  prefs()->SetInteger(prefs::kSyncDemographicsGender,
-                      static_cast<int>(user_demographics_gender));
+  SetDemographics(user_demographics_birth_year, user_demographics_gender);
+
   // Directly set birth year offset in demographic prefs to avoid it being set
   // with a random value when calling GetUserDemographics().
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset,
+  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearOffset,
                       birth_year_offset);
 
   base::Optional<UserDemographics> user_demographics =
@@ -1316,15 +1309,15 @@
   // disabled. We keep the demographic prefs available in this test to make
   // sure that they are not provided when sync is disabled (we want
   // base::nullopt in any case).
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYear,
-                      kOldEnoughForDemographicsUserBirthYear);
-  prefs()->SetInteger(
-      prefs::kSyncDemographicsGender,
-      static_cast<int>(metrics::UserDemographicsProto_Gender_GENDER_FEMALE));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  SetDemographics(kOldEnoughForDemographicsUserBirthYear,
+                  metrics::UserDemographicsProto_Gender_GENDER_FEMALE);
 
-  ASSERT_FALSE(service()->GetUserDemographics(GetNowTime()).has_value());
+  // Verify that demographic prefs exist (i.e., the test is set up).
+  ASSERT_TRUE(HasBirthYearDemographic(prefs()));
+  ASSERT_TRUE(HasGenderDemographic(prefs()));
+
+  // Verify that we don't get demographics when sync is off.
+  EXPECT_FALSE(service()->GetUserDemographics(GetNowTime()).has_value());
 }
 
 // Test whether sync service does not provide user demographics and does not
@@ -1344,18 +1337,17 @@
       metrics::UserDemographicsProto_Gender_GENDER_FEMALE;
 
   // Set demographic prefs that are normally fetched from server when syncing.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYear,
-                      user_demographics_birth_year);
-  prefs()->SetInteger(prefs::kSyncDemographicsGender,
-                      static_cast<int>(user_demographics_gender));
+  SetDemographics(user_demographics_birth_year, user_demographics_gender);
+
   // Set birth year noise offset that is usually set when calling
   // SyncPrefs::GetUserDemographics.
-  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearNoiseOffset,
+  prefs()->SetInteger(prefs::kSyncDemographicsBirthYearOffset,
                       static_cast<int>(birth_year_offset));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  ASSERT_TRUE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  ASSERT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+
+  // Verify that demographic prefs exist (i.e., the test is set up).
+  ASSERT_TRUE(HasBirthYearDemographic(prefs()));
+  ASSERT_TRUE(HasGenderDemographic(prefs()));
+  ASSERT_TRUE(HasBirthYearOffset(prefs()));
 
   // Temporarily disable sync without turning it off.
   service()->GetUserSettings()->SetSyncRequested(false);
@@ -1370,10 +1362,9 @@
   EXPECT_FALSE(user_demographics.has_value());
 
   // Verify that demographic prefs are not cleared.
-  EXPECT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYear));
-  EXPECT_TRUE(
-      prefs()->HasPrefPath(prefs::kSyncDemographicsBirthYearNoiseOffset));
-  EXPECT_TRUE(prefs()->HasPrefPath(prefs::kSyncDemographicsGender));
+  EXPECT_TRUE(HasBirthYearDemographic(prefs()));
+  EXPECT_TRUE(HasGenderDemographic(prefs()));
+  EXPECT_TRUE(HasBirthYearOffset(prefs()));
 }
 
 }  // namespace
diff --git a/components/sync/driver/resources/chrome_sync.js b/components/sync/driver/resources/chrome_sync.js
index ffc727d8..4ad2a35 100644
--- a/components/sync/driver/resources/chrome_sync.js
+++ b/components/sync/driver/resources/chrome_sync.js
@@ -12,85 +12,82 @@
    * A simple timer to measure elapsed time.
    * @constructor
    */
-  function Timer() {
+  class Timer {
+    constructor() {
+      /**
+       * The time that this Timer was created.
+       * @type {number}
+       * @private
+       * @const
+       */
+      this.start_ = Date.now();
+    }
+
     /**
-     * The time that this Timer was created.
-     * @type {number}
-     * @private
-     * @const
+     * @return {number} The elapsed seconds since this Timer was created.
      */
-    this.start_ = Date.now();
+    getElapsedSeconds() {
+      return (Date.now() - this.start_) / 1000;
+    }
   }
 
   /**
-   * @return {number} The elapsed seconds since this Timer was created.
-   */
-  Timer.prototype.getElapsedSeconds = function() {
-    return (Date.now() - this.start_) / 1000;
-  };
-
-  /** @return {!Timer} An object which measures elapsed time. */
-  const makeTimer = function() {
-    return new Timer;
-  };
-
-  /**
    * @param {string} name The name of the event type.
    * @param {!Object} details A collection of event-specific details.
    */
-  const dispatchEvent = function(name, details) {
+  function dispatchEvent(name, details) {
     const e = new Event(name);
     e.details = details;
     chrome.sync.events.dispatchEvent(e);
-  };
+  }
 
   /**
    * Registers to receive a stream of events through
    * chrome.sync.dispatchEvent().
    */
-  const registerForEvents = function() {
+  function registerForEvents() {
     chrome.send('registerForEvents');
-  };
+  }
 
   /**
    * Registers to receive a stream of status counter update events
    * chrome.sync.dispatchEvent().
    */
-  const registerForPerTypeCounters = function() {
+  function registerForPerTypeCounters() {
     chrome.send('registerForPerTypeCounters');
-  };
+  }
 
   /**
    * Asks the browser to refresh our snapshot of sync state. Should result
    * in an onAboutInfoUpdated event being emitted.
    */
-  const requestUpdatedAboutInfo = function() {
+  function requestUpdatedAboutInfo() {
     chrome.send('requestUpdatedAboutInfo');
-  };
+  }
 
   /**
    * Asks the browser to send us the list of registered types. Should result
    * in an onReceivedListOfTypes event being emitted.
    */
-  const requestListOfTypes = function() {
+  function requestListOfTypes() {
     chrome.send('requestListOfTypes');
-  };
+  }
 
   /**
    * Asks the browser to send us the initial state of the "include specifics"
    * flag. Should result in an onReceivedIncludeSpecificsInitialState event
    * being emitted.
    */
-  const requestIncludeSpecificsInitialState = function() {
+  function requestIncludeSpecificsInitialState() {
     chrome.send('requestIncludeSpecificsInitialState');
-  };
+  }
 
   /**
    * Asks the browser if we should show the User Events tab or not.
    */
-  const requestUserEventsVisibility = function() {
+  function requestUserEventsVisibility() {
     chrome.send('requestUserEventsVisibility');
-  };
+  }
 
   /**
    * Updates the logic sending events to the protocol logic if they should
@@ -99,9 +96,9 @@
    * @param {boolean} includeSpecifics Whether protocol events include
    *     specifics.
    */
-  const setIncludeSpecifics = function(includeSpecifics) {
+  function setIncludeSpecifics(includeSpecifics) {
     chrome.send('setIncludeSpecifics', [includeSpecifics]);
-  };
+  }
 
   /**
    * Sends data to construct a user event that should be committed.
@@ -109,37 +106,37 @@
    * @param {string} eventTimeUsec Timestamp for the new event.
    * @param {string} navigationId Timestamp of linked sessions navigation.
    */
-  const writeUserEvent = function(eventTimeUsec, navigationId) {
+  function writeUserEvent(eventTimeUsec, navigationId) {
     chrome.send('writeUserEvent', [eventTimeUsec, navigationId]);
-  };
+  }
 
   /**
    * Triggers a RequestStart call on the SyncService.
    */
-  const requestStart = function() {
+  function requestStart() {
     chrome.send('requestStart');
-  };
+  }
 
   /**
    * Triggers a RequestStop(KEEP_DATA) call on the SyncService.
    */
-  const requestStopKeepData = function() {
+  function requestStopKeepData() {
     chrome.send('requestStopKeepData');
-  };
+  }
 
   /**
    * Triggers a RequestStop(CLEAR_DATA) call on the SyncService.
    */
-  const requestStopClearData = function() {
+  function requestStopClearData() {
     chrome.send('requestStopClearData');
-  };
+  }
 
   /**
    * Triggers a GetUpdates call for all enabled datatypes.
    */
-  const triggerRefresh = function() {
+  function triggerRefresh() {
     chrome.send('triggerRefresh');
-  };
+  }
 
   /**
    * Counter to uniquely identify requests while they're in progress.
@@ -160,11 +157,11 @@
    *
    * @param {function(!Object)} callback The function to call with the response.
    */
-  const getAllNodes = function(callback) {
+  function getAllNodes(callback) {
     requestId++;
     requestCallbacks[requestId] = callback;
     chrome.send('getAllNodes', [requestId]);
-  };
+  }
 
   /**
    * Called from C++ with the response to a getAllNodes request.
@@ -172,13 +169,13 @@
    * @param {number} id The requestId passed in with the request.
    * @param {Object} response The response to the request.
    */
-  const getAllNodesCallback = function(id, response) {
+  function getAllNodesCallback(id, response) {
     requestCallbacks[id](response);
     delete requestCallbacks[id];
-  };
+  }
 
   return {
-    makeTimer: makeTimer,
+    Timer: Timer,
     dispatchEvent: dispatchEvent,
     // TODO(crbug.com/854268,crbug.com/976249): Use new cr.EventTarget() when
     // the native EventTarget constructor is implemented on iOS (not the case as
diff --git a/components/sync/driver/resources/sync_node_browser.js b/components/sync/driver/resources/sync_node_browser.js
index d772d80..dc8e51e 100644
--- a/components/sync/driver/resources/sync_node_browser.js
+++ b/components/sync/driver/resources/sync_node_browser.js
@@ -12,9 +12,9 @@
    *
    * @param {!Object} node The node to check.
    */
-  const isTypeRootNode = function(node) {
+  function isTypeRootNode(node) {
     return node.PARENT_ID == 'r' && node.UNIQUE_SERVER_TAG != '';
-  };
+  }
 
   /**
    * A helper function to determine if a node is a child of the given parent.
@@ -22,13 +22,13 @@
    * @param {!Object} parent node.
    * @param {!Object} node The node to check.
    */
-  const isChildOf = function(parentNode, node) {
+  function isChildOf(parentNode, node) {
     if (node.PARENT_ID != '') {
       return node.PARENT_ID == parentNode.ID;
     } else {
       return node.modelType == parentNode.modelType;
     }
-  };
+  }
 
   /**
    * A helper function to sort sync nodes.
@@ -39,7 +39,7 @@
    * If this proves to be slow and expensive, we should experiment with moving
    * this functionality to C++ instead.
    */
-  const nodeComparator = function(nodeA, nodeB) {
+  function nodeComparator(nodeA, nodeB) {
     if (nodeA.hasOwnProperty('positionIndex') &&
         nodeB.hasOwnProperty('positionIndex')) {
       return nodeA.positionIndex - nodeB.positionIndex;
@@ -48,7 +48,7 @@
     } else {
       return nodeA.METAHANDLE - nodeB.METAHANDLE;
     }
-  };
+  }
 
   /**
    * Updates the node detail view with the details for the given node.
@@ -76,7 +76,7 @@
    *     chrome.sync.getAllNodes().
    * @extends {cr.ui.TreeItem}
    */
-  const SyncNodeTreeItem = function(node) {
+  function SyncNodeTreeItem(node) {
     const treeItem = new cr.ui.TreeItem();
     treeItem.__proto__ = SyncNodeTreeItem.prototype;
 
@@ -93,7 +93,7 @@
       treeItem.classList.add('leaf');
     }
     return treeItem;
-  };
+  }
 
   SyncNodeTreeItem.prototype = {
     __proto__: cr.ui.TreeItem.prototype,
diff --git a/components/sync/driver/resources/sync_search.js b/components/sync/driver/resources/sync_search.js
index 3e04ca3..226e49c 100644
--- a/components/sync/driver/resources/sync_search.js
+++ b/components/sync/driver/resources/sync_search.js
@@ -7,16 +7,16 @@
 cr.define('chrome.sync', function() {
   let currSearchId = 0;
 
-  const setQueryString = function(queryControl, query) {
+  function setQueryString(queryControl, query) {
     queryControl.value = query;
-  };
+  }
 
-  const createDoQueryFunction = function(queryControl, submitControl, query) {
+  function createDoQueryFunction(queryControl, submitControl, query) {
     return function() {
       setQueryString(queryControl, query);
       submitControl.click();
     };
-  };
+  }
 
   /**
    * Decorates the quick search controls
@@ -27,7 +27,7 @@
    * @param {!HTMLInputElement} queryControl The <input> object of
    *     type=search where user's query is typed.
    */
-  const decorateQuickQueryControls = function(
+  function decorateQuickQueryControls(
       quickLinkArray, submitControl, queryControl) {
     for (let index = 0; index < quickLinkArray.length; ++index) {
       const quickQuery = quickLinkArray[index].getAttribute('data-query');
@@ -35,7 +35,7 @@
           createDoQueryFunction(queryControl, submitControl, quickQuery);
       quickLinkArray[index].addEventListener('click', quickQueryFunction);
     }
-  };
+  }
 
   /**
    * Runs a search with the given query.
@@ -44,7 +44,7 @@
    * @param {!Function} callback The callback called with the search results.
    *     not called if doSearch() is called again while the search is running.
    */
-  const doSearch = function(query, callback) {
+  function doSearch(query, callback) {
     const searchId = ++currSearchId;
     try {
       const regex = new RegExp(query);
@@ -70,7 +70,7 @@
       // be caught and handled here.
       callback([], err);
     }
-  };
+  }
 
   /**
    * Decorates the various search controls.
@@ -90,7 +90,7 @@
                                   resultsControl, detailsControl) {
     const resultsDataModel = new cr.ui.ArrayDataModel([]);
 
-    const searchFunction = function() {
+    function searchFunction() {
       const query = queryControl.value;
       statusControl.textContent = '';
       resultsDataModel.splice(0, resultsDataModel.length);
@@ -99,7 +99,7 @@
       }
       statusControl.textContent = 'Searching for ' + query + '...';
       queryControl.removeAttribute('error');
-      const timer = chrome.sync.makeTimer();
+      const timer = new chrome.sync.Timer();
       doSearch(query, function(nodes, error) {
         if (error) {
           statusControl.textContent = 'Error: ' + error;
@@ -121,7 +121,7 @@
           resultsControl.redraw();
         }
       });
-    };
+    }
 
     submitControl.addEventListener('click', searchFunction);
     // Decorate search box.
diff --git a/components/sync/driver/resources/types.js b/components/sync/driver/resources/types.js
index 94a359842..5bb6e55 100644
--- a/components/sync/driver/resources/types.js
+++ b/components/sync/driver/resources/types.js
@@ -11,7 +11,7 @@
    *
    * Makes use of typeCountersMap, which is defined in the containing scope.
    */
-  const refreshTypeCountersDisplay = function() {
+  function refreshTypeCountersDisplay() {
     const typeCountersArray = [];
 
     // Transform our map into an array to make jstemplate happy.
@@ -25,7 +25,7 @@
     jstProcess(
         new JsEvalContext({ rows: typeCountersArray }),
         $('type-counters-table'));
-  };
+  }
 
   /**
    * Helps to initialize the table by picking up where initTypeCounters() left
@@ -34,7 +34,7 @@
    *
    * @param {!Object} e An event containing the list of known sync types.
    */
-  const onReceivedListOfTypes = function(e) {
+  function onReceivedListOfTypes(e) {
     const types = e.details.types;
     types.map(function(type) {
       if (!typeCountersMap.hasOwnProperty(type)) {
@@ -45,14 +45,14 @@
         'onReceivedListOfTypes',
         onReceivedListOfTypes);
     refreshTypeCountersDisplay();
-  };
+  }
 
   /**
    * Callback for receipt of updated per-type counters.
    *
    * @param {!Object} e An event containing an updated counter.
    */
-  const onCountersUpdated = function(e) {
+  function onCountersUpdated(e) {
     const details = e.details;
 
     const modelType = details.modelType;
@@ -64,12 +64,12 @@
       }
     }
     refreshTypeCountersDisplay();
-  };
+  }
 
   /**
    * Initializes state and callbacks for the per-type counters and status UI.
    */
-  const initTypeCounters = function() {
+  function initTypeCounters() {
     chrome.sync.events.addEventListener(
         'onCountersUpdated',
         onCountersUpdated);
@@ -79,11 +79,11 @@
 
     chrome.sync.requestListOfTypes();
     chrome.sync.registerForPerTypeCounters();
-  };
+  }
 
-  const onLoad = function() {
+  function onLoad() {
     initTypeCounters();
-  };
+  }
 
   return {
     onLoad: onLoad
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker.cc b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
index a23e36b..aea26994ea 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
@@ -305,7 +305,7 @@
       reinterpret_cast<icu::RegexMatcher*>(DangerousPatternTLS().Get());
   if (!dangerous_pattern) {
     // The parentheses in the below strings belong to the raw string sequence
-    // R("..."). They are NOT part of the regular expression. Each sub
+    // R"(...)". They are NOT part of the regular expression. Each sub
     // regex is OR'ed with the | operator.
     dangerous_pattern = new icu::RegexMatcher(
         icu::UnicodeString(
@@ -362,6 +362,9 @@
             // Disallow dotless i (U+0131) followed by a combining mark.
             R"(\u0131[\u0300-\u0339]|)"
 
+            // Disallow combining Kana voiced sound marks.
+            R"(\u3099|\u309A|)"
+
             // Disallow U+0307 (dot above) after 'i', 'j', 'l' or dotless i
             // (U+0131). Dotless j (U+0237) is not in the allowed set to begin
             // with.
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
index 7c9f7f3..cb7ba5cc 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
@@ -1059,6 +1059,10 @@
     // This is allowed because the ideographs are not immediately next to
     // non-CJK.
     {"xn--oiqsace.com", L"丶乀乁丿.com", true},
+
+    // Kana voiced sound marks are not allowed.
+    {"xn--google-1m4e.com", L"google\x3099.com", false},
+    {"xn--google-8m4e.com", L"google\x309A.com", false},
 };
 
 namespace test {
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_android.cc b/content/browser/accessibility/browser_accessibility_state_impl_android.cc
index a97a68c..1a7c52bb 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_android.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_android.cc
@@ -5,6 +5,7 @@
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 
 #include "base/android/jni_android.h"
+#include "base/metrics/histogram_macros.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/android/content_jni_headers/BrowserAccessibilityState_jni.h"
 #include "content/public/browser/browser_thread.h"
@@ -33,6 +34,12 @@
 
   JNIEnv* env = AttachCurrentThread();
   Java_BrowserAccessibilityState_recordAccessibilityHistograms(env);
+
+  // Screen reader metric.
+  ui::AXMode mode =
+      BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode();
+  UMA_HISTOGRAM_BOOLEAN("Accessibility.Android.ScreenReader",
+                        mode.has_mode(ui::AXMode::kScreenReader));
 }
 
 // static
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_mac.mm b/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
index 8fee871..92601ed 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
@@ -85,6 +85,12 @@
 }
 
 void BrowserAccessibilityStateImpl::
-    UpdatePlatformSpecificHistogramsOnOtherThread() {}
+    UpdatePlatformSpecificHistogramsOnOtherThread() {
+  // Screen reader metric.
+  ui::AXMode mode =
+      BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode();
+  UMA_HISTOGRAM_BOOLEAN("Accessibility.Mac.ScreenReader",
+                        mode.has_mode(ui::AXMode::kScreenReader));
+}
 
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
index c311f31..0fd8916 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_win.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -105,10 +105,19 @@
   UMA_HISTOGRAM_BOOLEAN("Accessibility.WinAudioDescription",
                         !!audio_description.Enabled);
 
+  // This screen reader flag is nearly meaningless, it is set very often
+  // when there is no screen reader, and is not set for Narrator.
   BOOL win_screen_reader = FALSE;
   SystemParametersInfo(SPI_GETSCREENREADER, 0, &win_screen_reader, 0);
   UMA_HISTOGRAM_BOOLEAN("Accessibility.WinScreenReader", !!win_screen_reader);
 
+  // Better all-encompassing screen reader metric.
+  // See also specific screen reader metrics below, e.g. WinJAWS, WinNVDA.
+  ui::AXMode mode =
+      BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode();
+  UMA_HISTOGRAM_BOOLEAN("Accessibility.WinScreenReader2",
+                        mode.has_mode(ui::AXMode::kScreenReader));
+
   STICKYKEYS sticky_keys = {0};
   sticky_keys.cbSize = sizeof(STICKYKEYS);
   SystemParametersInfo(SPI_GETSTICKYKEYS, 0, &sticky_keys, 0);
@@ -153,7 +162,8 @@
       nvda = true;
     if (base::LowerCaseEqualsASCII(module_name, "stsaw32.dll"))
       satogo = true;
-    if (base::LowerCaseEqualsASCII(module_name, "zslhook.dll"))
+    if (base::LowerCaseEqualsASCII(module_name, "zslhook.dll") ||
+        base::LowerCaseEqualsASCII(module_name, "zslhook64.dll"))
       zoomtext = true;
   }
 
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index fee97c9..4304b436 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -633,7 +633,9 @@
   AppCacheSubresourceURLFactory::CreateURLLoaderFactory(GetWeakPtr(),
                                                         &factory_ptr);
 
-  frontend()->SetSubresourceFactory(std::move(factory_ptr));
+  // We may not have bound |factory_ptr| if the storage partition has shut down.
+  if (factory_ptr)
+    frontend()->SetSubresourceFactory(std::move(factory_ptr));
 }
 
 void AppCacheHost::SetAppCacheSubresourceFactory(
diff --git a/content/browser/appcache/appcache_quota_client.cc b/content/browser/appcache/appcache_quota_client.cc
index 2742e29..f692c13 100644
--- a/content/browser/appcache/appcache_quota_client.cc
+++ b/content/browser/appcache/appcache_quota_client.cc
@@ -11,12 +11,16 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/task/post_task.h"
 #include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/loader/navigation_url_loader_impl.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 
 using blink::mojom::StorageType;
 using storage::QuotaClient;
 
+namespace content {
 namespace {
 blink::mojom::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) {
   if (code == net::OK)
@@ -32,14 +36,24 @@
   queue->pop_front();
   std::move(request).Run();
 }
+
+void RunDeleteOnIO(const base::Location& from_here,
+                   net::CompletionRepeatingCallback callback,
+                   int result) {
+  if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    std::move(callback).Run(result);
+    return;
+  }
+
+  base::PostTaskWithTraits(from_here, {BrowserThread::IO},
+                           base::BindOnce(std::move(callback), result));
+}
 }  // namespace
 
-namespace content {
-
-AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service)
-    : service_(service),
-      appcache_is_ready_(false),
-      quota_manager_is_destroyed_(false) {
+AppCacheQuotaClient::AppCacheQuotaClient(
+    base::WeakPtr<AppCacheServiceImpl> service)
+    : service_(std::move(service)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 AppCacheQuotaClient::~AppCacheQuotaClient() {
@@ -53,6 +67,7 @@
 }
 
 void AppCacheQuotaClient::OnQuotaManagerDestroyed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DeletePendingRequests();
   if (!current_delete_request_callback_.is_null()) {
     current_delete_request_callback_.Reset();
@@ -60,17 +75,18 @@
   }
 
   quota_manager_is_destroyed_ = true;
-  if (!service_)
+  if (service_is_destroyed_)
     delete this;
 }
 
 void AppCacheQuotaClient::GetOriginUsage(const url::Origin& origin,
                                          StorageType type,
                                          GetUsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
   DCHECK(!quota_manager_is_destroyed_);
 
-  if (!service_) {
+  if (service_is_destroyed_) {
     std::move(callback).Run(0);
     return;
   }
@@ -87,23 +103,37 @@
     return;
   }
 
-  const std::map<url::Origin, int64_t>& map = GetUsageMap();
-  auto it = map.find(origin);
-  if (it == map.end()) {
-    std::move(callback).Run(0);
-    return;
-  }
-  std::move(callback).Run(it->second);
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID()},
+      base::BindOnce(
+          [](base::WeakPtr<AppCacheServiceImpl> service,
+             const url::Origin& origin) -> int64_t {
+            if (!service)
+              return 0;
+
+            const std::map<url::Origin, int64_t>& map =
+                service->storage()->usage_map();
+            auto it = map.find(origin);
+            if (it == map.end())
+              return 0;
+
+            return it->second;
+          },
+          service_, origin),
+      std::move(callback));
 }
 
 void AppCacheQuotaClient::GetOriginsForType(StorageType type,
                                             GetOriginsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   GetOriginsHelper(type, std::string(), std::move(callback));
 }
 
 void AppCacheQuotaClient::GetOriginsForHost(StorageType type,
                                             const std::string& host,
                                             GetOriginsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
   if (host.empty()) {
     std::move(callback).Run(std::set<url::Origin>());
@@ -115,9 +145,10 @@
 void AppCacheQuotaClient::DeleteOriginData(const url::Origin& origin,
                                            StorageType type,
                                            DeletionCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!quota_manager_is_destroyed_);
 
-  if (!service_) {
+  if (service_is_destroyed_) {
     std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorAbort);
     return;
   }
@@ -135,16 +166,20 @@
     return;
   }
 
-  service_->DeleteAppCachesForOrigin(origin,
-                                     GetServiceDeleteCallback()->callback());
+  NavigationURLLoaderImpl::RunOrPostTaskOnLoaderThread(
+      FROM_HERE,
+      base::BindOnce(&AppCacheServiceImpl::DeleteAppCachesForOrigin, service_,
+                     origin,
+                     base::BindOnce(&RunDeleteOnIO, FROM_HERE,
+                                    GetServiceDeleteCallback()->callback())));
 }
 
 bool AppCacheQuotaClient::DoesSupport(StorageType type) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return type == StorageType::kTemporary;
 }
 
 void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) {
-  DCHECK(service_);
   if (quota_manager_is_destroyed_)
     return;
 
@@ -164,7 +199,7 @@
   DCHECK(!callback.is_null());
   DCHECK(!quota_manager_is_destroyed_);
 
-  if (!service_) {
+  if (service_is_destroyed_) {
     std::move(callback).Run(std::set<url::Origin>());
     return;
   }
@@ -181,12 +216,24 @@
     return;
   }
 
-  std::set<url::Origin> origins;
-  for (const auto& pair : GetUsageMap()) {
-    if (opt_host.empty() || pair.first.host() == opt_host)
-      origins.insert(pair.first);
-  }
-  std::move(callback).Run(origins);
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID()},
+      base::BindOnce(
+          [](base::WeakPtr<AppCacheServiceImpl> service,
+             const std::string& opt_host) {
+            std::set<url::Origin> origins;
+            if (!service)
+              return origins;
+
+            for (const auto& pair : service->storage()->usage_map()) {
+              if (opt_host.empty() || pair.first.host() == opt_host)
+                origins.insert(pair.first);
+            }
+            return origins;
+          },
+          service_, opt_host),
+      std::move(callback));
 }
 
 void AppCacheQuotaClient::ProcessPendingRequests() {
@@ -203,11 +250,6 @@
   pending_serial_requests_.clear();
 }
 
-const std::map<url::Origin, int64_t>& AppCacheQuotaClient::GetUsageMap() const {
-  DCHECK(service_);
-  return service_->storage()->usage_map();
-}
-
 net::CancelableCompletionRepeatingCallback*
 AppCacheQuotaClient::GetServiceDeleteCallback() {
   // Lazily created due to base::CancelableCallback's threading restrictions,
@@ -223,6 +265,7 @@
 }
 
 void AppCacheQuotaClient::NotifyAppCacheReady() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Can reoccur during reinitialization.
   if (!appcache_is_ready_) {
     appcache_is_ready_ = true;
@@ -231,7 +274,9 @@
 }
 
 void AppCacheQuotaClient::NotifyAppCacheDestroyed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   service_ = nullptr;
+  service_is_destroyed_ = true;
   while (!pending_batch_requests_.empty())
     RunFront(&pending_batch_requests_);
 
diff --git a/content/browser/appcache/appcache_quota_client.h b/content/browser/appcache/appcache_quota_client.h
index 39790d44..18580dd 100644
--- a/content/browser/appcache/appcache_quota_client.h
+++ b/content/browser/appcache/appcache_quota_client.h
@@ -12,6 +12,7 @@
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
 #include "content/browser/appcache/appcache_storage.h"
 #include "content/common/content_export.h"
 #include "net/base/completion_repeating_callback.h"
@@ -58,7 +59,7 @@
   friend class AppCacheStorageImpl;  // for NotifyAppCacheIsReady
 
   CONTENT_EXPORT
-      explicit AppCacheQuotaClient(AppCacheServiceImpl* service);
+  explicit AppCacheQuotaClient(base::WeakPtr<AppCacheServiceImpl> service);
 
   void DidDeleteAppCachesForOrigin(int rv);
   void GetOriginsHelper(blink::mojom::StorageType type,
@@ -66,7 +67,6 @@
                         GetOriginsCallback callback);
   void ProcessPendingRequests();
   void DeletePendingRequests();
-  const std::map<url::Origin, int64_t>& GetUsageMap() const;
   net::CancelableCompletionRepeatingCallback* GetServiceDeleteCallback();
 
   // For use by appcache internals during initialization and shutdown.
@@ -84,9 +84,11 @@
   std::unique_ptr<net::CancelableCompletionRepeatingCallback>
       service_delete_callback_;
 
-  AppCacheServiceImpl* service_;
-  bool appcache_is_ready_;
-  bool quota_manager_is_destroyed_;
+  base::WeakPtr<AppCacheServiceImpl> service_;
+  bool appcache_is_ready_ = false;
+  bool quota_manager_is_destroyed_ = false;
+  bool service_is_destroyed_ = false;
+  SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(AppCacheQuotaClient);
 };
diff --git a/content/browser/appcache/appcache_quota_client_unittest.cc b/content/browser/appcache/appcache_quota_client_unittest.cc
index 79de6c660..67bae98 100644
--- a/content/browser/appcache/appcache_quota_client_unittest.cc
+++ b/content/browser/appcache/appcache_quota_client_unittest.cc
@@ -9,9 +9,9 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
 #include "content/browser/appcache/appcache_quota_client.h"
 #include "content/browser/appcache/mock_appcache_service.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/net_errors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -115,7 +115,7 @@
   AppCacheQuotaClient* CreateClient() {
     // The bare operator new is used here because AppCacheQuotaClient deletes
     // itself when the QuotaManager goes out of scope.
-    return new AppCacheQuotaClient(&mock_service_);
+    return new AppCacheQuotaClient(mock_service_.AsWeakPtr());
   }
 
   void Call_NotifyAppCacheReady(AppCacheQuotaClient* client) {
@@ -146,7 +146,7 @@
     delete_status_ = status;
   }
 
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestBrowserThreadBundle thread_bundle_;
   int64_t usage_;
   std::set<url::Origin> origins_;
   blink::mojom::QuotaStatusCode delete_status_;
@@ -319,11 +319,10 @@
 
   // Pending requests should get serviced when the appcache is ready.
   Call_NotifyAppCacheReady(client);
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(2, num_get_origin_usage_completions_);
   EXPECT_EQ(4, num_get_origins_completions_);
-  EXPECT_EQ(0, num_delete_origins_completions_);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(3, num_delete_origins_completions_);  // deletes are really async
+  EXPECT_EQ(3, num_delete_origins_completions_);
 
   // They should be serviced in order requested.
   EXPECT_EQ(10, usage_);
diff --git a/content/browser/appcache/appcache_request_handler_unittest.cc b/content/browser/appcache/appcache_request_handler_unittest.cc
index 523cc245..4255f70 100644
--- a/content/browser/appcache/appcache_request_handler_unittest.cc
+++ b/content/browser/appcache/appcache_request_handler_unittest.cc
@@ -36,6 +36,7 @@
 #include "content/browser/appcache/mock_appcache_policy.h"
 #include "content/browser/appcache/mock_appcache_service.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/net_errors.h"
@@ -196,10 +197,14 @@
   AppCacheRequestHandlerTest()
       : host_(nullptr), request_(nullptr), request_handler_type_(GetParam()) {
     AppCacheRequestHandler::SetRunningInTests(true);
-    if (request_handler_type_ == URLLOADER)
-      feature_list_.InitAndEnableFeature(network::features::kNetworkService);
-    else
+    if (request_handler_type_ == URLLOADER) {
+      // TODO(http://crbug.com/824840): Enable NavigationLoaderOnUI for these
+      // tests.
+      feature_list_.InitWithFeatures({network::features::kNetworkService},
+                                     {features::kNavigationLoaderOnUI});
+    } else {
       feature_list_.InitAndDisableFeature(network::features::kNetworkService);
+    }
   }
 
   ~AppCacheRequestHandlerTest() {
diff --git a/content/browser/appcache/appcache_service_impl.cc b/content/browser/appcache/appcache_service_impl.cc
index 9f432ef..dcfc1a49 100644
--- a/content/browser/appcache/appcache_service_impl.cc
+++ b/content/browser/appcache/appcache_service_impl.cc
@@ -29,6 +29,7 @@
 #include "content/browser/appcache/appcache_response.h"
 #include "content/browser/appcache/appcache_storage_impl.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "net/base/io_buffer.h"
 #include "storage/browser/quota/special_storage_policy.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
@@ -387,7 +388,7 @@
     // The operator new is used here because this AppCacheQuotaClient instance
     // deletes itself after both the QuotaManager and the AppCacheService are
     // destroyed.
-    quota_client_ = new AppCacheQuotaClient(this);
+    quota_client_ = new AppCacheQuotaClient(AsWeakPtr());
     quota_manager_proxy_->RegisterClient(quota_client_);
   }
 }
@@ -400,8 +401,16 @@
   for (auto& helper : pending_helpers_)
     helper.first->Cancel();
   pending_helpers_.clear();
-  if (quota_client_)
-    quota_client_->NotifyAppCacheDestroyed();
+  if (quota_client_) {
+    if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+      quota_client_->NotifyAppCacheDestroyed();
+    } else {
+      base::PostTaskWithTraits(
+          FROM_HERE, {BrowserThread::IO},
+          base::BindOnce(&AppCacheQuotaClient::NotifyAppCacheDestroyed,
+                         base::Unretained(quota_client_)));
+    }
+  }
 
   // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
   // (special_storage_policy_).
@@ -409,6 +418,8 @@
 }
 
 void AppCacheServiceImpl::Initialize(const base::FilePath& cache_directory) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   DCHECK(!storage_.get());
   cache_directory_ = cache_directory;
   auto storage = std::make_unique<AppCacheStorageImpl>(this);
@@ -417,6 +428,8 @@
 }
 
 void AppCacheServiceImpl::ScheduleReinitialize() {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   if (reinit_timer_.IsRunning())
     return;
 
@@ -444,6 +457,8 @@
 }
 
 void AppCacheServiceImpl::Reinitialize() {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   AppCacheHistograms::CountReinitAttempt(!last_reinit_time_.is_null());
   last_reinit_time_ = base::Time::Now();
 
@@ -459,6 +474,8 @@
 
 void AppCacheServiceImpl::GetAllAppCacheInfo(AppCacheInfoCollection* collection,
                                              OnceCompletionCallback callback) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   DCHECK(collection);
   GetInfoHelper* helper =
       new GetInfoHelper(this, collection, std::move(callback));
@@ -468,6 +485,8 @@
 void AppCacheServiceImpl::DeleteAppCacheGroup(
     const GURL& manifest_url,
     net::CompletionOnceCallback callback) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   DeleteHelper* helper =
       new DeleteHelper(this, manifest_url, std::move(callback));
   helper->Start();
@@ -476,6 +495,8 @@
 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
     const url::Origin& origin,
     net::CompletionOnceCallback callback) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   DeleteOriginHelper* helper =
       new DeleteOriginHelper(this, origin, std::move(callback));
   helper->Start();
@@ -484,6 +505,8 @@
 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL& manifest_url,
                                                 int64_t cache_id,
                                                 int64_t response_id) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   CheckResponseHelper* helper = new CheckResponseHelper(
       this, manifest_url, cache_id, response_id);
   helper->Start();
@@ -491,27 +514,37 @@
 
 void AppCacheServiceImpl::set_special_storage_policy(
     storage::SpecialStoragePolicy* policy) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   special_storage_policy_ = policy;
 }
 
 void AppCacheServiceImpl::RegisterBackend(
     AppCacheBackendImpl* backend_impl) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
   backends_.insert({backend_impl->process_id(), backend_impl});
 }
 
 void AppCacheServiceImpl::UnregisterBackend(
     AppCacheBackendImpl* backend_impl) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   backends_.erase(backend_impl->process_id());
 }
 
 AppCacheHost* AppCacheServiceImpl::GetHost(
     const base::UnguessableToken& host_id) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   auto it = hosts_.find(host_id);
   return (it != hosts_.end()) ? (it->second.get()) : nullptr;
 }
 
 bool AppCacheServiceImpl::EraseHost(const base::UnguessableToken& host_id) {
+  DCHECK_CURRENTLY_ON(
+      NavigationURLLoaderImpl::GetLoaderRequestControllerThreadID());
   return (hosts_.erase(host_id) != 0);
 }
 
diff --git a/content/browser/appcache/appcache_service_unittest.cc b/content/browser/appcache/appcache_service_unittest.cc
index 9d57dc5..57c4ccc1d 100644
--- a/content/browser/appcache/appcache_service_unittest.cc
+++ b/content/browser/appcache/appcache_service_unittest.cc
@@ -14,11 +14,11 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
-#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/appcache/appcache_response.h"
 #include "content/browser/appcache/appcache_service_impl.h"
 #include "content/browser/appcache/mock_appcache_storage.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/io_buffer.h"
 #include "net/http/http_response_headers.h"
@@ -190,7 +190,7 @@
   const url::Origin kOrigin;
   const GURL kManifestUrl;
 
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<AppCacheServiceImpl> service_;
   int delete_result_;
   int delete_completion_count_;
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
index f881195..f8587cc 100644
--- a/content/browser/appcache/appcache_storage_impl.cc
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -20,6 +20,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_database.h"
@@ -29,6 +30,7 @@
 #include "content/browser/appcache/appcache_quota_client.h"
 #include "content/browser/appcache/appcache_response.h"
 #include "content/browser/appcache/appcache_service_impl.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "net/base/cache_type.h"
 #include "net/base/net_errors.h"
 #include "sql/database.h"
@@ -305,8 +307,17 @@
         kDelay);
   }
 
-  if (storage_->service()->quota_client())
-    storage_->service()->quota_client()->NotifyAppCacheReady();
+  if (storage_->service()->quota_client()) {
+    if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+      storage_->service()->quota_client()->NotifyAppCacheReady();
+    } else {
+      base::PostTaskWithTraits(
+          FROM_HERE, {BrowserThread::IO},
+          base::BindOnce(
+              &AppCacheQuotaClient::NotifyAppCacheReady,
+              base::Unretained(storage_->service()->quota_client())));
+    }
+  }
 }
 
 // DisableDatabaseTask -------
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index 1e2d927e..d25aa23f3 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -38,6 +38,7 @@
 #include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -289,6 +290,10 @@
     head.mime_type = "text/html";
     mock_url_loader_factory_.AddResponse(GetMockUrl("empty.html"), head, "",
                                          status);
+    // TODO(http://crbug.com/824840): Enable NavigationLoaderOnUI for these
+    // tests.
+    feature_list_.InitWithFeatures({network::features::kNetworkService},
+                                   {features::kNavigationLoaderOnUI});
   }
 
   template <class Method>
@@ -1565,10 +1570,6 @@
   }
 
   void Reinitialize(ReinitTestCase test_case) {
-    // These tests use the network service code path when simulating requests
-    // to app cache.
-    feature_list_.InitAndEnableFeature(network::features::kNetworkService);
-
     // Unlike all of the other tests, this one actually read/write files.
     ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
 
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index e48c457f..ecd6770 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/appcache/appcache_group.h"
@@ -31,6 +32,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -704,6 +706,9 @@
         tested_manifest_path_override_(nullptr),
         thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
         process_id_(123) {
+    // TODO(http://crbug.com/824840): Enable NavigationLoaderOnUI for these
+    // tests.
+    feature_list_.InitAndDisableFeature(features::kNavigationLoaderOnUI);
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::IO},
         base::BindOnce(&IOThread::Init, base::Unretained(io_thread_.get())));
@@ -3830,6 +3835,8 @@
     MANIFEST_WITH_INTERCEPT
   };
 
+  base::test::ScopedFeatureList feature_list_;
+
   // base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<IOThread> io_thread_;
 
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index b12cde95..baf22af 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -379,24 +379,28 @@
          options.min_interval == kMinIntervalForOneShotSync);
 
   if (GetBackgroundSyncType(options) == BackgroundSyncType::ONE_SHOT) {
+    auto id = op_scheduler_.CreateId();
     op_scheduler_.ScheduleOperation(
+        id, CacheStorageSchedulerMode::kExclusive,
         CacheStorageSchedulerOp::kBackgroundSync,
         base::BindOnce(
             &BackgroundSyncManager::RegisterCheckIfHasMainFrame,
             weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
             std::move(options),
-            op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+            op_scheduler_.WrapCallbackToRunNext(id, std::move(callback))));
   } else {
     // Periodic Background Sync events already have a pre-defined cadence which
     // the user agent decides. Don't block registration if there's no top level
     // frame at the time of registration.
+    auto id = op_scheduler_.CreateId();
     op_scheduler_.ScheduleOperation(
+        id, CacheStorageSchedulerMode::kExclusive,
         CacheStorageSchedulerOp::kBackgroundSync,
         base::BindOnce(
             &BackgroundSyncManager::RegisterImpl,
             weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
             std::move(options),
-            op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+            op_scheduler_.WrapCallbackToRunNext(id, std::move(callback))));
   }
 }
 
@@ -413,11 +417,14 @@
     return;
   }
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
-      base::BindOnce(&BackgroundSyncManager::UnregisterPeriodicSyncImpl,
-                     weak_ptr_factory_.GetWeakPtr(), sw_registration_id, tag,
-                     op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &BackgroundSyncManager::UnregisterPeriodicSyncImpl,
+          weak_ptr_factory_.GetWeakPtr(), sw_registration_id, tag,
+          op_scheduler_.WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void BackgroundSyncManager::DidResolveRegistration(
@@ -426,11 +433,13 @@
 
   if (disabled_)
     return;
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::DidResolveRegistrationImpl,
                      weak_ptr_factory_.GetWeakPtr(),
-                     std::move(registration_info)));
+                     std::move(registration_info), id));
 }
 
 void BackgroundSyncManager::GetOneShotSyncRegistrations(
@@ -462,12 +471,14 @@
     return;
   }
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
-      base::BindOnce(&BackgroundSyncManager::GetRegistrationsImpl,
-                     weak_ptr_factory_.GetWeakPtr(), sync_type,
-                     sw_registration_id,
-                     op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &BackgroundSyncManager::GetRegistrationsImpl,
+          weak_ptr_factory_.GetWeakPtr(), sync_type, sw_registration_id,
+          op_scheduler_.WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void BackgroundSyncManager::OnRegistrationDeleted(int64_t sw_registration_id,
@@ -477,11 +488,13 @@
   // Operations already in the queue will either fail when they write to storage
   // or return stale results based on registrations loaded in memory. This is
   // inconsequential since the service worker is gone.
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::OnRegistrationDeletedImpl,
                      weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
-                     MakeEmptyCompletion()));
+                     MakeEmptyCompletion(id)));
 }
 
 void BackgroundSyncManager::OnStorageWiped() {
@@ -490,19 +503,23 @@
   // Operations already in the queue will either fail when they write to storage
   // or return stale results based on registrations loaded in memory. This is
   // inconsequential since the service workers are gone.
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::OnStorageWipedImpl,
-                     weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
+                     weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion(id)));
 }
 
 void BackgroundSyncManager::SetMaxSyncAttemptsForTesting(int max_attempts) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::SetMaxSyncAttemptsImpl,
                      weak_ptr_factory_.GetWeakPtr(), max_attempts,
-                     MakeEmptyCompletion()));
+                     MakeEmptyCompletion(id)));
 }
 
 void BackgroundSyncManager::EmulateDispatchSyncEvent(
@@ -570,10 +587,12 @@
   DCHECK(!op_scheduler_.ScheduledOperations());
   DCHECK(!disabled_);
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::InitImpl,
-                     weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion()));
+                     weak_ptr_factory_.GetWeakPtr(), MakeEmptyCompletion(id)));
 }
 
 void BackgroundSyncManager::InitImpl(base::OnceClosure callback) {
@@ -1107,7 +1126,8 @@
 }
 
 void BackgroundSyncManager::DidResolveRegistrationImpl(
-    blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
+    blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
+    CacheStorageSchedulerId id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BackgroundSyncRegistration* registration =
@@ -1116,7 +1136,7 @@
     // There might not be a registration if the client ack's a registration that
     // was a duplicate in the first place and was already firing and finished by
     // the time the client acknowledged the second registration.
-    op_scheduler_.CompleteOperationAndRunNext();
+    op_scheduler_.CompleteOperationAndRunNext(id);
     return;
   }
 
@@ -1127,16 +1147,17 @@
                      service_worker_context_, std::move(*registration_info)),
       base::BindOnce(
           &BackgroundSyncManager::ResolveRegistrationDidCreateKeepAlive,
-          weak_ptr_factory_.GetWeakPtr()));
+          weak_ptr_factory_.GetWeakPtr(), id));
 }
 
 void BackgroundSyncManager::ResolveRegistrationDidCreateKeepAlive(
+    CacheStorageSchedulerId id,
     std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   FireReadyEvents(BackgroundSyncType::ONE_SHOT, base::DoNothing::Once(),
                   std::move(keepalive));
-  op_scheduler_.CompleteOperationAndRunNext();
+  op_scheduler_.CompleteOperationAndRunNext(id);
 }
 
 void BackgroundSyncManager::RemoveActiveRegistration(
@@ -1518,11 +1539,13 @@
   if (disabled_)
     return;
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
       base::BindOnce(&BackgroundSyncManager::ReviveOriginImpl,
                      weak_ptr_factory_.GetWeakPtr(), std::move(origin),
-                     MakeEmptyCompletion()));
+                     MakeEmptyCompletion(id)));
 }
 
 void BackgroundSyncManager::ReviveOriginImpl(url::Origin origin,
@@ -1657,12 +1680,15 @@
     std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
-      base::BindOnce(&BackgroundSyncManager::FireReadyEventsImpl,
-                     weak_ptr_factory_.GetWeakPtr(), sync_type,
-                     op_scheduler_.WrapCallbackToRunNext(std::move(callback)),
-                     std::move(keepalive)));
+      base::BindOnce(
+          &BackgroundSyncManager::FireReadyEventsImpl,
+          weak_ptr_factory_.GetWeakPtr(), sync_type,
+          op_scheduler_.WrapCallbackToRunNext(id, std::move(callback)),
+          std::move(keepalive)));
 }
 
 void BackgroundSyncManager::FireReadyEventsImpl(
@@ -1850,13 +1876,15 @@
                      registration_info->sync_type,
                      status_code == blink::ServiceWorkerStatusCode::kOk));
 
+  auto id = op_scheduler_.CreateId();
   op_scheduler_.ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kBackgroundSync,
-      base::BindOnce(&BackgroundSyncManager::EventCompleteImpl,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(registration_info), std::move(keepalive),
-                     status_code, origin,
-                     op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &BackgroundSyncManager::EventCompleteImpl,
+          weak_ptr_factory_.GetWeakPtr(), std::move(registration_info),
+          std::move(keepalive), status_code, origin,
+          op_scheduler_.WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void BackgroundSyncManager::EventCompleteImpl(
@@ -2084,9 +2112,10 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
-base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion() {
+base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion(
+    CacheStorageSchedulerId id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return op_scheduler_.WrapCallbackToRunNext(base::DoNothing::Once());
+  return op_scheduler_.WrapCallbackToRunNext(id, base::DoNothing::Once());
 }
 
 blink::ServiceWorkerStatusCode BackgroundSyncManager::CanEmulateSyncEvent(
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index 19f67f5..e3e2a85b 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -310,8 +310,10 @@
 
   // DidResolveRegistration callbacks
   void DidResolveRegistrationImpl(
-      blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info);
+      blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info,
+      CacheStorageSchedulerId id);
   void ResolveRegistrationDidCreateKeepAlive(
+      CacheStorageSchedulerId id,
       std::unique_ptr<BackgroundSyncEventKeepAlive> keepalive);
 
   // GetRegistrations callbacks
@@ -411,7 +413,7 @@
                                   blink::ServiceWorkerStatusCode status);
   void DidReceiveDelaysForSuspendedRegistrations(base::OnceClosure callback);
 
-  base::OnceClosure MakeEmptyCompletion();
+  base::OnceClosure MakeEmptyCompletion(CacheStorageSchedulerId id);
 
   blink::ServiceWorkerStatusCode CanEmulateSyncEvent(
       scoped_refptr<ServiceWorkerVersion> active_version);
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index ac8dd73a..5fbd75f6 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -531,6 +531,14 @@
     return;
   browser_context->was_notify_will_be_destroyed_called_ = true;
 
+  // Stop the ServiceManagerConnection from handling any new incoming requests
+  // before we tear anything down. This prevents races at shutdown.
+  BrowserContextServiceManagerConnectionHolder* connection_holder =
+      static_cast<BrowserContextServiceManagerConnectionHolder*>(
+          browser_context->GetUserData(kServiceManagerConnection));
+  if (connection_holder)
+    connection_holder->service_manager_connection()->Stop();
+
   // Subclasses of BrowserContext may expect there to be no more
   // RenderProcessHosts using them by the time this function returns. We
   // therefore explicitly tear down embedded Content Service instances now to
@@ -542,9 +550,6 @@
   // because it's possible for someone to call
   // |GetServiceManagerConnectionFor()| between now and actual BrowserContext
   // destruction.
-  BrowserContextServiceManagerConnectionHolder* connection_holder =
-      static_cast<BrowserContextServiceManagerConnectionHolder*>(
-          browser_context->GetUserData(kServiceManagerConnection));
   if (connection_holder)
     connection_holder->DestroyRunningServices();
 
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 2e2d4aa..59db93d 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -1594,7 +1594,7 @@
   EXPECT_FALSE(Delete(origin1_, "tmp"));  // Init storage.
   CacheStorageHandle cache_storage = CacheStorageForOrigin(origin1_);
   auto* impl = LegacyCacheStorage::From(cache_storage);
-  impl->StartAsyncOperationForTesting();
+  auto id = impl->StartAsyncOperationForTesting();
 
   base::RunLoop open_loop;
   cache_storage.value()->OpenCache(
@@ -1605,7 +1605,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(callback_cache_handle_.value());
 
-  impl->CompleteAsyncOperationForTesting();
+  impl->CompleteAsyncOperationForTesting(id);
   open_loop.Run();
   EXPECT_TRUE(callback_cache_handle_.value());
 }
diff --git a/content/browser/cache_storage/cache_storage_operation.cc b/content/browser/cache_storage/cache_storage_operation.cc
index d9084936..99cd3b5 100644
--- a/content/browser/cache_storage/cache_storage_operation.cc
+++ b/content/browser/cache_storage/cache_storage_operation.cc
@@ -14,12 +14,16 @@
 
 CacheStorageOperation::CacheStorageOperation(
     base::OnceClosure closure,
+    CacheStorageSchedulerId id,
     CacheStorageSchedulerClient client_type,
+    CacheStorageSchedulerMode mode,
     CacheStorageSchedulerOp op_type,
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : closure_(std::move(closure)),
       creation_ticks_(base::TimeTicks::Now()),
+      id_(id),
       client_type_(client_type),
+      mode_(mode),
       op_type_(op_type),
       task_runner_(std::move(task_runner)) {}
 
diff --git a/content/browser/cache_storage/cache_storage_operation.h b/content/browser/cache_storage/cache_storage_operation.h
index 84a66637..91e07fe6 100644
--- a/content/browser/cache_storage/cache_storage_operation.h
+++ b/content/browser/cache_storage/cache_storage_operation.h
@@ -21,7 +21,9 @@
 class CONTENT_EXPORT CacheStorageOperation {
  public:
   CacheStorageOperation(base::OnceClosure closure,
+                        CacheStorageSchedulerId id,
                         CacheStorageSchedulerClient client_type,
+                        CacheStorageSchedulerMode mode,
                         CacheStorageSchedulerOp op_type,
                         scoped_refptr<base::SequencedTaskRunner> task_runner);
 
@@ -31,6 +33,8 @@
   void Run();
 
   base::TimeTicks creation_ticks() const { return creation_ticks_; }
+  CacheStorageSchedulerId id() const { return id_; }
+  CacheStorageSchedulerMode mode() const { return mode_; }
   CacheStorageSchedulerOp op_type() const { return op_type_; }
   base::WeakPtr<CacheStorageOperation> AsWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
@@ -51,7 +55,9 @@
   // If the operation took a long time to run.
   bool was_slow_ = false;
 
+  const CacheStorageSchedulerId id_;
   const CacheStorageSchedulerClient client_type_;
+  const CacheStorageSchedulerMode mode_;
   const CacheStorageSchedulerOp op_type_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   base::WeakPtrFactory<CacheStorageOperation> weak_ptr_factory_{this};
diff --git a/content/browser/cache_storage/cache_storage_operation_unittest.cc b/content/browser/cache_storage/cache_storage_operation_unittest.cc
index b82da9b..b02f6cff 100644
--- a/content/browser/cache_storage/cache_storage_operation_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_operation_unittest.cc
@@ -39,7 +39,8 @@
       : mock_task_runner_(new base::TestMockTimeTaskRunner()) {
     operation_ = std::make_unique<CacheStorageOperation>(
         base::BindOnce(&TestTask::Run, base::Unretained(&task_)),
-        CacheStorageSchedulerClient::kStorage, CacheStorageSchedulerOp::kTest,
+        /* id = */ 0, CacheStorageSchedulerClient::kStorage,
+        CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kTest,
         mock_task_runner_);
   }
 
diff --git a/content/browser/cache_storage/cache_storage_scheduler.cc b/content/browser/cache_storage/cache_storage_scheduler.cc
index 9ea8838d..e7fc43d 100644
--- a/content/browser/cache_storage/cache_storage_scheduler.cc
+++ b/content/browser/cache_storage/cache_storage_scheduler.cc
@@ -7,62 +7,151 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "content/browser/cache_storage/cache_storage_histogram_utils.h"
 #include "content/browser/cache_storage/cache_storage_operation.h"
+#include "content/public/common/content_features.h"
 
 namespace content {
 
+namespace {
+
+const base::FeatureParam<int> kCacheStorageMaxSharedOps{
+    &features::kCacheStorageParallelOps, "max_shared_ops", 1};
+
+}  // namespace
+
 CacheStorageScheduler::CacheStorageScheduler(
     CacheStorageSchedulerClient client_type,
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : task_runner_(std::move(task_runner)), client_type_(client_type) {}
 
-CacheStorageScheduler::~CacheStorageScheduler() {}
+CacheStorageScheduler::~CacheStorageScheduler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
-void CacheStorageScheduler::ScheduleOperation(CacheStorageSchedulerOp op_type,
+CacheStorageSchedulerId CacheStorageScheduler::CreateId() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return next_id_++;
+}
+
+void CacheStorageScheduler::ScheduleOperation(CacheStorageSchedulerId id,
+                                              CacheStorageSchedulerMode mode,
+                                              CacheStorageSchedulerOp op_type,
                                               base::OnceClosure closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   RecordCacheStorageSchedulerUMA(CacheStorageSchedulerUMA::kQueueLength,
                                  client_type_, op_type,
                                  pending_operations_.size());
 
   pending_operations_.push_back(std::make_unique<CacheStorageOperation>(
-      std::move(closure), client_type_, op_type, task_runner_));
-  RunOperationIfIdle();
+      std::move(closure), id, client_type_, mode, op_type, task_runner_));
+  MaybeRunOperation();
 }
 
-void CacheStorageScheduler::CompleteOperationAndRunNext() {
-  DCHECK(running_operation_);
-  running_operation_.reset();
+void CacheStorageScheduler::CompleteOperationAndRunNext(
+    CacheStorageSchedulerId id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto it = running_operations_.find(id);
+  DCHECK(it != running_operations_.end());
+  DCHECK_EQ(it->second->id(), id);
 
-  RunOperationIfIdle();
+  if (it->second->mode() == CacheStorageSchedulerMode::kShared) {
+    DCHECK_EQ(num_running_exclusive_, 0);
+    DCHECK_GT(num_running_shared_, 0);
+    num_running_shared_ -= 1;
+    if (num_running_shared_ == 0) {
+      UMA_HISTOGRAM_COUNTS_1000("ServiceWorkerCache.PeakParallelSharedOps",
+                                peak_parallel_shared_);
+      peak_parallel_shared_ = 0;
+    }
+  } else {
+    DCHECK_EQ(num_running_shared_, 0);
+    DCHECK_EQ(num_running_exclusive_, 1);
+    num_running_exclusive_ -= 1;
+  }
+
+  running_operations_.erase(it);
+
+  MaybeRunOperation();
 }
 
 bool CacheStorageScheduler::ScheduledOperations() const {
-  return running_operation_ || !pending_operations_.empty();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return !running_operations_.empty() || !pending_operations_.empty();
+}
+
+bool CacheStorageScheduler::IsRunningExclusiveOperation() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return num_running_exclusive_ > 0;
 }
 
 void CacheStorageScheduler::DispatchOperationTask(base::OnceClosure task) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   task_runner_->PostTask(FROM_HERE, std::move(task));
 }
 
-void CacheStorageScheduler::RunOperationIfIdle() {
-  if (!running_operation_ && !pending_operations_.empty()) {
-    // TODO(jkarlin): Run multiple operations in parallel where allowed.
-    running_operation_ = std::move(pending_operations_.front());
-    pending_operations_.pop_front();
-
-    RecordCacheStorageSchedulerUMA(
-        CacheStorageSchedulerUMA::kQueueDuration, client_type_,
-        running_operation_->op_type(),
-        base::TimeTicks::Now() - running_operation_->creation_ticks());
-
-    DispatchOperationTask(base::BindOnce(&CacheStorageOperation::Run,
-                                         running_operation_->AsWeakPtr()));
+void CacheStorageScheduler::MaybeRunOperation() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // If there are no operations, then we can't run anything.
+  if (pending_operations_.empty()) {
+    DoneStartingAvailableOperations();
+    return;
   }
+
+  auto* next_operation = pending_operations_.front().get();
+
+  // Determine if we can run the next operation based on its mode
+  // and the current state of executing operations.  We allow multiple
+  // kShared operations to run in parallel, but a kExclusive operation
+  // must not overlap with any other operation.
+  if (next_operation->mode() == CacheStorageSchedulerMode::kShared) {
+    if (num_running_exclusive_ > 0 ||
+        num_running_shared_ >= kCacheStorageMaxSharedOps.Get()) {
+      DoneStartingAvailableOperations();
+      return;
+    }
+  } else if (num_running_shared_ > 0 || num_running_exclusive_ > 0) {
+    DCHECK_EQ(next_operation->mode(), CacheStorageSchedulerMode::kExclusive);
+    DoneStartingAvailableOperations();
+    return;
+  }
+
+  running_operations_.emplace(next_operation->id(),
+                              std::move(pending_operations_.front()));
+  pending_operations_.pop_front();
+
+  RecordCacheStorageSchedulerUMA(
+      CacheStorageSchedulerUMA::kQueueDuration, client_type_,
+      next_operation->op_type(),
+      base::TimeTicks::Now() - next_operation->creation_ticks());
+
+  if (next_operation->mode() == CacheStorageSchedulerMode::kShared) {
+    DCHECK_EQ(num_running_exclusive_, 0);
+    num_running_shared_ += 1;
+    peak_parallel_shared_ =
+        std::max(num_running_shared_, peak_parallel_shared_);
+  } else {
+    DCHECK_EQ(num_running_exclusive_, 0);
+    DCHECK_EQ(num_running_shared_, 0);
+    num_running_exclusive_ += 1;
+  }
+
+  DispatchOperationTask(
+      base::BindOnce(&CacheStorageOperation::Run, next_operation->AsWeakPtr()));
+
+  // If we just executed a kShared operation, then we may be able to schedule
+  // additional kShared parallel operations.  Recurse to process the next
+  // pending operation.
+  if (next_operation->mode() == CacheStorageSchedulerMode::kShared)
+    MaybeRunOperation();
+  else
+    DoneStartingAvailableOperations();
 }
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_scheduler.h b/content/browser/cache_storage/cache_storage_scheduler.h
index 09bbb71f..6bd9a57 100644
--- a/content/browser/cache_storage/cache_storage_scheduler.h
+++ b/content/browser/cache_storage/cache_storage_scheduler.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_H_
 
 #include <list>
+#include <map>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -19,7 +20,6 @@
 
 class CacheStorageOperation;
 
-// TODO(jkarlin): Support readers and writers so operations can run in parallel.
 // TODO(jkarlin): Support operation identification so that ops can be checked in
 // DCHECKs.
 
@@ -33,35 +33,56 @@
                         scoped_refptr<base::SequencedTaskRunner> task_runner);
   virtual ~CacheStorageScheduler();
 
-  // Adds the operation to the tail of the queue and starts it if the scheduler
-  // is idle.
-  void ScheduleOperation(CacheStorageSchedulerOp op_type,
+  // Create a scheduler-unique identifier for an operation to be scheduled.
+  // This value must be passed to the ScheduleOperation(),
+  // CompleteOperationAndRunNext(), and WrapCallbackToRunNext() methods.
+  CacheStorageSchedulerId CreateId();
+
+  // Adds the operation to the tail of the queue and starts it if possible.
+  // A unique identifier must be provided via the CreateId() method.  The
+  // mode determines whether the operation should run exclusively by itself
+  // or can safely run in parallel with other shared operations.
+  void ScheduleOperation(CacheStorageSchedulerId id,
+                         CacheStorageSchedulerMode mode,
+                         CacheStorageSchedulerOp op_type,
                          base::OnceClosure closure);
 
-  // Call this after each operation completes. It cleans up the current
-  // operation and starts the next.
-  void CompleteOperationAndRunNext();
+  // Call this after each operation completes. It cleans up the operation
+  // associated with the given id.  If may also start the next set of
+  // operations.
+  void CompleteOperationAndRunNext(CacheStorageSchedulerId id);
 
   // Returns true if there are any running or pending operations.
   bool ScheduledOperations() const;
 
+  // Returns true if the scheduler is currently running an exclusive operation.
+  bool IsRunningExclusiveOperation() const;
+
   // Wraps |callback| to also call CompleteOperationAndRunNext.
   template <typename... Args>
   base::OnceCallback<void(Args...)> WrapCallbackToRunNext(
+      CacheStorageSchedulerId id,
       base::OnceCallback<void(Args...)> callback) {
     return base::BindOnce(&CacheStorageScheduler::RunNextContinuation<Args...>,
-                          weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+                          weak_ptr_factory_.GetWeakPtr(), id,
+                          std::move(callback));
   }
 
  protected:
   // virtual for testing
   virtual void DispatchOperationTask(base::OnceClosure task);
 
+  // virtual for testing
+  virtual void DoneStartingAvailableOperations() {}
+
  private:
-  void RunOperationIfIdle();
+  // Maybe start running the next operation depending on the current
+  // set of running operations and the mode of the next operation.
+  void MaybeRunOperation();
 
   template <typename... Args>
-  void RunNextContinuation(base::OnceCallback<void(Args...)> callback,
+  void RunNextContinuation(CacheStorageSchedulerId id,
+                           base::OnceCallback<void(Args...)> callback,
                            Args... args) {
     // Grab a weak ptr to guard against the scheduler being deleted during the
     // callback.
@@ -70,14 +91,26 @@
 
     std::move(callback).Run(std::forward<Args>(args)...);
     if (scheduler)
-      CompleteOperationAndRunNext();
+      CompleteOperationAndRunNext(id);
   }
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   std::list<std::unique_ptr<CacheStorageOperation>> pending_operations_;
-  std::unique_ptr<CacheStorageOperation> running_operation_;
+  std::map<CacheStorageSchedulerId, std::unique_ptr<CacheStorageOperation>>
+      running_operations_;
   const CacheStorageSchedulerClient client_type_;
+  CacheStorageSchedulerId next_id_ = 0;
 
+  // Number of shared/exclusive operations currently running.
+  int num_running_shared_ = 0;
+  int num_running_exclusive_ = 0;
+
+  // The peak number of parallel shared operations that ran at once.  Measured
+  // between the last time the sheduler started running shared operations and
+  // when the number of running shared operations drops to zero.
+  int peak_parallel_shared_ = 0;
+
+  SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<CacheStorageScheduler> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(CacheStorageScheduler);
diff --git a/content/browser/cache_storage/cache_storage_scheduler_types.h b/content/browser/cache_storage/cache_storage_scheduler_types.h
index 6d562f2d..45f9245 100644
--- a/content/browser/cache_storage/cache_storage_scheduler_types.h
+++ b/content/browser/cache_storage/cache_storage_scheduler_types.h
@@ -5,8 +5,12 @@
 #ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_TYPES_H_
 #define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_TYPES_H_
 
+#include <stdint.h>
+
 namespace content {
 
+using CacheStorageSchedulerId = int64_t;
+
 // Define the types of clients that might own a scheduler.  This enum is used
 // to populate histogram names and must be kept in sync with the function
 // in cache_storage_histogram_utils.cc.  Please keep this list sorted.  It is
@@ -18,6 +22,11 @@
   kStorage = 2,
 };
 
+enum class CacheStorageSchedulerMode {
+  kExclusive,
+  kShared,
+};
+
 // Define the different types of operations that can be scheduled.  This enum
 // is used to populate histogram names and must be kept in sync with the
 // function in cache_storage_histogram_utils.cc.  Please keep this list sorted.
diff --git a/content/browser/cache_storage/cache_storage_scheduler_unittest.cc b/content/browser/cache_storage/cache_storage_scheduler_unittest.cc
index 3afaf4c9..042f61f8 100644
--- a/content/browser/cache_storage/cache_storage_scheduler_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_scheduler_unittest.cc
@@ -7,7 +7,9 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,70 +19,470 @@
 class TestTask {
  public:
   TestTask(CacheStorageScheduler* scheduler)
-      : scheduler_(scheduler), callback_count_(0) {}
+      : scheduler_(scheduler),
+        id_(scheduler_->CreateId()),
+        callback_count_(0) {}
 
-  virtual void Run() { callback_count_++; }
-  void Done() { scheduler_->CompleteOperationAndRunNext(); }
+  virtual void Run() {
+    callback_count_++;
+    run_loop_.Quit();
+  }
+  void Done() { scheduler_->CompleteOperationAndRunNext(id_); }
 
   int callback_count() const { return callback_count_; }
+  CacheStorageSchedulerId id() const { return id_; }
+  base::RunLoop& run_loop() { return run_loop_; }
 
  protected:
   CacheStorageScheduler* scheduler_;
+  const CacheStorageSchedulerId id_;
+  base::RunLoop run_loop_;
   int callback_count_;
 };
 
+class TestScheduler : public CacheStorageScheduler {
+ public:
+  TestScheduler()
+      : CacheStorageScheduler(CacheStorageSchedulerClient::kStorage,
+                              base::ThreadTaskRunnerHandle::Get()) {}
+
+  void SetDoneStartingClosure(base::OnceClosure done_closure) {
+    CHECK(!done_closure_);
+    done_closure_ = std::move(done_closure);
+  }
+
+ protected:
+  void DoneStartingAvailableOperations() override {
+    if (done_closure_) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                    std::move(done_closure_));
+    }
+    CacheStorageScheduler::DoneStartingAvailableOperations();
+  }
+
+  base::OnceClosure done_closure_;
+};
+
 class CacheStorageSchedulerTest : public testing::Test {
  protected:
   CacheStorageSchedulerTest()
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
-        scheduler_(CacheStorageSchedulerClient::kStorage,
-                   base::ThreadTaskRunnerHandle::Get()),
-        task1_(TestTask(&scheduler_)),
-        task2_(TestTask(&scheduler_)) {}
+        task1_(&scheduler_),
+        task2_(&scheduler_),
+        task3_(&scheduler_) {}
 
   TestBrowserThreadBundle browser_thread_bundle_;
-  CacheStorageScheduler scheduler_;
+  TestScheduler scheduler_;
   TestTask task1_;
   TestTask task2_;
+  TestTask task3_;
 };
 
 TEST_F(CacheStorageSchedulerTest, ScheduleOne) {
+  base::RunLoop done_loop;
+  scheduler_.SetDoneStartingClosure(done_loop.QuitClosure());
   scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kTest,
       base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
-  base::RunLoop().RunUntilIdle();
+  task1_.run_loop().Run();
+  done_loop.Run();
   EXPECT_EQ(1, task1_.callback_count());
 }
 
-TEST_F(CacheStorageSchedulerTest, ScheduleTwo) {
+TEST_F(CacheStorageSchedulerTest, ScheduledOperations) {
+  base::RunLoop done_loop;
+  scheduler_.SetDoneStartingClosure(done_loop.QuitClosure());
   scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kTest,
       base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task1_.run_loop().Run();
+  done_loop.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+  task1_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleTwoExclusive) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
   scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kTest,
       base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
-  base::RunLoop().RunUntilIdle();
+
+  // Should only run the first exclusive op.
+  task1_.run_loop().Run();
+  done_loop1.Run();
   EXPECT_EQ(1, task1_.callback_count());
   EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
 
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run the second exclusive op after the first completes.
   task1_.Done();
   EXPECT_TRUE(scheduler_.ScheduledOperations());
-  base::RunLoop().RunUntilIdle();
+  task2_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleTwoShared) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+
+  // Should run both shared ops in paralle.
+  task1_.run_loop().Run();
+  task2_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Completing the first op should trigger a check for new ops
+  // which will not be present here.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop3;
+  scheduler_.SetDoneStartingClosure(done_loop3.QuitClosure());
+
+  // Completing the second op should result in the scheduler
+  // becoming idle.
+  task2_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+  done_loop3.Run();
   EXPECT_EQ(1, task1_.callback_count());
   EXPECT_EQ(1, task2_.callback_count());
 }
 
-TEST_F(CacheStorageSchedulerTest, ScheduledOperations) {
+TEST_F(CacheStorageSchedulerTest, ScheduleOneExclusiveOneShared) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
   scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kTest,
       base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
-  EXPECT_TRUE(scheduler_.ScheduledOperations());
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+
+  // Should only run the first exclusive op.
+  task1_.run_loop().Run();
+  done_loop1.Run();
   EXPECT_EQ(1, task1_.callback_count());
-  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run the second shared op after the first is completed.
   task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task2_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  task2_.Done();
   EXPECT_FALSE(scheduler_.ScheduledOperations());
 }
 
+TEST_F(CacheStorageSchedulerTest, ScheduleOneSharedOneExclusive) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+
+  // Should only run the first shared op.
+  task1_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run the second exclusive op after the first completes.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task2_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+
+  task2_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleTwoSharedOneExclusive) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task3_.id(), CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task3_)));
+
+  // Should run the two shared ops in parallel.
+  task1_.run_loop().Run();
+  task2_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(0, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Completing the first shared op should not allow the exclusive op
+  // to run yet.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(0, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop3;
+  scheduler_.SetDoneStartingClosure(done_loop3.QuitClosure());
+
+  // The third exclusive op should run after both the preceding shared ops
+  // complete.
+  task2_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task3_.run_loop().Run();
+  done_loop3.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(1, task3_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+
+  task3_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleOneExclusiveTwoShared) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task3_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task3_)));
+
+  // Should only run the first exclusive op.
+  task1_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_EQ(0, task3_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run both the shared ops in parallel after the first exclusive
+  // op is completed.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task2_.run_loop().Run();
+  task3_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(1, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop3;
+  scheduler_.SetDoneStartingClosure(done_loop3.QuitClosure());
+
+  task2_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  done_loop3.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(1, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  task3_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleOneSharedOneExclusiveOneShared) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "3"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task3_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task3_)));
+
+  // Should only run the first shared op.
+  task1_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_EQ(0, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run the exclusive op after the first op is completed.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task2_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(0, task3_.callback_count());
+  EXPECT_TRUE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop3;
+  scheduler_.SetDoneStartingClosure(done_loop3.QuitClosure());
+
+  // Should run the last shared op after the preceding exclusive op
+  // is completed.
+  task2_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task3_.run_loop().Run();
+  done_loop3.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_EQ(1, task3_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  task3_.Done();
+  EXPECT_FALSE(scheduler_.ScheduledOperations());
+}
+
+TEST_F(CacheStorageSchedulerTest, ScheduleTwoSharedNotParallel) {
+  // Disable parallelism
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kCacheStorageParallelOps, {{"max_shared_ops", "1"}});
+
+  scheduler_.ScheduleOperation(
+      task1_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task1_)));
+  base::RunLoop done_loop1;
+  scheduler_.SetDoneStartingClosure(done_loop1.QuitClosure());
+  scheduler_.ScheduleOperation(
+      task2_.id(), CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kTest,
+      base::BindOnce(&TestTask::Run, base::Unretained(&task2_)));
+
+  // Should only run one shared op since the max shared is set to 1.
+  task1_.run_loop().Run();
+  done_loop1.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(0, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+
+  base::RunLoop done_loop2;
+  scheduler_.SetDoneStartingClosure(done_loop2.QuitClosure());
+
+  // Should run the next shared op after the first completes.
+  task1_.Done();
+  EXPECT_TRUE(scheduler_.ScheduledOperations());
+  task2_.run_loop().Run();
+  done_loop2.Run();
+  EXPECT_EQ(1, task1_.callback_count());
+  EXPECT_EQ(1, task2_.callback_count());
+  EXPECT_FALSE(scheduler_.IsRunningExclusiveOperation());
+}
+
 }  // namespace cache_storage_scheduler_unittest
 }  // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.cc b/content/browser/cache_storage/legacy/legacy_cache_storage.cc
index b30684d..a064cef4 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage.cc
@@ -619,11 +619,13 @@
   //       operations to better support use by internal code that may
   //       start a single operation without explicitly maintaining a
   //       handle.
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kOpen,
-      base::BindOnce(&LegacyCacheStorage::OpenCacheImpl,
-                     weak_factory_.GetWeakPtr(), cache_name, trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kOpen,
+      base::BindOnce(
+          &LegacyCacheStorage::OpenCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::HasCache(const std::string& cache_name,
@@ -638,11 +640,13 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kHas,
-      base::BindOnce(&LegacyCacheStorage::HasCacheImpl,
-                     weak_factory_.GetWeakPtr(), cache_name, trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kHas,
+      base::BindOnce(
+          &LegacyCacheStorage::HasCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::DoomCache(const std::string& cache_name,
@@ -657,11 +661,14 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kDelete,
-      base::BindOnce(&LegacyCacheStorage::DoomCacheImpl,
-                     weak_factory_.GetWeakPtr(), cache_name, trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorage::DoomCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::EnumerateCaches(int64_t trace_id,
@@ -675,11 +682,13 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kKeys,
-      base::BindOnce(&LegacyCacheStorage::EnumerateCachesImpl,
-                     weak_factory_.GetWeakPtr(), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kKeys,
+      base::BindOnce(
+          &LegacyCacheStorage::EnumerateCachesImpl, weak_factory_.GetWeakPtr(),
+          trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::MatchCache(
@@ -697,12 +706,13 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kMatch,
-      base::BindOnce(&LegacyCacheStorage::MatchCacheImpl,
-                     weak_factory_.GetWeakPtr(), cache_name, std::move(request),
-                     std::move(match_options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kMatch,
+      base::BindOnce(
+          &LegacyCacheStorage::MatchCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, std::move(request), std::move(match_options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::MatchAllCaches(
@@ -719,12 +729,14 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
       CacheStorageSchedulerOp::kMatchAll,
-      base::BindOnce(&LegacyCacheStorage::MatchAllCachesImpl,
-                     weak_factory_.GetWeakPtr(), std::move(request),
-                     std::move(match_options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorage::MatchAllCachesImpl, weak_factory_.GetWeakPtr(),
+          std::move(request), std::move(match_options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::WriteToCache(
@@ -742,12 +754,16 @@
       CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_,
       StorageType::kTemporary);
 
+  // Note, this is a shared operation since it only reads CacheStorage data.
+  // The CacheStorageCache is responsible for making its put operation
+  // exclusive.
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kPut,
-      base::BindOnce(&LegacyCacheStorage::WriteToCacheImpl,
-                     weak_factory_.GetWeakPtr(), cache_name, std::move(request),
-                     std::move(response), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kPut,
+      base::BindOnce(
+          &LegacyCacheStorage::WriteToCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, std::move(request), std::move(response), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::GetSizeThenCloseAllCaches(SizeCallback callback) {
@@ -756,11 +772,17 @@
   if (!initialized_)
     LazyInit();
 
+  // Note, this is a shared operation since it only reads CacheStorage data.
+  // The CacheStorageCache is responsible for making its close operation
+  // exclusive.
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
       CacheStorageSchedulerOp::kSizeThenClose,
-      base::BindOnce(&LegacyCacheStorage::GetSizeThenCloseAllCachesImpl,
-                     weak_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorage::GetSizeThenCloseAllCachesImpl,
+          weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::Size(LegacyCacheStorage::SizeCallback callback) {
@@ -769,10 +791,12 @@
   if (!initialized_)
     LazyInit();
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kSize,
-      base::BindOnce(&LegacyCacheStorage::SizeImpl, weak_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kSize,
+      base::BindOnce(
+          &LegacyCacheStorage::SizeImpl, weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::ResetManager() {
@@ -798,16 +822,19 @@
 
 void LegacyCacheStorage::WriteIndex(base::OnceCallback<void(bool)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kWriteIndex,
-      base::BindOnce(&LegacyCacheStorage::WriteIndexImpl,
-                     weak_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorage::WriteIndexImpl, weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorage::WriteIndexImpl(
     base::OnceCallback<void(bool)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   cache_loader_->WriteIndex(*cache_index_, std::move(callback));
 }
 
@@ -871,13 +898,17 @@
   cache_map_it->second.reset();
 }
 
-void LegacyCacheStorage::StartAsyncOperationForTesting() {
-  scheduler_->ScheduleOperation(CacheStorageSchedulerOp::kTest,
+CacheStorageSchedulerId LegacyCacheStorage::StartAsyncOperationForTesting() {
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(id, CacheStorageSchedulerMode::kExclusive,
+                                CacheStorageSchedulerOp::kTest,
                                 base::DoNothing());
+  return id;
 }
 
-void LegacyCacheStorage::CompleteAsyncOperationForTesting() {
-  scheduler_->CompleteOperationAndRunNext();
+void LegacyCacheStorage::CompleteAsyncOperationForTesting(
+    CacheStorageSchedulerId id) {
+  scheduler_->CompleteOperationAndRunNext(id);
 }
 
 // Init is run lazily so that it is called on the proper MessageLoop.
@@ -891,7 +922,9 @@
   DCHECK(!scheduler_->ScheduledOperations());
 
   initializing_ = true;
+  init_id_ = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      init_id_, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kInit,
       base::BindOnce(&LegacyCacheStorage::LazyInitImpl,
                      weak_factory_.GetWeakPtr()));
@@ -907,6 +940,7 @@
   // 3. Once each load is complete, update the map variables.
   // 4. Call the list of waiting callbacks.
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   cache_loader_->LoadIndex(base::BindOnce(
       &LegacyCacheStorage::LazyInitDidLoadIndex, weak_factory_.GetWeakPtr()));
 }
@@ -926,7 +960,7 @@
   initializing_ = false;
   initialized_ = true;
 
-  scheduler_->CompleteOperationAndRunNext();
+  scheduler_->CompleteOperationAndRunNext(init_id_);
 }
 
 void LegacyCacheStorage::OpenCacheImpl(const std::string& cache_name,
@@ -943,6 +977,7 @@
     return;
   }
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   cache_loader_->PrepareNewCacheDestination(
       cache_name, base::BindOnce(&LegacyCacheStorage::CreateCacheDidCreateCache,
                                  weak_factory_.GetWeakPtr(), cache_name,
@@ -1034,6 +1069,7 @@
     return;
   }
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   LegacyCacheStorageCache::From(cache_handle)->SetObserver(nullptr);
   cache_index_->DoomCache(cache_name);
   cache_loader_->WriteIndex(
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.h b/content/browser/cache_storage/legacy/legacy_cache_storage.h
index f0985a7..14ae5b6 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage.h
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage_cache_observer.h"
+#include "content/browser/cache_storage/cache_storage_scheduler_types.h"
 #include "content/browser/cache_storage/legacy/legacy_cache_storage_cache.h"
 
 namespace base {
@@ -119,8 +120,8 @@
 
   // The functions below are for tests to verify that the operations run
   // serially.
-  void StartAsyncOperationForTesting();
-  void CompleteAsyncOperationForTesting();
+  CacheStorageSchedulerId StartAsyncOperationForTesting();
+  void CompleteAsyncOperationForTesting(CacheStorageSchedulerId id);
 
   // Removes the manager reference. Called before this storage is deleted by the
   // manager, since it is removed from manager's storage map before deleting.
@@ -295,6 +296,8 @@
   // The owner that this CacheStorage is associated with.
   CacheStorageOwner owner_;
 
+  CacheStorageSchedulerId init_id_ = -1;
+
   // The manager that owns this cache storage. Only set to null by
   // RemoveManager() when this cache storage is being deleted.
   LegacyCacheStorageManager* cache_storage_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 5eb3460a1..7e2a5d6 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -540,12 +540,13 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kMatch,
-      base::BindOnce(&LegacyCacheStorageCache::MatchImpl,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     std::move(match_options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kMatch,
+      base::BindOnce(
+          &LegacyCacheStorageCache::MatchImpl, weak_ptr_factory_.GetWeakPtr(),
+          std::move(request), std::move(match_options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::MatchAll(
@@ -560,12 +561,15 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
       CacheStorageSchedulerOp::kMatchAll,
-      base::BindOnce(&LegacyCacheStorageCache::MatchAllImpl,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     std::move(match_options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorageCache::MatchAllImpl,
+          weak_ptr_factory_.GetWeakPtr(), std::move(request),
+          std::move(match_options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::WriteSideData(ErrorCallback callback,
@@ -825,23 +829,26 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kKeys,
-      base::BindOnce(&LegacyCacheStorageCache::KeysImpl,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     std::move(options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kKeys,
+      base::BindOnce(
+          &LegacyCacheStorageCache::KeysImpl, weak_ptr_factory_.GetWeakPtr(),
+          std::move(request), std::move(options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::Close(base::OnceClosure callback) {
   DCHECK_NE(BACKEND_CLOSED, backend_state_)
       << "Was LegacyCacheStorageCache::Close() called twice?";
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kClose,
-      base::BindOnce(&LegacyCacheStorageCache::CloseImpl,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorageCache::CloseImpl, weak_ptr_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::Size(SizeCallback callback) {
@@ -852,11 +859,12 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kSize,
-      base::BindOnce(&LegacyCacheStorageCache::SizeImpl,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kSize,
+      base::BindOnce(
+          &LegacyCacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::GetSizeThenClose(SizeCallback callback) {
@@ -866,14 +874,16 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kSizeThenClose,
       base::BindOnce(
           &LegacyCacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(
               &LegacyCacheStorageCache::GetSizeThenCloseDidGetSize,
               weak_ptr_factory_.GetWeakPtr(),
-              scheduler_->WrapCallbackToRunNext(std::move(callback)))));
+              scheduler_->WrapCallbackToRunNext(id, std::move(callback)))));
 }
 
 void LegacyCacheStorageCache::SetObserver(CacheStorageCacheObserver* observer) {
@@ -1358,11 +1368,13 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kWriteSideData,
       base::BindOnce(&LegacyCacheStorageCache::WriteSideDataImpl,
                      weak_ptr_factory_.GetWeakPtr(),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback)),
+                     scheduler_->WrapCallbackToRunNext(id, std::move(callback)),
                      url, expected_response_time, trace_id, buffer, buf_len));
 }
 
@@ -1477,6 +1489,7 @@
           weak_ptr_factory_.GetWeakPtr(), std::move(callback), std::move(entry),
           buf_len, std::move(response), side_data_size_before_write, trace_id));
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   int rv = temp_entry_ptr->WriteData(
       INDEX_SIDE_DATA, 0 /* offset */, buffer.get(), buf_len,
       write_side_data_callback, true /* truncate */);
@@ -1560,11 +1573,12 @@
 
   auto put_context = cache_entry_handler_->CreatePutContext(
       std::move(request), std::move(response), trace_id);
+  auto id = scheduler_->CreateId();
   put_context->callback =
-      scheduler_->WrapCallbackToRunNext(std::move(callback));
+      scheduler_->WrapCallbackToRunNext(id, std::move(callback));
 
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kPut,
+      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kPut,
       base::BindOnce(&LegacyCacheStorageCache::PutImpl,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context)));
 }
@@ -1645,6 +1659,7 @@
                          weak_ptr_factory_.GetWeakPtr(),
                          std::move(scoped_entry_ptr), std::move(put_context)));
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   int rv = backend_ptr->CreateEntry(request_.url.spec(), net::HIGHEST,
                                     entry_ptr, create_entry_callback);
 
@@ -1729,6 +1744,7 @@
                          weak_ptr_factory_.GetWeakPtr(), std::move(put_context),
                          buffer->size()));
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   rv = temp_entry_ptr->WriteData(INDEX_HEADERS, 0 /* offset */, buffer.get(),
                                  buffer->size(), write_headers_callback,
                                  true /* truncate */);
@@ -1977,12 +1993,15 @@
     return;
   }
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
       CacheStorageSchedulerOp::kGetAllMatched,
-      base::BindOnce(&LegacyCacheStorageCache::GetAllMatchedEntriesImpl,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     std::move(options), trace_id,
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorageCache::GetAllMatchedEntriesImpl,
+          weak_ptr_factory_.GetWeakPtr(), std::move(request),
+          std::move(options), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::GetAllMatchedEntriesImpl(
@@ -2060,12 +2079,14 @@
   request->referrer = operation->request->referrer.Clone();
   request->headers = operation->request->headers;
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kDelete,
-      base::BindOnce(&LegacyCacheStorageCache::DeleteImpl,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     std::move(operation->match_options),
-                     scheduler_->WrapCallbackToRunNext(std::move(callback))));
+      base::BindOnce(
+          &LegacyCacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(),
+          std::move(request), std::move(operation->match_options),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
 void LegacyCacheStorageCache::DeleteImpl(
@@ -2104,6 +2125,8 @@
     return;
   }
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
+
   for (auto& result : *query_cache_results) {
     disk_cache::ScopedEntryPtr entry = std::move(result.entry);
     if (ShouldPadResourceSize(*result.response)) {
@@ -2171,6 +2194,7 @@
 void LegacyCacheStorageCache::CloseImpl(base::OnceClosure callback) {
   DCHECK_EQ(BACKEND_OPEN, backend_state_);
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   backend_.reset();
   post_backend_closed_callback_ = std::move(callback);
 }
@@ -2229,6 +2253,7 @@
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                          std::move(backend_ptr)));
 
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
   int rv = disk_cache::CreateCacheBackend(
       cache_type, net::CACHE_BACKEND_SIMPLE, path_, max_bytes,
       false, /* force */
@@ -2261,15 +2286,16 @@
   DCHECK(!scheduler_->ScheduledOperations());
   initializing_ = true;
 
+  auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
-      CacheStorageSchedulerOp::kInit,
+      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kInit,
       base::BindOnce(
           &LegacyCacheStorageCache::CreateBackend,
           weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(
               &LegacyCacheStorageCache::InitDidCreateBackend,
               weak_ptr_factory_.GetWeakPtr(),
-              scheduler_->WrapCallbackToRunNext(base::DoNothing::Once()))));
+              scheduler_->WrapCallbackToRunNext(id, base::DoNothing::Once()))));
 }
 
 void LegacyCacheStorageCache::InitDidCreateBackend(
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
index 2d6b24c..537640fe 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
@@ -167,7 +167,16 @@
     auto origin_path = LegacyCacheStorageManager::ConstructOriginPath(
         root_path, origin, owner);
     if (path != origin_path) {
-      RecordIndexValidationResult(IndexResult::kPathMismatch);
+      CacheStorageOwner other_owner = owner == CacheStorageOwner::kCacheAPI
+                                          ? CacheStorageOwner::kBackgroundFetch
+                                          : CacheStorageOwner::kCacheAPI;
+      auto other_owner_path = LegacyCacheStorageManager::ConstructOriginPath(
+          root_path, origin, other_owner);
+      // Some of the paths in the |root_path| directory are for a different
+      // |owner|.  That is valid and expected, but if the path doesn't match
+      // the calculated path for either |owner|, then it is invalid.
+      if (path != other_owner_path)
+        RecordIndexValidationResult(IndexResult::kPathMismatch);
       continue;
     }
 
diff --git a/content/browser/content_index/content_index_context_impl.cc b/content/browser/content_index/content_index_context_impl.cc
index ffdff6a..65353e61 100644
--- a/content/browser/content_index/content_index_context_impl.cc
+++ b/content/browser/content_index/content_index_context_impl.cc
@@ -133,13 +133,27 @@
     blink::ServiceWorkerStatusCode start_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  // Don't allow DB operations while the `contentdelete` event is firing.
+  // This is to prevent re-registering the deleted content within the event.
+  content_index_database_.BlockOrigin(service_worker->script_origin());
+
   int request_id = service_worker->StartRequest(
-      ServiceWorkerMetrics::EventType::CONTENT_DELETE, base::DoNothing());
+      ServiceWorkerMetrics::EventType::CONTENT_DELETE,
+      base::BindOnce(&ContentIndexContextImpl::DidDispatchEvent, this,
+                     service_worker->script_origin()));
 
   service_worker->endpoint()->DispatchContentDeleteEvent(
       description_id, service_worker->CreateSimpleEventCallback(request_id));
 }
 
+void ContentIndexContextImpl::DidDispatchEvent(
+    const url::Origin& origin,
+    blink::ServiceWorkerStatusCode service_worker_status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  content_index_database_.UnblockOrigin(origin);
+}
+
 void ContentIndexContextImpl::Shutdown() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   content_index_database_.Shutdown();
diff --git a/content/browser/content_index/content_index_context_impl.h b/content/browser/content_index/content_index_context_impl.h
index c746e65..6cc780b3 100644
--- a/content/browser/content_index/content_index_context_impl.h
+++ b/content/browser/content_index/content_index_context_impl.h
@@ -69,6 +69,9 @@
       const std::string& description_id,
       blink::ServiceWorkerStatusCode start_worker_status);
 
+  void DidDispatchEvent(const url::Origin& origin,
+                        blink::ServiceWorkerStatusCode service_worker_status);
+
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
   ContentIndexDatabase content_index_database_;
 
diff --git a/content/browser/content_index/content_index_database.cc b/content/browser/content_index/content_index_database.cc
index b125d300..cdbcb57 100644
--- a/content/browser/content_index/content_index_database.cc
+++ b/content/browser/content_index/content_index_database.cc
@@ -119,6 +119,12 @@
     const SkBitmap& icon,
     const GURL& launch_url,
     blink::mojom::ContentIndexService::AddCallback callback) {
+  if (blocked_origins_.count(origin)) {
+    // TODO(crbug.com/973844): Does this need a more specific error?
+    std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR);
+    return;
+  }
+
   SerializeIcon(icon, base::BindOnce(&ContentIndexDatabase::DidSerializeIcon,
                                      weak_ptr_factory_io_.GetWeakPtr(),
                                      service_worker_registration_id, origin,
@@ -356,6 +362,19 @@
       EntryFromSerializedProto(service_worker_registration_id, data.front()));
 }
 
+void ContentIndexDatabase::BlockOrigin(const url::Origin& origin) {
+  blocked_origins_[origin]++;
+}
+
+void ContentIndexDatabase::UnblockOrigin(const url::Origin& origin) {
+  DCHECK(blocked_origins_.count(origin));
+  auto it = blocked_origins_.find(origin);
+  if (it->second == 1)
+    blocked_origins_.erase(it);
+  else
+    it->second--;
+}
+
 void ContentIndexDatabase::Shutdown() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/content/browser/content_index/content_index_database.h b/content/browser/content_index/content_index_database.h
index f7da3a1..2cfb68d 100644
--- a/content/browser/content_index/content_index_database.h
+++ b/content/browser/content_index/content_index_database.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_DATABASE_H_
 #define CONTENT_BROWSER_CONTENT_INDEX_CONTENT_INDEX_DATABASE_H_
 
+#include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
@@ -62,6 +63,10 @@
                 const std::string& description_id,
                 ContentIndexContext::GetEntryCallback callback);
 
+  // Block/Unblock DB operations for |origin|.
+  void BlockOrigin(const url::Origin& origin);
+  void UnblockOrigin(const url::Origin& origin);
+
   // Called when the storage partition is shutting down.
   void Shutdown();
 
@@ -110,6 +115,9 @@
   // Lives on the UI thread.
   ContentIndexProvider* provider_;
 
+  // A map from origins to how many times it's been blocked.
+  base::flat_map<url::Origin, int> blocked_origins_;
+
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
   base::WeakPtrFactory<ContentIndexDatabase> weak_ptr_factory_io_;
   base::WeakPtrFactory<ContentIndexDatabase> weak_ptr_factory_ui_;
diff --git a/content/browser/content_index/content_index_database_unittest.cc b/content/browser/content_index/content_index_database_unittest.cc
index 9615abc8..4a7734b 100644
--- a/content/browser/content_index/content_index_database_unittest.cc
+++ b/content/browser/content_index/content_index_database_unittest.cc
@@ -409,4 +409,32 @@
   }
 }
 
+TEST_F(ContentIndexDatabaseTest, BlockedOriginsCannotRegisterContent) {
+  // Initially adding is fine.
+  EXPECT_EQ(AddEntry(CreateDescription("id1")),
+            blink::mojom::ContentIndexError::NONE);
+
+  // Two delete events were dispatched.
+  database()->BlockOrigin(origin());
+  database()->BlockOrigin(origin());
+
+  // Content can't be registered while the origin is blocked.
+  EXPECT_EQ(AddEntry(CreateDescription("id2")),
+            blink::mojom::ContentIndexError::STORAGE_ERROR);
+
+  // First event dispatch completed.
+  database()->UnblockOrigin(origin());
+
+  // Content still can't be registered.
+  EXPECT_EQ(AddEntry(CreateDescription("id3")),
+            blink::mojom::ContentIndexError::STORAGE_ERROR);
+
+  // Last event dispatch completed.
+  database()->UnblockOrigin(origin());
+
+  // Registering is OK now.
+  EXPECT_EQ(AddEntry(CreateDescription("id4")),
+            blink::mojom::ContentIndexError::NONE);
+}
+
 }  // namespace content
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index b999311..b4f1b8b 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -1121,7 +1121,8 @@
 
   // Download manager is only initialized if both history db and in progress
   // cache are initialized.
-  if (!history_db_initialized_ || !in_progress_cache_initialized_)
+  bool history_loaded = history_db_initialized_ || IsOffTheRecord();
+  if (!history_loaded || !in_progress_cache_initialized_)
     return;
 
 #if defined(OS_ANDROID)
@@ -1381,8 +1382,8 @@
     }
     url_loader_factory_getter =
         base::MakeRefCounted<FileSystemDownloadURLLoaderFactoryGetter>(
-            params->url(), rfh, /*is_navigation=*/false,
-            storage_partition->GetFileSystemContext(), storage_domain);
+            params->url(), rfh, storage_partition->GetFileSystemContext(),
+            storage_domain);
   } else if (params->url().SchemeIs(url::kDataScheme)) {
     url_loader_factory_getter =
         CreateDownloadURLLoaderFactoryGetterFromURLLoaderFactory(
diff --git a/content/browser/download/file_system_download_url_loader_factory_getter.cc b/content/browser/download/file_system_download_url_loader_factory_getter.cc
index 3c4780e..13da85d0 100644
--- a/content/browser/download/file_system_download_url_loader_factory_getter.cc
+++ b/content/browser/download/file_system_download_url_loader_factory_getter.cc
@@ -9,6 +9,8 @@
 #include "components/download/public/common/download_task_runner.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
 #include "content/browser/url_loader_factory_getter.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -19,11 +21,9 @@
     FileSystemDownloadURLLoaderFactoryGetter(
         const GURL& url,
         RenderFrameHost* rfh,
-        bool is_navigation,
         scoped_refptr<storage::FileSystemContext> file_system_context,
         const std::string& storage_domain)
     : rfh_(rfh),
-      is_navigation_(is_navigation),
       file_system_context_(file_system_context),
       storage_domain_(storage_domain) {
   DCHECK(url.SchemeIs(url::kFileSystemScheme));
@@ -38,7 +38,8 @@
   DCHECK(download::GetIOTaskRunner()->BelongsToCurrentThread());
 
   std::unique_ptr<network::mojom::URLLoaderFactory> factory =
-      CreateFileSystemURLLoaderFactory(rfh_, is_navigation_,
+      CreateFileSystemURLLoaderFactory(rfh_->GetProcess()->GetID(),
+                                       rfh_->GetFrameTreeNodeId(),
                                        file_system_context_, storage_domain_);
 
   network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info;
diff --git a/content/browser/download/file_system_download_url_loader_factory_getter.h b/content/browser/download/file_system_download_url_loader_factory_getter.h
index 422a6e9ab..fae54d0a 100644
--- a/content/browser/download/file_system_download_url_loader_factory_getter.h
+++ b/content/browser/download/file_system_download_url_loader_factory_getter.h
@@ -26,7 +26,6 @@
   FileSystemDownloadURLLoaderFactoryGetter(
       const GURL& url,
       RenderFrameHost* rfh,
-      bool is_navigation,
       scoped_refptr<storage::FileSystemContext> file_system_context,
       const std::string& storage_domain);
 
@@ -38,7 +37,6 @@
 
  private:
   RenderFrameHost* rfh_;
-  const bool is_navigation_;
   scoped_refptr<storage::FileSystemContext> file_system_context_;
   const std::string storage_domain_;
 
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index 8b76dbb..fa5bc07 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -279,7 +279,7 @@
             &partition_name, &in_memory);
       }
       url_loader_factory = CreateFileSystemURLLoaderFactory(
-          rfh, /*is_navigation=*/false,
+          rfh->GetProcess()->GetID(), rfh->GetFrameTreeNodeId(),
           storage_partition->GetFileSystemContext(), storage_domain);
       factory = url_loader_factory.get();
     } else if (rfh && url.SchemeIs(content::kChromeUIScheme)) {
diff --git a/content/browser/fileapi/file_system_url_loader_factory.cc b/content/browser/fileapi/file_system_url_loader_factory.cc
index 063d74c88..c6537709 100644
--- a/content/browser/fileapi/file_system_url_loader_factory.cc
+++ b/content/browser/fileapi/file_system_url_loader_factory.cc
@@ -650,19 +650,12 @@
 
 std::unique_ptr<network::mojom::URLLoaderFactory>
 CreateFileSystemURLLoaderFactory(
-    RenderFrameHost* render_frame_host,
-    bool is_navigation,
+    int render_process_host_id,
+    int frame_tree_node_id,
     scoped_refptr<FileSystemContext> file_system_context,
     const std::string& storage_domain) {
-  // Get the RPH ID for security checks for non-navigation resource requests.
-  int render_process_host_id = is_navigation
-                                   ? ChildProcessHost::kInvalidUniqueID
-                                   : render_frame_host->GetProcess()->GetID();
-
-  FactoryParams params = {render_process_host_id,
-                          render_frame_host->GetFrameTreeNodeId(),
+  FactoryParams params = {render_process_host_id, frame_tree_node_id,
                           file_system_context, storage_domain};
-
   return std::make_unique<FileSystemURLLoaderFactory>(
       std::move(params),
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}));
diff --git a/content/browser/fileapi/file_system_url_loader_factory.h b/content/browser/fileapi/file_system_url_loader_factory.h
index 2cfda1e1..c00d9594 100644
--- a/content/browser/fileapi/file_system_url_loader_factory.h
+++ b/content/browser/fileapi/file_system_url_loader_factory.h
@@ -19,10 +19,30 @@
 
 // Create a URLLoaderFactory to serve filesystem: requests from the given
 // |file_system_context| and |storage_domain|.
+//
+// |render_process_host_id| is the ID of the RenderProcessHost where the
+// requests are issued.
+// - For a factory created for a browser-initiated navigation request:
+//   ChildProcessHost::kInvalidUniqueID (there is no process yet).
+// - For a factory created to pass to the renderer for subresource requests from
+//   the frame: that renderer process's ID.
+// - For a factory created for a browser-initiated worker main script request:
+//   the ID of the process the worker will run in.
+//   TODO(https://crbug.com/986188): We should specify kInvalidUniqueID for this
+//   worker main script case like the browser-initiated navigation case.
+// - For a factory created to pass to the renderer for subresource requests from
+//   the worker: that renderer process's ID.
+//
+// |frame_tree_node_id| is the ID of the FrameTreeNode where the requests are
+// associated.
+// - For a factory created for a browser-initiated navigation request, or for a
+//   factory created for subresource requests from the frame: that frame's ID.
+// - For a factory created for workers (which don't have frames):
+//   RenderFrameHost::kNoFrameTreeNodeId.
 CONTENT_EXPORT std::unique_ptr<network::mojom::URLLoaderFactory>
 CreateFileSystemURLLoaderFactory(
-    RenderFrameHost* render_frame_host,
-    bool is_navigation,
+    int render_process_host_id,
+    int frame_tree_node_id,
     scoped_refptr<storage::FileSystemContext> file_system_context,
     const std::string& storage_domain);
 
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 c8ea0b4..b431248 100644
--- a/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc
+++ b/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc
@@ -473,7 +473,8 @@
     const std::string storage_domain = url.GetOrigin().host();
 
     auto factory = content::CreateFileSystemURLLoaderFactory(
-        render_frame_host(), /*is_navigation=*/false, file_system_context,
+        render_frame_host()->GetProcess()->GetID(),
+        render_frame_host()->GetFrameTreeNodeId(), file_system_context,
         storage_domain);
 
     auto client = std::make_unique<network::TestURLLoaderClient>();
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 0cd7025..b798e7a3 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -44,13 +44,6 @@
 // Overrideable via SetCommitTimeoutForTesting.
 base::TimeDelta g_commit_timeout = kDefaultCommitTimeout;
 
-// Use this to get a new unique ID for a NavigationHandle during construction.
-// The returned ID is guaranteed to be nonzero (zero is the "no ID" indicator).
-int64_t CreateUniqueHandleID() {
-  static int64_t unique_id_counter = 0;
-  return ++unique_id_counter;
-}
-
 }  // namespace
 
 NavigationHandleImpl::NavigationHandleImpl(
@@ -60,7 +53,6 @@
     : navigation_request_(navigation_request),
       request_headers_(std::move(request_headers)),
       pending_nav_entry_id_(pending_nav_entry_id),
-      navigation_id_(CreateUniqueHandleID()),
       reload_type_(ReloadType::NONE),
       restore_type_(RestoreType::NONE) {
   const GURL& url = navigation_request_->common_params().url;
@@ -125,7 +117,7 @@
 }
 
 int64_t NavigationHandleImpl::GetNavigationId() {
-  return navigation_id_;
+  return navigation_request_->navigation_handle_id();
 }
 
 const GURL& NavigationHandleImpl::GetURL() {
@@ -401,7 +393,7 @@
 }
 
 const net::ProxyServer& NavigationHandleImpl::GetProxyServer() {
-  return proxy_server_;
+  return navigation_request_->proxy_server();
 }
 
 void NavigationHandleImpl::InitServiceWorkerHandle(
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index d95a3f4..2770c97 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -194,10 +194,6 @@
     return navigation_request_->common_params().source_location;
   }
 
-  void set_proxy_server(const net::ProxyServer& proxy_server) {
-    proxy_server_ = proxy_server;
-  }
-
   std::vector<std::string> TakeRemovedRequestHeaders() {
     return std::move(removed_request_headers_);
   }
@@ -272,18 +268,12 @@
   // corresponding provider is created in the renderer.
   std::unique_ptr<ServiceWorkerNavigationHandle> service_worker_handle_;
 
-  // The unique id to identify this to navigation with.
-  int64_t navigation_id_;
-
   // Stores the reload type, or NONE if it's not a reload.
   ReloadType reload_type_;
 
   // Stores the restore type, or NONE it it's not a restore.
   RestoreType restore_type_;
 
-  // Which proxy server was used for this navigation, if any.
-  net::ProxyServer proxy_server_;
-
   // Allows to override response_headers_ in tests.
   // TODO(clamy): Clean this up once the architecture of unit tests is better.
   scoped_refptr<net::HttpResponseHeaders> response_headers_for_testing_;
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 22ab203..f8d5c526 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -450,6 +450,13 @@
   NOTREACHED() << "Invalid page transition: " << transition;
 }
 
+// Use this to get a new unique ID for a NavigationHandle during construction.
+// The returned ID is guaranteed to be nonzero (zero is the "no ID" indicator).
+int64_t CreateUniqueHandleID() {
+  static int64_t unique_id_counter = 0;
+  return ++unique_id_counter;
+}
+
 }  // namespace
 
 // static
@@ -989,6 +996,7 @@
   }
 
   handle_state_ = NavigationRequest::INITIAL;
+  navigation_handle_id_ = CreateUniqueHandleID();
 
   std::unique_ptr<NavigationHandleImpl> navigation_handle = base::WrapUnique(
       new NavigationHandleImpl(this, nav_entry_id_, std::move(headers)));
@@ -1388,7 +1396,7 @@
   }
 
   // This must be set before DetermineCommittedPreviews is called.
-  navigation_handle_->set_proxy_server(response_head->head.proxy_server);
+  proxy_server_ = response_head->head.proxy_server;
 
   // Update the previews state of the request.
   common_params_.previews_state =
@@ -2418,6 +2426,18 @@
         rfh, blink::mojom::WebFeature::kOpenerNavigationDownloadCrossOrigin);
   }
 
+  // Log UseCounters for download in sandbox.
+  if (download_policy.IsType(NavigationDownloadType::kSandbox)) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh, blink::mojom::WebFeature::kDownloadInSandbox);
+  }
+
+  // Log UseCounters for download without user activation.
+  if (download_policy.IsType(NavigationDownloadType::kNoGesture)) {
+    GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+        rfh, blink::mojom::WebFeature::kDownloadWithoutUserGesture);
+  }
+
   // Log UseCounters for download in sandbox without user activation.
   if (download_policy.IsType(NavigationDownloadType::kSandboxNoGesture)) {
     GetContentClient()->browser()->LogWebFeatureForCurrentPage(
@@ -2430,10 +2450,10 @@
         rfh, blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture);
   }
 
-  // Log UseCounters for download in ad frame with user activation.
-  if (download_policy.IsType(NavigationDownloadType::kAdFrameGesture)) {
+  // Log UseCounters for download in ad frame.
+  if (download_policy.IsType(NavigationDownloadType::kAdFrame)) {
     GetContentClient()->browser()->LogWebFeatureForCurrentPage(
-        rfh, blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture);
+        rfh, blink::mojom::WebFeature::kDownloadInAdFrame);
   }
 }
 
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index f5aa353..67c95139 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -29,6 +29,7 @@
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/common/previews_state.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/proxy_server.h"
 #include "services/network/public/cpp/origin_policy.h"
 
 #if defined(OS_ANDROID)
@@ -456,6 +457,10 @@
     complete_callback_for_testing_ = std::move(callback);
   }
 
+  const net::ProxyServer& proxy_server() { return proxy_server_; }
+
+  int64_t navigation_handle_id() { return navigation_handle_id_; }
+
  private:
   // TODO(clamy): Transform NavigationHandleImplTest into NavigationRequestTest
   // once NavigationHandleImpl has become a wrapper around NavigationRequest.
@@ -946,6 +951,12 @@
   // load it from the corresponding entry.
   std::unique_ptr<BundledExchangesFactory> bundled_exchanges_factory_;
 
+  // Which proxy server was used for this navigation, if any.
+  net::ProxyServer proxy_server_;
+
+  // The unique id to identify the NavigationHandle with.
+  int64_t navigation_handle_id_ = 0;
+
   base::WeakPtrFactory<NavigationRequest> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index d570dbb..a9d30fa2e 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -5038,8 +5038,8 @@
     non_network_url_loader_factories_.emplace(
         url::kFileSystemScheme,
         content::CreateFileSystemURLLoaderFactory(
-            this, /*is_navigation=*/false, partition->GetFileSystemContext(),
-            storage_domain));
+            process_->GetID(), GetFrameTreeNodeId(),
+            partition->GetFileSystemContext(), storage_domain));
 
     non_network_url_loader_factories_.emplace(
         url::kDataScheme, std::make_unique<DataURLLoaderFactory>());
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 6181257..4a971e5 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -1469,6 +1469,112 @@
   }
 }
 
+namespace {
+bool HasDefaultSiteInstance(RenderFrameHost* rfh) {
+  return static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance())
+      ->IsDefaultSiteInstance();
+}
+}  // namespace
+
+// Verify process assignment behavior for the case where a site that does not
+// require isolation embeds a frame that does require isolation, which in turn
+// embeds another site that does not require isolation.
+// A  (Does not require isolation)
+// +-> B (requires isolation)
+//     +-> C (different site from A that does not require isolation.)
+//         +-> A (same site as top-level which also does not require isolation.)
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, AIsolatedCA) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "www.foo.com",
+      "/cross_site_iframe_factory.html?a(isolated.foo.com(c(www.foo.com)))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  RenderFrameHost* a = root->current_frame_host();
+  RenderFrameHost* b = root->child_at(0)->current_frame_host();
+  RenderFrameHost* c = root->child_at(0)->child_at(0)->current_frame_host();
+  RenderFrameHost* d =
+      root->child_at(0)->child_at(0)->child_at(0)->current_frame_host();
+
+  // Sanity check that the test works with the right frame tree.
+  EXPECT_FALSE(IsIsolatedOrigin(a->GetLastCommittedOrigin()));
+  EXPECT_TRUE(IsIsolatedOrigin(b->GetLastCommittedOrigin()));
+  EXPECT_FALSE(IsIsolatedOrigin(c->GetLastCommittedOrigin()));
+  EXPECT_FALSE(IsIsolatedOrigin(d->GetLastCommittedOrigin()));
+  EXPECT_EQ("www.foo.com", a->GetLastCommittedURL().host());
+  EXPECT_EQ("isolated.foo.com", b->GetLastCommittedURL().host());
+  EXPECT_EQ("c.com", c->GetLastCommittedURL().host());
+  EXPECT_EQ("www.foo.com", d->GetLastCommittedURL().host());
+
+  // Verify that the isolated site is indeed isolated.
+  EXPECT_NE(b->GetProcess()->GetID(), a->GetProcess()->GetID());
+  EXPECT_NE(b->GetProcess()->GetID(), c->GetProcess()->GetID());
+  EXPECT_NE(b->GetProcess()->GetID(), d->GetProcess()->GetID());
+
+  // Verify that same-origin a and d frames share a process.  This is
+  // necessary for correctness - otherwise a and d wouldn't be able to
+  // synchronously script each other.
+  EXPECT_EQ(a->GetProcess()->GetID(), d->GetProcess()->GetID());
+
+  // Verify that same-origin a and d frames can script each other.
+  EXPECT_TRUE(ExecuteScript(a, "window.name = 'a';"));
+  EXPECT_TRUE(ExecuteScript(d, R"(
+      a = window.open('', 'a');
+      a.cross_frame_property_test = 'hello from d'; )"));
+  EXPECT_EQ("hello from d",
+            EvalJs(a, "window.cross_frame_property_test").ExtractString());
+
+  // The test assertions below are not strictly necessary - they just document
+  // the current behavior.  In particular, consolidating www.foo.com and c.com
+  // sites into the same process is not necessary for correctness.
+  if (AreAllSitesIsolatedForTesting()) {
+    // All sites are isolated so we expect foo.com, isolated.foo.com and c.com
+    // to all be in their own processes.
+    EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
+    EXPECT_NE(a->GetProcess()->GetID(), c->GetProcess()->GetID());
+    EXPECT_NE(b->GetProcess()->GetID(), c->GetProcess()->GetID());
+
+    EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
+    EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
+    EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
+    EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
+
+    EXPECT_FALSE(HasDefaultSiteInstance(a));
+    EXPECT_FALSE(HasDefaultSiteInstance(b));
+    EXPECT_FALSE(HasDefaultSiteInstance(c));
+  } else if (AreDefaultSiteInstancesEnabled()) {
+    // All sites that are not isolated should be in the same default
+    // SiteInstance process.
+    EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
+    EXPECT_EQ(a->GetProcess()->GetID(), c->GetProcess()->GetID());
+
+    EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
+    EXPECT_EQ(a->GetSiteInstance(), c->GetSiteInstance());
+    EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
+    EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
+
+    EXPECT_TRUE(HasDefaultSiteInstance(a));
+    EXPECT_FALSE(HasDefaultSiteInstance(b));
+  } else {
+    // Documenting current behavior where the top level document doesn't end
+    // up in a default SiteInstance even though it is not isolated and does not
+    // require a dedicated process. c.com does get placed in a default
+    // SiteInstance because we currently allow subframes that don't require
+    // isolation to share a process. This behavior should go away once we
+    // turn on default SiteInstances by default.
+    EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
+    EXPECT_NE(a->GetProcess()->GetID(), c->GetProcess()->GetID());
+
+    EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
+    EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
+    EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
+    EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
+
+    EXPECT_FALSE(HasDefaultSiteInstance(a));
+    EXPECT_FALSE(HasDefaultSiteInstance(b));
+    EXPECT_TRUE(HasDefaultSiteInstance(c));
+  }
+}
+
 class IsolatedOriginTestWithMojoBlobURLs : public IsolatedOriginTest {
  public:
   IsolatedOriginTestWithMojoBlobURLs() {
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 2f3c7e91..7dafa5c2 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -935,13 +935,18 @@
         browser_context_to_use = browser_context_;
       else
         resource_context_to_use = resource_context_;
+      // TODO(crbug.com/984460): Don't use WeakPtrs for callbacks. These
+      // WeakPtrs are to work around a case where ServiceWorkerNavigationLoader
+      // outlives the interceptor. We may want to reset fallback callback in
+      // ServiceWorkerNavigationLoader when the URLLoaderRequestController is
+      // going away.
       next_interceptor->MaybeCreateLoader(
           *resource_request_, browser_context_to_use, resource_context_to_use,
           base::BindOnce(&URLLoaderRequestController::MaybeStartLoader,
-                         base::Unretained(this), next_interceptor),
+                         weak_factory_.GetWeakPtr(), next_interceptor),
           base::BindOnce(
               &URLLoaderRequestController::FallbackToNonInterceptedRequest,
-              base::Unretained(this)));
+              weak_factory_.GetWeakPtr()));
       return;
     }
 
@@ -1789,8 +1794,8 @@
 
     const std::string storage_domain;
     non_network_url_loader_factories_[url::kFileSystemScheme] =
-        CreateFileSystemURLLoaderFactory(frame_tree_node->current_frame_host(),
-                                         /*is_navigation=*/true,
+        CreateFileSystemURLLoaderFactory(ChildProcessHost::kInvalidUniqueID,
+                                         frame_tree_node->frame_tree_node_id(),
                                          partition->GetFileSystemContext(),
                                          storage_domain);
   }
diff --git a/content/browser/media/cdm_file_impl.cc b/content/browser/media/cdm_file_impl.cc
index 2390ccf..887215a 100644
--- a/content/browser/media/cdm_file_impl.cc
+++ b/content/browser/media/cdm_file_impl.cc
@@ -20,6 +20,7 @@
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "storage/browser/fileapi/file_stream_reader.h"
+#include "storage/browser/fileapi/file_stream_writer.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_context.h"
 #include "storage/browser/fileapi/file_system_url.h"
@@ -116,30 +117,6 @@
   return file_lock_map;
 }
 
-// Write |data| to |file|. Returns kSuccess if everything works, kFailure
-// otherwise.  This method owns |file| and it will be closed at the end.
-CdmFileImpl::Status WriteFile(base::File file, std::vector<uint8_t> data) {
-  // As the temporary file should have been newly created, it should be empty.
-  CHECK_EQ(0u, file.GetLength()) << "Temporary file is not empty.";
-  int bytes_to_write = base::checked_cast<int>(data.size());
-
-  TRACE_EVENT0("media", "CdmFileWriteFile");
-  base::TimeTicks start = base::TimeTicks::Now();
-  int bytes_written =
-      file.Write(0, reinterpret_cast<const char*>(data.data()), bytes_to_write);
-  base::TimeDelta write_time = base::TimeTicks::Now() - start;
-  if (bytes_written != bytes_to_write) {
-    DLOG(WARNING) << "Failed to write file. Requested " << bytes_to_write
-                  << " bytes, wrote " << bytes_written;
-    return CdmFileImpl::Status::kFailure;
-  }
-
-  // Only report writing time for successful writes.
-  UMA_HISTOGRAM_TIMES("Media.EME.CdmFileIO.WriteTime", write_time);
-
-  return CdmFileImpl::Status::kSuccess;
-}
-
 // File stream operations need an IOBuffer to hold the data. This class stores
 // the data in a std::vector<uint8_t> to match what is used in the
 // mojom::CdmFile API.
@@ -150,6 +127,11 @@
     data_ = reinterpret_cast<char*>(buffer_.data());
   }
 
+  // Create a buffer that contains |data|.
+  explicit CdmFileIOBuffer(const std::vector<uint8_t>& data) : buffer_(data) {
+    data_ = reinterpret_cast<char*>(buffer_.data());
+  }
+
   // Returns ownership of |buffer_| to the caller.
   std::vector<uint8_t>&& TakeData() { return std::move(buffer_); }
 
@@ -292,6 +274,102 @@
   DISALLOW_COPY_AND_ASSIGN(FileReader);
 };
 
+class CdmFileImpl::FileWriter {
+ public:
+  // Returns whether the write operation succeeded or not.
+  using WriteDoneCB = base::OnceCallback<void(bool)>;
+
+  FileWriter() {}
+
+  // Writes |buffer| as the contents of |file_url| and calls |callback| with
+  // whether the write succeeded or not.
+  void Write(scoped_refptr<storage::FileSystemContext> file_system_context,
+             const storage::FileSystemURL& file_url,
+             scoped_refptr<net::IOBuffer> buffer,
+             int bytes_to_write,
+             WriteDoneCB callback) {
+    DVLOG(3) << __func__ << " url: " << file_url.DebugString();
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+    callback_ = std::move(callback);
+
+    // Create a writer on |temp_file_name_|. This temp file will be renamed
+    // after a successful write.
+    file_stream_writer_ =
+        file_system_context->CreateFileStreamWriter(file_url, 0);
+    base::TimeTicks start_time = base::TimeTicks::Now();
+    auto result = file_stream_writer_->Write(
+        buffer.get(), bytes_to_write,
+        base::BindOnce(&FileWriter::OnWrite, weak_factory_.GetWeakPtr(), buffer,
+                       start_time, bytes_to_write));
+    DVLOG(3) << __func__ << " Write(): " << result;
+
+    // If Write() is running asynchronously, simply return.
+    if (result == net::ERR_IO_PENDING)
+      return;
+
+    // Write() was synchronous, so pass the result on.
+    OnWrite(std::move(buffer), start_time, bytes_to_write, result);
+  }
+
+ private:
+  // Called when the file has been written. |result| will be the number of bytes
+  // written (if >= 0) or a net:: error on failure (if < 0).
+  void OnWrite(scoped_refptr<net::IOBuffer> buffer,
+               base::TimeTicks start_time,
+               int bytes_to_write,
+               int result) {
+    DVLOG(3) << __func__ << " Expected to write " << bytes_to_write
+             << " bytes, got " << result;
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+    if (result != bytes_to_write) {
+      // Unable to write the file.
+      DLOG(WARNING) << "Failed to write file. Sent " << bytes_to_write
+                    << " bytes, wrote " << result;
+      std::move(callback_).Run(false);
+      return;
+    }
+
+    // Only report writing time for successful writes.
+    base::TimeDelta write_time = base::TimeTicks::Now() - start_time;
+    UMA_HISTOGRAM_TIMES("Media.EME.CdmFileIO.WriteTime", write_time);
+
+    result = file_stream_writer_->Flush(
+        base::BindOnce(&FileWriter::OnFlush, weak_factory_.GetWeakPtr()));
+    DVLOG(3) << __func__ << " Flush(): " << result;
+
+    // If Flush() is running asynchronously, simply return.
+    if (result == net::ERR_IO_PENDING)
+      return;
+
+    // Flush() was synchronous, so pass the result on.
+    OnFlush(result);
+  }
+
+  // Called when the file has been flushed. |result| is the net:: error code.
+  void OnFlush(int result) {
+    DVLOG(3) << __func__ << " Result: " << result;
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+    // We are done with |file_stream_writer_|.
+    file_stream_writer_.reset();
+
+    DLOG_IF(WARNING, result != net::OK)
+        << "Failed to flush file, result: " << result;
+    std::move(callback_).Run(result == net::OK);
+  }
+
+  // Called when the write operation is done.
+  WriteDoneCB callback_;
+
+  // Used to write the stream.
+  std::unique_ptr<storage::FileStreamWriter> file_stream_writer_;
+
+  base::WeakPtrFactory<FileWriter> weak_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(FileWriter);
+};
+
 // static
 bool CdmFileImpl::IsValidName(const std::string& name) {
   // File names must only contain letters (A-Za-z), digits(0-9), or "._-",
@@ -335,11 +413,8 @@
   if (read_callback_)
     std::move(read_callback_).Run(Status::kFailure, {});
 
-  if (file_reader_) {
-    // |file_reader_| must be deleted on the IO thread.
-    base::CreateSequencedTaskRunner({BrowserThread::IO})
-        ->DeleteSoon(FROM_HERE, std::move(file_reader_));
-  }
+  if (write_callback_)
+    std::move(write_callback_).Run(Status::kFailure);
 
   if (file_locked_)
     ReleaseFileLock(file_name_);
@@ -380,12 +455,10 @@
   // the IO thread. Use of base::Unretained() is OK as the reader is owned by
   // |this|, and if |this| is destructed it will destroy the file reader on the
   // IO thread.
-  file_reader_ = std::make_unique<FileReader>();
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&FileReader::Read, base::Unretained(file_reader_.get()),
-                     file_system_context_, CreateFileSystemURL(file_name_),
-                     std::move(read_done_cb)));
+  file_reader_ = base::SequenceBound<FileReader>(
+      base::CreateSequencedTaskRunner({BrowserThread::IO}));
+  file_reader_.Post(FROM_HERE, &FileReader::Read, file_system_context_,
+                    CreateFileSystemURL(file_name_), std::move(read_done_cb));
 }
 
 void CdmFileImpl::ReadDone(bool success, std::vector<uint8_t> data) {
@@ -397,7 +470,7 @@
   DCHECK(read_callback_);
 
   // We are done with the reader, so destroy it.
-  file_reader_.reset();
+  file_reader_.Reset();
 
   if (!success) {
     // Unable to read the contents of the file.
@@ -408,37 +481,13 @@
   std::move(read_callback_).Run(Status::kSuccess, std::move(data));
 }
 
-void CdmFileImpl::OpenFile(const std::string& file_name,
-                           uint32_t file_flags,
-                           CreateOrOpenCallback callback) {
-  DVLOG(3) << __func__ << " file: " << file_name << ", flags: " << file_flags;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-
-  storage::FileSystemURL file_url = CreateFileSystemURL(file_name);
-  storage::AsyncFileUtil* file_util = file_system_context_->GetAsyncFileUtil(
-      storage::kFileSystemTypePluginPrivate);
-  auto operation_context =
-      std::make_unique<storage::FileSystemOperationContext>(
-          file_system_context_.get());
-  operation_context->set_allowed_bytes_growth(storage::QuotaManager::kNoLimit);
-  DVLOG(3) << "Opening " << file_url.DebugString();
-
-  file_util->CreateOrOpen(std::move(operation_context), file_url, file_flags,
-                          std::move(callback));
-}
-
 void CdmFileImpl::Write(const std::vector<uint8_t>& data,
                         WriteCallback callback) {
   DVLOG(3) << __func__ << " file: " << file_name_ << ", size: " << data.size();
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(file_locked_);
-
-  // If there is no data to write, delete the file to save space.
-  if (data.empty()) {
-    DeleteFile(std::move(callback));
-    return;
-  }
+  DCHECK(!write_callback_);
+  DCHECK(!file_writer_);
 
   // Files are limited in size, so fail if file too big. This should have been
   // checked by the caller, but we don't fully trust IPC.
@@ -449,51 +498,83 @@
     return;
   }
 
-  // Open the temporary file for writing. Specifying FLAG_CREATE_ALWAYS which
-  // will overwrite any existing file.
-  OpenFile(
-      temp_file_name_, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE,
-      base::BindOnce(&CdmFileImpl::OnTempFileOpenedForWriting,
-                     weak_factory_.GetWeakPtr(), data, std::move(callback)));
-}
+  // Save |callback| for later use.
+  write_callback_ = std::move(callback);
 
-void CdmFileImpl::OnTempFileOpenedForWriting(
-    std::vector<uint8_t> data,
-    WriteCallback callback,
-    base::File file,
-    base::OnceClosure on_close_callback) {
-  DVLOG(3) << __func__ << " file: " << temp_file_name_
-           << ", bytes_to_write: " << data.size();
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-
-  if (!file.IsValid()) {
-    DLOG(WARNING) << "Unable to open file " << temp_file_name_ << ", error: "
-                  << base::File::ErrorToString(file.error_details());
-    std::move(callback).Run(Status::kFailure);
+  // If there is no data to write, delete the file to save space.
+  // |write_callback_| will be called after the file is deleted.
+  if (data.empty()) {
+    DeleteFile();
     return;
   }
 
-  // Writing to |file| must be done on a thread that allows blocking, so post a
-  // task to do the writing on a separate thread. When that completes we need to
-  // rename the file in order to replace any existing contents.
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(&WriteFile, std::move(file), std::move(data)),
-      base::BindOnce(&CdmFileImpl::OnFileWritten, weak_factory_.GetWeakPtr(),
-                     std::move(callback)));
+  // Copy |data| into a net::IOBuffer.
+  int bytes_to_write = base::checked_cast<int>(data.size());
+  auto buffer = base::MakeRefCounted<CdmFileIOBuffer>(data);
+
+  // FileStreamWriter only works on existing files. |temp_file_name_| should not
+  // exist, so create an empty one if necessary.
+  auto url = CreateFileSystemURL(temp_file_name_);
+  auto* file_util = file_system_context_->GetAsyncFileUtil(
+      storage::kFileSystemTypePluginPrivate);
+  auto operation_context =
+      std::make_unique<storage::FileSystemOperationContext>(
+          file_system_context_.get());
+  operation_context->set_allowed_bytes_growth(storage::QuotaManager::kNoLimit);
+  file_util->EnsureFileExists(
+      std::move(operation_context), url,
+      base::Bind(&CdmFileImpl::OnEnsureFileExists, weak_factory_.GetWeakPtr(),
+                 std::move(buffer), bytes_to_write));
 }
 
-void CdmFileImpl::OnFileWritten(WriteCallback callback, Status status) {
-  DVLOG(3) << __func__ << " file: " << temp_file_name_
-           << ", status: " << status;
+void CdmFileImpl::OnEnsureFileExists(scoped_refptr<net::IOBuffer> buffer,
+                                     int bytes_to_write,
+                                     base::File::Error result,
+                                     bool created) {
+  DVLOG(3) << __func__ << " file: " << file_name_
+           << ", result: " << base::File::ErrorToString(result);
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(file_locked_);
+  DCHECK(write_callback_);
+  DCHECK(!file_writer_);
 
-  if (status != Status::kSuccess) {
-    // Write failed, so fail.
-    std::move(callback).Run(status);
+  if (result != base::File::FILE_OK) {
+    // Unable to create the file.
+    DLOG(WARNING) << "Failed to create temporary file, result: "
+                  << base::File::ErrorToString(result);
+    std::move(write_callback_).Run(Status::kFailure);
+    return;
+  }
+
+  // As writing is done on the IO thread, when it's done WriteDone() needs to be
+  // called on this thread.
+  auto write_done_cb = media::BindToCurrentLoop(
+      base::BindOnce(&CdmFileImpl::WriteDone, weak_factory_.GetWeakPtr()));
+
+  // Create the file writer that runs on the IO thread, and then call Write()
+  // on the IO thread to write |buffer| into the temporary file. Use of
+  // base::Unretained() is OK as |file_writer_| is owned by |this|, and if
+  // |this| is destructed it will destroy |file_writer_| on the IO thread.
+  file_writer_ = base::SequenceBound<FileWriter>(
+      base::CreateSequencedTaskRunner({BrowserThread::IO}));
+  file_writer_.Post(FROM_HERE, &FileWriter::Write, file_system_context_,
+                    CreateFileSystemURL(temp_file_name_), std::move(buffer),
+                    bytes_to_write, std::move(write_done_cb));
+}
+
+void CdmFileImpl::WriteDone(bool success) {
+  DVLOG(3) << __func__ << " file: " << file_name_
+           << ", success: " << (success ? "yes" : "no");
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(file_locked_);
+  DCHECK(file_writer_);
+  DCHECK(write_callback_);
+
+  // We are done with |file_writer_|.
+  file_writer_.Reset();
+
+  if (!success) {
+    std::move(write_callback_).Run(Status::kFailure);
     return;
   }
 
@@ -510,32 +591,35 @@
   file_util->MoveFileLocal(
       std::move(operation_context), src_file_url, dest_file_url,
       storage::FileSystemOperation::OPTION_NONE,
-      base::BindOnce(&CdmFileImpl::OnFileRenamed, weak_factory_.GetWeakPtr(),
-                     std::move(callback)));
+      base::BindOnce(&CdmFileImpl::OnFileRenamed, weak_factory_.GetWeakPtr()));
 }
 
-void CdmFileImpl::OnFileRenamed(WriteCallback callback,
-                                base::File::Error move_result) {
-  DVLOG(3) << __func__;
+void CdmFileImpl::OnFileRenamed(base::File::Error move_result) {
+  DVLOG(3) << __func__ << " file: " << file_name_
+           << ", result: " << base::File::ErrorToString(move_result);
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(file_locked_);
+  DCHECK(!file_writer_);
+  DCHECK(write_callback_);
 
   // Was the rename successful?
   if (move_result != base::File::FILE_OK) {
     DLOG(WARNING) << "Unable to rename file " << temp_file_name_ << " to "
                   << file_name_
                   << ", error: " << base::File::ErrorToString(move_result);
-    std::move(callback).Run(Status::kFailure);
+    std::move(write_callback_).Run(Status::kFailure);
     return;
   }
 
-  std::move(callback).Run(Status::kSuccess);
+  std::move(write_callback_).Run(Status::kSuccess);
 }
 
-void CdmFileImpl::DeleteFile(WriteCallback callback) {
+void CdmFileImpl::DeleteFile() {
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(file_locked_);
+  DCHECK(!file_writer_);
+  DCHECK(write_callback_);
 
   storage::FileSystemURL file_url = CreateFileSystemURL(file_name_);
   storage::AsyncFileUtil* file_util = file_system_context_->GetAsyncFileUtil(
@@ -547,25 +631,26 @@
   DVLOG(3) << "Deleting " << file_url.DebugString();
   file_util->DeleteFile(
       std::move(operation_context), file_url,
-      base::BindOnce(&CdmFileImpl::OnFileDeleted, weak_factory_.GetWeakPtr(),
-                     std::move(callback)));
+      base::BindOnce(&CdmFileImpl::OnFileDeleted, weak_factory_.GetWeakPtr()));
 }
 
-void CdmFileImpl::OnFileDeleted(WriteCallback callback,
-                                base::File::Error result) {
-  DVLOG(3) << __func__;
+void CdmFileImpl::OnFileDeleted(base::File::Error result) {
+  DVLOG(3) << __func__ << " file: " << file_name_
+           << ", result: " << base::File::ErrorToString(result);
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(file_locked_);
+  DCHECK(!file_writer_);
+  DCHECK(write_callback_);
 
   if (result != base::File::FILE_OK &&
       result != base::File::FILE_ERROR_NOT_FOUND) {
     DLOG(WARNING) << "Unable to delete file " << file_name_
                   << ", error: " << base::File::ErrorToString(result);
-    std::move(callback).Run(Status::kFailure);
+    std::move(write_callback_).Run(Status::kFailure);
     return;
   }
 
-  std::move(callback).Run(Status::kSuccess);
+  std::move(write_callback_).Run(Status::kSuccess);
 }
 
 storage::FileSystemURL CdmFileImpl::CreateFileSystemURL(
diff --git a/content/browser/media/cdm_file_impl.h b/content/browser/media/cdm_file_impl.h
index 5eeb5f81..3bb43a7 100644
--- a/content/browser/media/cdm_file_impl.h
+++ b/content/browser/media/cdm_file_impl.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/sequence_bound.h"
 #include "base/threading/thread_checker.h"
 #include "media/mojo/interfaces/cdm_storage.mojom.h"
 #include "storage/browser/fileapi/async_file_util.h"
@@ -51,33 +52,26 @@
 
  private:
   class FileReader;
-
-  using CreateOrOpenCallback = storage::AsyncFileUtil::CreateOrOpenCallback;
-
-  // Open the file |file_name| using the flags provided in |file_flags|.
-  // |callback| is called with the result.
-  void OpenFile(const std::string& file_name,
-                uint32_t file_flags,
-                CreateOrOpenCallback callback);
+  class FileWriter;
 
   // Called when the file is read. If |success| is true, |data| is the contents
   // of the file read.
   void ReadDone(bool success, std::vector<uint8_t> data);
 
-  // Called when |temp_file_name_| has been opened for writing. Writes
-  // |data| to |file|, closes |file|, and then kicks off a rename of
-  // |temp_file_name_| to |file_name_|, effectively replacing the contents of
-  // the old file.
-  void OnTempFileOpenedForWriting(std::vector<uint8_t> data,
-                                  WriteCallback callback,
-                                  base::File file,
-                                  base::OnceClosure on_close_callback);
-  void OnFileWritten(WriteCallback callback, Status status);
-  void OnFileRenamed(WriteCallback callback, base::File::Error move_result);
+  // Called in sequence to write the file. |buffer| is the contents to be
+  // written to the file, |bytes_to_write| is the length. Uses |file_writer_|,
+  // which is cleared when no longer needed. |write_callback_| will always be
+  // called with the result.
+  void OnEnsureFileExists(scoped_refptr<net::IOBuffer> buffer,
+                          int bytes_to_write,
+                          base::File::Error result,
+                          bool created);
+  void WriteDone(bool success);
+  void OnFileRenamed(base::File::Error move_result);
 
   // Deletes |file_name_| asynchronously.
-  void DeleteFile(WriteCallback callback);
-  void OnFileDeleted(WriteCallback callback, base::File::Error result);
+  void DeleteFile();
+  void OnFileDeleted(base::File::Error result);
 
   // Returns the FileSystemURL for the specified |file_name|.
   storage::FileSystemURL CreateFileSystemURL(const std::string& file_name);
@@ -107,7 +101,11 @@
 
   // Used when reading the file. |file_reader_| lives on the IO thread.
   ReadCallback read_callback_;
-  std::unique_ptr<FileReader> file_reader_;
+  base::SequenceBound<FileReader> file_reader_;
+
+  // Used when writing the file. |file_writer_| lives on the IO thread.
+  WriteCallback write_callback_;
+  base::SequenceBound<FileWriter> file_writer_;
 
   THREAD_CHECKER(thread_checker_);
   base::WeakPtrFactory<CdmFileImpl> weak_factory_{this};
diff --git a/content/browser/media/hardware_key_media_controller.h b/content/browser/media/hardware_key_media_controller.h
index 2f0a491f..0aafea6 100644
--- a/content/browser/media/hardware_key_media_controller.h
+++ b/content/browser/media/hardware_key_media_controller.h
@@ -40,6 +40,8 @@
       override;
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   // ui::MediaKeysListener::Delegate:
   void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override;
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 5c029100..372e80ef 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -16,6 +16,7 @@
 #include "ipc/ipc_message_macros.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/device/public/mojom/wake_lock_context.mojom.h"
+#include "services/media_session/public/cpp/media_position.h"
 #include "third_party/blink/public/platform/web_fullscreen_video_status.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -139,6 +140,8 @@
                         OnMediaPlaying)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMutedStatusChanged,
                         OnMediaMutedStatusChanged)
+    IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMediaPositionStateChanged,
+                        OnMediaPositionStateChanged);
     IPC_MESSAGE_HANDLER(
         MediaPlayerDelegateHostMsg_OnMediaEffectivelyFullscreenChanged,
         OnMediaEffectivelyFullscreenChanged)
@@ -333,6 +336,14 @@
   web_contents_impl()->MediaMutedStatusChanged(id, muted);
 }
 
+void MediaWebContentsObserver::OnMediaPositionStateChanged(
+    RenderFrameHost* render_frame_host,
+    int delegate_id,
+    const media_session::MediaPosition& position) {
+  const MediaPlayerId id(render_frame_host, delegate_id);
+  session_controllers_manager_.OnMediaPositionStateChanged(id, position);
+}
+
 void MediaWebContentsObserver::AddMediaPlayerEntry(
     const MediaPlayerId& id,
     ActiveMediaPlayerMap* player_map) {
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h
index 5bdf7d61..cc9fbf4 100644
--- a/content/browser/media/media_web_contents_observer.h
+++ b/content/browser/media/media_web_contents_observer.h
@@ -31,6 +31,10 @@
 enum class MediaContentType;
 }  // namespace media
 
+namespace media_session {
+struct MediaPosition;
+}  // namespace media_session
+
 namespace gfx {
 class Size;
 }  // namespace size
@@ -126,6 +130,10 @@
   void OnMediaMutedStatusChanged(RenderFrameHost* render_frame_host,
                                  int delegate_id,
                                  bool muted);
+  void OnMediaPositionStateChanged(
+      RenderFrameHost* render_frame_host,
+      int delegate_id,
+      const media_session::MediaPosition& position);
 
   // Clear |render_frame_host|'s tracking entry for its WakeLocks.
   void ClearWakeLocks(RenderFrameHost* render_frame_host);
diff --git a/content/browser/media/mpris_notifier.h b/content/browser/media/mpris_notifier.h
index eebb851..8813ed1 100644
--- a/content/browser/media/mpris_notifier.h
+++ b/content/browser/media/mpris_notifier.h
@@ -46,6 +46,8 @@
       override {}
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   void SetMprisServiceForTesting(mpris::MprisService* service) {
     service_ = service;
diff --git a/content/browser/media/now_playing_info_center_notifier.h b/content/browser/media/now_playing_info_center_notifier.h
index 7a9d9ca..9eb9d7e 100644
--- a/content/browser/media/now_playing_info_center_notifier.h
+++ b/content/browser/media/now_playing_info_center_notifier.h
@@ -45,6 +45,8 @@
       override {}
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
  private:
   // Our connection to the underlying OS API for MPNowPlayingInfoCenter.
diff --git a/content/browser/media/session/media_session_android.h b/content/browser/media/session/media_session_android.h
index 655a292..ad0d324 100644
--- a/content/browser/media/session/media_session_android.h
+++ b/content/browser/media/session/media_session_android.h
@@ -45,6 +45,8 @@
       const base::flat_map<media_session::mojom::MediaSessionImageType,
                            std::vector<media_session::MediaImage>>& images)
       override;
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   // MediaSession method wrappers.
   void Resume(JNIEnv* env, const base::android::JavaParamRef<jobject>& j_obj);
diff --git a/content/browser/media/session/media_session_controller.cc b/content/browser/media/session/media_session_controller.cc
index 92ca3a1..5e2b26c 100644
--- a/content/browser/media/session/media_session_controller.cc
+++ b/content/browser/media/session/media_session_controller.cc
@@ -31,12 +31,16 @@
 bool MediaSessionController::Initialize(
     bool has_audio,
     bool is_remote,
-    media::MediaContentType media_content_type) {
+    media::MediaContentType media_content_type,
+    media_session::MediaPosition* position) {
   // Store these as we will need them later.
   is_remote_ = is_remote;
   has_audio_ = has_audio;
   media_content_type_ = media_content_type;
 
+  if (position)
+    position_ = *position;
+
   // Don't generate a new id if one has already been set.
   if (!has_session_) {
     // These objects are only created on the UI thread, so this is safe.
@@ -144,4 +148,12 @@
   }
 }
 
+void MediaSessionController::OnMediaPositionStateChanged(
+    const media_session::MediaPosition& position) {
+  position_ = position;
+
+  // TODO(https://crbug.com/985394): Notify MediaSessionImpl that the
+  // position state has changed.
+}
+
 }  // namespace content
diff --git a/content/browser/media/session/media_session_controller.h b/content/browser/media/session/media_session_controller.h
index a619c57..9415005 100644
--- a/content/browser/media/session/media_session_controller.h
+++ b/content/browser/media/session/media_session_controller.h
@@ -6,12 +6,14 @@
 #define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_CONTROLLER_H_
 
 #include "base/compiler_specific.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "content/browser/media/session/media_session_player_observer.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/media_player_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "media/base/media_content_type.h"
+#include "services/media_session/public/cpp/media_position.h"
 
 namespace content {
 
@@ -38,7 +40,8 @@
   // the BrowserMediaPlayerManagers.  Tracked by http://crbug.com/580626
   bool Initialize(bool has_audio,
                   bool is_remote,
-                  media::MediaContentType media_content_type);
+                  media::MediaContentType media_content_type,
+                  media_session::MediaPosition* position);
 
   // Must be called when a pause occurs on the renderer side media player; keeps
   // the MediaSession instance in sync with renderer side behavior.
@@ -54,10 +57,18 @@
 
   // Test helpers.
   int get_player_id_for_testing() const { return player_id_; }
+  const base::Optional<media_session::MediaPosition>& get_position_for_testing()
+      const {
+    return position_;
+  }
 
   // Called when the WebContents is either muted or unmuted.
   void WebContentsMutedStateChanged(bool muted);
 
+  // Called when the media position state of the player has changed.
+  void OnMediaPositionStateChanged(
+      const media_session::MediaPosition& position);
+
  private:
   const MediaPlayerId id_;
 
@@ -67,6 +78,8 @@
   // Non-owned pointer; lifetime is the same as |media_web_contents_observer_|.
   MediaSessionImpl* const media_session_;
 
+  base::Optional<media_session::MediaPosition> position_;
+
   int player_id_ = 0;
   bool has_session_ = false;
   bool has_audio_ = false;
diff --git a/content/browser/media/session/media_session_controller_unittest.cc b/content/browser/media/session/media_session_controller_unittest.cc
index 5653bf1..6585ce20 100644
--- a/content/browser/media/session/media_session_controller_unittest.cc
+++ b/content/browser/media/session/media_session_controller_unittest.cc
@@ -153,29 +153,29 @@
 };
 
 TEST_F(MediaSessionControllerTest, NoAudioNoSession) {
-  ASSERT_TRUE(controller_->Initialize(false, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      false, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_FALSE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
 }
 
 TEST_F(MediaSessionControllerTest, IsRemoteNoSession) {
-  ASSERT_TRUE(
-      controller_->Initialize(true, true, media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, true, media::MediaContentType::Persistent, nullptr));
   EXPECT_FALSE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
 }
 
 TEST_F(MediaSessionControllerTest, TransientNoControllableSession) {
-  ASSERT_TRUE(
-      controller_->Initialize(true, false, media::MediaContentType::Transient));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Transient, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
 }
 
 TEST_F(MediaSessionControllerTest, BasicControls) {
-  ASSERT_TRUE(controller_->Initialize(true, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
 
@@ -205,8 +205,8 @@
 }
 
 TEST_F(MediaSessionControllerTest, VolumeMultiplier) {
-  ASSERT_TRUE(controller_->Initialize(true, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
 
@@ -222,8 +222,8 @@
 }
 
 TEST_F(MediaSessionControllerTest, ControllerSidePause) {
-  ASSERT_TRUE(controller_->Initialize(true, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
 
@@ -233,28 +233,28 @@
   EXPECT_TRUE(media_session()->IsControllable());
 
   // Verify the next Initialize() call restores the session.
-  ASSERT_TRUE(controller_->Initialize(true, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
 }
 
 TEST_F(MediaSessionControllerTest, Reinitialize) {
-  ASSERT_TRUE(controller_->Initialize(false, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      false, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_FALSE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
 
   // Create a transient type session.
-  ASSERT_TRUE(
-      controller_->Initialize(true, false, media::MediaContentType::Transient));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Transient, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
   const int current_player_id = controller_->get_player_id_for_testing();
 
   // Reinitialize the session as a content type.
-  ASSERT_TRUE(controller_->Initialize(true, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
   // Player id should not change when there's an active session.
@@ -272,18 +272,39 @@
   // Attempt to switch to no audio player, which should do nothing.
   // TODO(dalecurtis): Delete this test once we're no longer using WMPA and
   // the BrowserMediaPlayerManagers.  Tracked by http://crbug.com/580626
-  ASSERT_TRUE(controller_->Initialize(false, false,
-                                      media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      false, false, media::MediaContentType::Persistent, nullptr));
   EXPECT_TRUE(media_session()->IsActive());
   EXPECT_TRUE(media_session()->IsControllable());
   EXPECT_EQ(current_player_id, controller_->get_player_id_for_testing());
 
   // Switch to a remote player, which should release the session.
-  ASSERT_TRUE(
-      controller_->Initialize(true, true, media::MediaContentType::Persistent));
+  ASSERT_TRUE(controller_->Initialize(
+      true, true, media::MediaContentType::Persistent, nullptr));
   EXPECT_FALSE(media_session()->IsActive());
   EXPECT_FALSE(media_session()->IsControllable());
   EXPECT_EQ(current_player_id, controller_->get_player_id_for_testing());
 }
 
+TEST_F(MediaSessionControllerTest, PositionState) {
+  {
+    media_session::MediaPosition expected_position(1.0, base::TimeDelta(),
+                                                   base::TimeDelta());
+
+    ASSERT_TRUE(controller_->Initialize(
+        true, true, media::MediaContentType::Persistent, &expected_position));
+
+    EXPECT_EQ(expected_position, controller_->get_position_for_testing());
+  }
+
+  {
+    media_session::MediaPosition expected_position(
+        0.0, base::TimeDelta::FromSeconds(10), base::TimeDelta());
+
+    controller_->OnMediaPositionStateChanged(expected_position);
+
+    EXPECT_EQ(expected_position, controller_->get_position_for_testing());
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/media/session/media_session_controllers_manager.cc b/content/browser/media/session/media_session_controllers_manager.cc
index 5ab217ff..5d3edf8 100644
--- a/content/browser/media/session/media_session_controllers_manager.cc
+++ b/content/browser/media/session/media_session_controllers_manager.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/media/session/media_session_controllers_manager.h"
 
+#include "base/stl_util.h"
 #include "content/browser/media/session/media_session_controller.h"
 #include "media/base/media_switches.h"
 #include "services/media_session/public/cpp/features.h"
@@ -47,6 +48,13 @@
   if (!IsMediaSessionEnabled())
     return true;
 
+  // If we have previously received the position for this player then we should
+  // initialize the controller with it.
+  media_session::MediaPosition* position = nullptr;
+  auto position_it = position_map_.find(id);
+  if (position_it != position_map_.end())
+    position = &position_it->second;
+
   // Since we don't remove session instances on pause, there may be an existing
   // instance for this playback attempt.
   //
@@ -55,8 +63,11 @@
   // controller. A later playback attempt will create a new controller.
   auto it = controllers_map_.find(id);
   if (it != controllers_map_.end()) {
-    if (it->second->Initialize(has_audio, is_remote, media_content_type))
+    if (it->second->Initialize(has_audio, is_remote, media_content_type,
+                               position)) {
       return true;
+    }
+
     controllers_map_.erase(it);
     return false;
   }
@@ -64,8 +75,10 @@
   std::unique_ptr<MediaSessionController> controller(
       new MediaSessionController(id, media_web_contents_observer_));
 
-  if (!controller->Initialize(has_audio, is_remote, media_content_type))
+  if (!controller->Initialize(has_audio, is_remote, media_content_type,
+                              position)) {
     return false;
+  }
 
   controllers_map_[id] = std::move(controller);
   return true;
@@ -88,6 +101,21 @@
   controllers_map_.erase(id);
 }
 
+void MediaSessionControllersManager::OnMediaPositionStateChanged(
+    const MediaPlayerId& id,
+    const media_session::MediaPosition& position) {
+  if (!IsMediaSessionEnabled())
+    return;
+
+  base::InsertOrAssign(position_map_, id, position);
+
+  auto it = controllers_map_.find(id);
+  if (it == controllers_map_.end())
+    return;
+
+  it->second->OnMediaPositionStateChanged(position);
+}
+
 void MediaSessionControllersManager::WebContentsMutedStateChanged(bool muted) {
   if (!IsMediaSessionEnabled())
     return;
diff --git a/content/browser/media/session/media_session_controllers_manager.h b/content/browser/media/session/media_session_controllers_manager.h
index ed8aaac6..d294ace6 100644
--- a/content/browser/media/session/media_session_controllers_manager.h
+++ b/content/browser/media/session/media_session_controllers_manager.h
@@ -18,6 +18,10 @@
 enum class MediaContentType;
 }  // namespace media
 
+namespace media_session {
+struct MediaPosition;
+}  // namespace media_session
+
 namespace content {
 
 class MediaSessionController;
@@ -30,6 +34,7 @@
  public:
   using ControllersMap =
       std::map<MediaPlayerId, std::unique_ptr<MediaSessionController>>;
+  using PositionMap = std::map<MediaPlayerId, media_session::MediaPosition>;
 
   explicit MediaSessionControllersManager(
       MediaWebContentsObserver* media_web_contents_observer);
@@ -53,6 +58,11 @@
   // Called when the given player |id| has ended.
   void OnEnd(const MediaPlayerId& id);
 
+  // Called when the media position state for the player |id| has changed.
+  void OnMediaPositionStateChanged(
+      const MediaPlayerId& id,
+      const media_session::MediaPosition& position);
+
   // Called when the WebContents was muted or unmuted.
   void WebContentsMutedStateChanged(bool muted);
 
@@ -64,6 +74,10 @@
 
   ControllersMap controllers_map_;
 
+  // Stores the last position for each player. This is because a controller
+  // may be created after we have already received the position state.
+  PositionMap position_map_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaSessionControllersManager);
 };
 
diff --git a/content/browser/media/session/media_session_controllers_manager_unittest.cc b/content/browser/media/session/media_session_controllers_manager_unittest.cc
index 0dbd9e1..55b56d3 100644
--- a/content/browser/media/session/media_session_controllers_manager_unittest.cc
+++ b/content/browser/media/session/media_session_controllers_manager_unittest.cc
@@ -96,6 +96,12 @@
     return &manager_->controllers_map_;
   }
 
+  MediaSessionController* GetController(const MediaPlayerId& id) {
+    auto it = manager_->controllers_map_.find(id);
+    DCHECK(it != manager_->controllers_map_.end());
+    return it->second.get();
+  }
+
   void TearDown() override {
     manager_.reset();
     service_manager_context_.reset();
@@ -214,6 +220,94 @@
   }
 }
 
+TEST_P(MediaSessionControllersManagerTest, PositionState) {
+  // If not enabled, no adds will occur, as RequestPlay returns early.
+  if (!IsMediaSessionEnabled())
+    return;
+
+  {
+    media_session::MediaPosition expected_position(1.0, base::TimeDelta(),
+                                                   base::TimeDelta());
+
+    manager_->OnMediaPositionStateChanged(media_player_id_, expected_position);
+
+    EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false,
+                                      media::MediaContentType::Transient));
+    EXPECT_EQ(1U, GetControllersMap()->size());
+
+    // The controller should be created with the last received position for
+    // that player.
+    EXPECT_EQ(expected_position,
+              GetController(media_player_id_)->get_position_for_testing());
+  }
+
+  {
+    media_session::MediaPosition expected_position(
+        0.0, base::TimeDelta::FromSeconds(10), base::TimeDelta());
+
+    manager_->OnMediaPositionStateChanged(media_player_id_, expected_position);
+
+    // The controller should be updated with the new position.
+    EXPECT_EQ(expected_position,
+              GetController(media_player_id_)->get_position_for_testing());
+
+    // Destroy the current controller.
+    manager_->OnEnd(media_player_id_);
+    EXPECT_TRUE(GetControllersMap()->empty());
+
+    // Recreate the current controller.
+    EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false,
+                                      media::MediaContentType::Transient));
+    EXPECT_EQ(1U, GetControllersMap()->size());
+
+    // The controller should be created with the last received position for
+    // that player.
+    EXPECT_EQ(expected_position,
+              GetController(media_player_id_)->get_position_for_testing());
+  }
+}
+
+TEST_P(MediaSessionControllersManagerTest, MultiplePlayersWithPositionState) {
+  // If not enabled, no adds will occur, as RequestPlay returns early.
+  if (!IsMediaSessionEnabled())
+    return;
+
+  MediaPlayerId media_player_id_2 =
+      MediaPlayerId(contents()->GetMainFrame(), 2);
+
+  media_session::MediaPosition expected_position1(1.0, base::TimeDelta(),
+                                                  base::TimeDelta());
+  media_session::MediaPosition expected_position2(
+      0.0, base::TimeDelta::FromSeconds(10), base::TimeDelta());
+
+  manager_->OnMediaPositionStateChanged(media_player_id_, expected_position1);
+  manager_->OnMediaPositionStateChanged(media_player_id_2, expected_position2);
+
+  EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false,
+                                    media::MediaContentType::Transient));
+  EXPECT_TRUE(manager_->RequestPlay(media_player_id_2, true, false,
+                                    media::MediaContentType::Transient));
+
+  EXPECT_EQ(2U, GetControllersMap()->size());
+
+  // The controllers should have been created with the correct positions.
+  EXPECT_EQ(expected_position1,
+            GetController(media_player_id_)->get_position_for_testing());
+  EXPECT_EQ(expected_position2,
+            GetController(media_player_id_2)->get_position_for_testing());
+
+  media_session::MediaPosition new_position(
+      0.0, base::TimeDelta::FromSeconds(20), base::TimeDelta());
+
+  manager_->OnMediaPositionStateChanged(media_player_id_, new_position);
+
+  // The controller should be updated with the new position.
+  EXPECT_EQ(new_position,
+            GetController(media_player_id_)->get_position_for_testing());
+  EXPECT_EQ(expected_position2,
+            GetController(media_player_id_2)->get_position_for_testing());
+}
+
 // First bool is to indicate whether InternalMediaSession is enabled.
 // Second bool is to indicate whether AudioFocus is enabled.
 INSTANTIATE_TEST_SUITE_P(MediaSessionEnabledTestInstances,
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index b64df45..3176e47 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -847,6 +847,13 @@
   std::vector<media_session::mojom::MediaSessionAction> actions(
       actions_.begin(), actions_.end());
   media_session_observer->MediaSessionActionsChanged(actions);
+
+  // TODO(crbug.com/985394): Use real position data here, this is mock data.
+  media_session::MediaPosition media_position(
+      1 /* playback_rate */, base::TimeDelta::FromSeconds(600) /* duration */,
+      base::TimeDelta::FromSeconds(300) /* position */);
+  media_session_observer->MediaSessionPositionChanged(media_position);
+
   observers_.Add(std::move(media_session_observer));
 }
 
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index 53f48e8..31504f5a 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -274,6 +274,14 @@
   ASSERT_EQ(services_[sub_frame_].get(), ComputeServiceForRouting());
 }
 
+TEST_F(MediaSessionImplServiceRoutingTest, NotifyMockPositionData) {
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+
+  // Verify that the default/mock position data is received by the observer.
+  observer.WaitForNonEmptyPosition();
+}
+
 TEST_F(MediaSessionImplServiceRoutingTest,
        DontNotifyMetadataAndActionsChangeWhenUncontrollable) {
   media_session::test::MockMediaSessionMojoObserver observer(
diff --git a/content/browser/media/system_media_controls_notifier.h b/content/browser/media/system_media_controls_notifier.h
index e5cd260..c403ea9 100644
--- a/content/browser/media/system_media_controls_notifier.h
+++ b/content/browser/media/system_media_controls_notifier.h
@@ -48,6 +48,8 @@
       override {}
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override {}
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override {}
 
   // media_session::mojom::MediaControllerImageObserver implementation.
   void MediaControllerImageChanged(
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 4e8448e..6e06e063 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -1726,8 +1726,10 @@
       popup,
       "window.opener.location ='data:html/text;base64,'+btoa('payload');"));
   observer.WaitForFinished();
+
+  // Implies NavigationDownloadType::kOpenerCrossOrigin has 0 count.
   histograms.ExpectUniqueSample("Navigation.DownloadPolicy.LogPerPolicyApplied",
-                                NavigationDownloadType::kDefaultAllow, 1);
+                                NavigationDownloadType::kNoGesture, 1);
 }
 
 // A variation of the OpenerNavigation_DownloadPolicy test above, but uses a
@@ -1779,7 +1781,7 @@
   // boolean before the navigation turns into a download. Just expect metrics
   // when the network service is enabled.
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    histograms.ExpectUniqueSample(
+    histograms.ExpectBucketCount(
         "Navigation.DownloadPolicy.LogPerPolicyApplied",
         NavigationDownloadType::kOpenerCrossOrigin, 1);
   }
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index 16fba62..e566b73 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -31,7 +31,6 @@
 
 # Embedded Frame Sinks (eg. Offscreen Canvas)
 per-file embedded_frame_sink*.*=xlai@chromium.org
-per-file embedded_frame_sink*.*=xidachen@chromium.org
 per-file embedded_frame_sink*.*=junov@chromium.org
 per-file embedded_frame_sink*.*=kylechar@chromium.org
 
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index 2e13ade..3024a12 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -5,8 +5,6 @@
 #include "content/browser/renderer_host/render_widget_targeter.h"
 
 #include "base/bind.h"
-#include "base/debug/crash_logging.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
@@ -14,11 +12,8 @@
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/renderer_host/input/one_shot_timeout_monitor.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/browser/scoped_active_url.h"
-#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/site_isolation_policy.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "ui/events/blink/blink_event_util.h"
@@ -45,33 +40,6 @@
 
 constexpr const char kTracingCategory[] = "input,latency";
 
-// This function helps with debugging the reasons of viz hit testing mismatch.
-void DumpWithoutCrashing(RenderWidgetHostViewBase* root_view,
-                         RenderWidgetHostViewBase* target,
-                         const base::Optional<gfx::PointF>& target_location) {
-  RenderViewHostImpl* rvh =
-      RenderViewHostImpl::From(root_view->GetRenderWidgetHost());
-  if (!rvh || !rvh->GetMainFrame())
-    return;
-  ScopedActiveURL scoped_active_url(rvh);
-
-  static auto* crash_key = base::debug::AllocateCrashKeyString(
-      "vizhittest-mismatch-v2-coordinate", base::debug::CrashKeySize::Size32);
-  const std::string global_coordinate =
-      target->TransformPointToRootCoordSpaceF(target_location.value())
-          .ToString();
-  base::debug::SetCrashKeyString(crash_key, global_coordinate);
-
-  crash_key = base::debug::AllocateCrashKeyString(
-      "vizhittest-mismatch-v2-viewport-size",
-      base::debug::CrashKeySize::Size32);
-  const std::string viewport_size =
-      root_view->GetVisibleViewportSize().ToString();
-  base::debug::SetCrashKeyString(crash_key, viewport_size);
-
-  base::debug::DumpWithoutCrashing();
-}
-
 }  // namespace
 
 class TracingUmaTracker {
@@ -88,11 +56,11 @@
   TracingUmaTracker(TracingUmaTracker&& tracker) = default;
 
   void StopAndRecord() {
-    Stop();
+    StopButNotRecord();
     UmaHistogramTimes(metric_name_, base::TimeTicks::Now() - start_time_);
   }
 
-  void Stop() {
+  void StopButNotRecord() {
     TRACE_EVENT_ASYNC_END0(
         kTracingCategory, metric_name_,
         TRACE_ID_WITH_SCOPE("UmaTracker", TRACE_ID_LOCAL(id_)));
@@ -192,7 +160,7 @@
 
 void RenderWidgetTargeter::TargetingRequest::StopQueueingTimeTracker() {
   if (tracker)
-    tracker->Stop();
+    tracker->StopAndRecord();
 }
 
 bool RenderWidgetTargeter::TargetingRequest::IsWebInputEventRequest() const {
@@ -432,7 +400,7 @@
     const viz::FrameSinkId& frame_sink_id,
     const gfx::PointF& transformed_location) {
   if (is_verification_request) {
-    tracker.Stop();
+    tracker.StopButNotRecord();
   } else {
     tracker.StopAndRecord();
   }
@@ -545,7 +513,6 @@
         // If the result did not change, it is likely that viz hit test finds
         // the wrong target.
         match_result = HitTestResultsMatch::kDoNotMatch;
-        DumpWithoutCrashing(request->GetRootView(), target, target_location);
       } else {
         // Hit test data changed, so the result is no longer reliable.
         match_result = HitTestResultsMatch::kHitTestResultChanged;
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index fc8b53d..6f253f93 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -780,7 +780,7 @@
     blink::mojom::EmbeddedWorkerStartParamsPtr params) {
   DCHECK(context_);
   DCHECK(params->service_worker_request.is_pending());
-  DCHECK(params->controller_request.is_pending());
+  DCHECK(params->controller_receiver.is_valid());
   DCHECK(!instance_host_binding_.is_bound());
 
   instance_host_binding_.Bind(mojo::MakeRequest(&params->instance_host));
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 9357e7a..ee994583 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -27,6 +27,8 @@
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -163,7 +165,7 @@
     params->is_installed = false;
 
     params->service_worker_request = CreateServiceWorker();
-    params->controller_request = CreateController();
+    params->controller_receiver = CreateController();
     params->installed_scripts_info = GetInstalledScriptsInfoPtr();
     params->provider_info = CreateProviderInfo(std::move(version));
     return params;
@@ -183,9 +185,10 @@
     return mojo::MakeRequest(&service_workers_.back());
   }
 
-  blink::mojom::ControllerServiceWorkerRequest CreateController() {
+  mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+  CreateController() {
     controllers_.emplace_back();
-    return mojo::MakeRequest(&controllers_.back());
+    return controllers_.back().BindNewPipeAndPassReceiver();
   }
 
   void SetWorkerStatus(EmbeddedWorkerInstance* worker,
@@ -208,7 +211,7 @@
 
   // Mojo endpoints.
   std::vector<blink::mojom::ServiceWorkerPtr> service_workers_;
-  std::vector<blink::mojom::ControllerServiceWorkerPtr> controllers_;
+  std::vector<mojo::Remote<blink::mojom::ControllerServiceWorker>> controllers_;
   std::vector<blink::mojom::ServiceWorkerInstalledScriptsManagerPtr>
       installed_scripts_managers_;
   std::vector<blink::mojom::ServiceWorkerInstalledScriptsManagerHostRequest>
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 3db775f6..8a9e819 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -215,7 +215,6 @@
         base::BindRepeating(&ServiceWorkerContextWrapper::OnRegistrationUpdated,
                             base::Unretained(this)),
         base::DoNothing(), base::DoNothing());
-    watcher_->Start();
   }
 }
 
@@ -227,6 +226,8 @@
     URLLoaderFactoryGetter* loader_factory_getter) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(storage_partition_);
+  if (watcher_)
+    watcher_->Start();
 
   is_incognito_ = user_data_directory.empty();
   // The database task runner is BLOCK_SHUTDOWN in order to support
@@ -251,7 +252,7 @@
 
   storage_partition_ = nullptr;
   process_manager_->Shutdown();
-  if (NavigationURLLoaderImpl::IsNavigationLoaderOnUIEnabled()) {
+  if (watcher_) {
     watcher_->Stop();
     watcher_ = nullptr;
   }
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 94a1d2e..78900e0 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -169,13 +169,17 @@
   SubresourceLoaderParams params;
   auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New();
   controller_info->mode = provider_host_->GetControllerMode();
-  // Note that |controller_info->endpoint| is null if the controller has no
-  // fetch event handler. In that case the renderer frame won't get the
+  // Note that |controller_info->remote_controller| is null if the controller
+  // has no fetch event handler. In that case the renderer frame won't get the
   // controller pointer upon the navigation commit, and subresource loading will
   // not be intercepted. (It might get intercepted later if the controller
   // changes due to skipWaiting() so SetController is sent.)
-  controller_info->endpoint =
-      provider_host_->GetControllerServiceWorkerPtr().PassInterface();
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote =
+      provider_host_->GetRemoteControllerServiceWorker();
+  if (remote.is_bound()) {
+    controller_info->remote_controller = remote.Unbind();
+  }
+
   controller_info->client_id = provider_host_->client_uuid();
   if (provider_host_->fetch_request_window_id()) {
     controller_info->fetch_request_window_id =
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 c6917ee..02bbd1bc 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -54,7 +54,7 @@
                          ResourceContext* resource_context,
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override;
-  // Returns params with the ControllerServiceWorkerPtr if we have found
+  // Returns params with the ControllerServiceWorkerInfoPtr if we have found
   // a matching controller service worker for the |request| that is given
   // to MaybeCreateLoader(). Otherwise this returns base::nullopt.
   base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams()
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
index 9fa5afa..a3b6bce 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
@@ -63,7 +63,7 @@
                          ResourceContext* resource_context,
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override;
-  // Returns params with the ControllerServiceWorkerPtr if we have found
+  // Returns params with the ControllerServiceWorkerInfoPtr if we have found
   // a matching controller service worker for the |request| that is given
   // to MaybeCreateLoader(). Otherwise this returns base::nullopt.
   base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams()
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 432083a..51d4f0d 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -366,16 +366,17 @@
   UpdateController(true /* notify_controllerchange */);
 }
 
-blink::mojom::ControllerServiceWorkerPtr
-ServiceWorkerProviderHost::GetControllerServiceWorkerPtr() {
+mojo::Remote<blink::mojom::ControllerServiceWorker>
+ServiceWorkerProviderHost::GetRemoteControllerServiceWorker() {
   DCHECK(controller_);
   if (controller_->fetch_handler_existence() ==
       ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST) {
-    return nullptr;
+    return mojo::Remote<blink::mojom::ControllerServiceWorker>();
   }
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr;
-  controller_->controller()->Clone(mojo::MakeRequest(&controller_ptr));
-  return controller_ptr;
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller;
+  controller_->controller()->Clone(
+      remote_controller.BindNewPipeAndPassReceiver());
+  return remote_controller;
 }
 
 void ServiceWorkerProviderHost::UpdateUrls(const GURL& url,
@@ -778,7 +779,11 @@
   controller_info->mode = GetControllerMode();
 
   // Pass an endpoint for the client to talk to this controller.
-  controller_info->endpoint = GetControllerServiceWorkerPtr().PassInterface();
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote =
+      GetRemoteControllerServiceWorker();
+  if (remote.is_bound()) {
+    controller_info->remote_controller = remote.Unbind();
+  }
 
   // Set the info for the JavaScript ServiceWorkerContainer#controller object.
   base::WeakPtr<ServiceWorkerObjectHost> object_host =
@@ -1129,14 +1134,14 @@
 }
 
 void ServiceWorkerProviderHost::StartControllerComplete(
-    blink::mojom::ControllerServiceWorkerRequest controller_request,
+    mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
     blink::ServiceWorkerStatusCode status) {
   if (status == blink::ServiceWorkerStatusCode::kOk)
-    controller_->controller()->Clone(std::move(controller_request));
+    controller_->controller()->Clone(std::move(receiver));
 }
 
 void ServiceWorkerProviderHost::EnsureControllerServiceWorker(
-    blink::mojom::ControllerServiceWorkerRequest controller_request,
+    mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
     blink::mojom::ControllerServiceWorkerPurpose purpose) {
   // TODO(kinuko): Log the reasons we drop the request.
   if (!IsContextAlive() || !controller_)
@@ -1145,7 +1150,7 @@
   controller_->RunAfterStartWorker(
       PurposeToEventType(purpose),
       base::BindOnce(&ServiceWorkerProviderHost::StartControllerComplete,
-                     AsWeakPtr(), std::move(controller_request)));
+                     AsWeakPtr(), std::move(receiver)));
 }
 
 void ServiceWorkerProviderHost::CloneContainerHost(
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 1507692..f95b687 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -27,6 +27,8 @@
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
@@ -229,7 +231,10 @@
   //
   // TODO(kinuko): revisit this if we start to use the ControllerServiceWorker
   // for posting messages.
-  blink::mojom::ControllerServiceWorkerPtr GetControllerServiceWorkerPtr();
+  // TODO(hayato): Return PendingRemote, instead of Remote. Binding to Remote
+  // as late as possible is more idiomatic for new Mojo types.
+  mojo::Remote<blink::mojom::ControllerServiceWorker>
+  GetRemoteControllerServiceWorker();
 
   // For service worker clients. Sets |url_| and |site_for_cookies_| and updates
   // the client uuid if it's a cross-origin transition.
@@ -494,7 +499,7 @@
   void GetRegistrationForReady(
       GetRegistrationForReadyCallback callback) override;
   void EnsureControllerServiceWorker(
-      blink::mojom::ControllerServiceWorkerRequest controller_request,
+      mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
       blink::mojom::ControllerServiceWorkerPurpose purpose) override;
   void CloneContainerHost(blink::mojom::ServiceWorkerContainerHostRequest
                               container_host_request) override;
@@ -525,7 +530,7 @@
 
   // Callback for ServiceWorkerVersion::RunAfterStartWorker()
   void StartControllerComplete(
-      blink::mojom::ControllerServiceWorkerRequest controller_request,
+      mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
       blink::ServiceWorkerStatusCode status);
 
   bool IsValidGetRegistrationMessage(const GURL& client_url,
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 7235a21..c11b6b2 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -798,10 +798,22 @@
   SetStatus(REDUNDANT);
   if (running_status() == EmbeddedWorkerStatus::STARTING ||
       running_status() == EmbeddedWorkerStatus::RUNNING) {
-    if (embedded_worker()->devtools_attached())
-      stop_when_devtools_detached_ = true;
-    else
+    // |start_worker_status_| == kErrorExists means that this version was
+    // created for update but the script was identical to the incumbent version.
+    // In this case we should stop the worker immediately even when DevTools is
+    // attached. Otherwise the redundant worker stays as a selectable context
+    // in DevTools' console.
+    // TODO(bashi): Remove this workaround when byte-for-byte update check is
+    // shipped.
+    bool stop_immediately =
+        base::FeatureList::IsEnabled(
+            blink::features::kOffMainThreadServiceWorkerScriptFetch) &&
+        start_worker_status_ == blink::ServiceWorkerStatusCode::kErrorExists;
+    if (stop_immediately || !embedded_worker()->devtools_attached()) {
       embedded_worker_->Stop();
+    } else {
+      stop_when_devtools_detached_ = true;
+    }
   }
 }
 
@@ -1622,11 +1634,10 @@
   if (!pause_after_download())
     InitializeGlobalScope();
 
-  if (!controller_request_.is_pending()) {
-    DCHECK(!controller_ptr_.is_bound());
-    controller_request_ = mojo::MakeRequest(&controller_ptr_);
+  if (!controller_receiver_.is_valid()) {
+    controller_receiver_ = remote_controller_.BindNewPipeAndPassReceiver();
   }
-  params->controller_request = std::move(controller_request_);
+  params->controller_receiver = std::move(controller_receiver_);
 
   params->provider_info = std::move(provider_info);
 
@@ -1984,8 +1995,8 @@
   request_timeouts_.clear();
   external_request_uuid_to_request_id_.clear();
   service_worker_ptr_.reset();
-  controller_ptr_.reset();
-  DCHECK(!controller_request_.is_pending());
+  remote_controller_.reset();
+  DCHECK(!controller_receiver_.is_valid());
   installed_scripts_sender_.reset();
   binding_.Close();
   pending_external_requests_.clear();
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 36863e7..bc5b68a68 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -39,6 +39,8 @@
 #include "content/common/content_export.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
@@ -343,11 +345,11 @@
   // not running is a bit sketchy, maybe this should queue a task to check
   // if the pending request is pending too long? https://crbug.com/797222
   blink::mojom::ControllerServiceWorker* controller() {
-    if (!controller_ptr_.is_bound()) {
-      DCHECK(!controller_request_.is_pending());
-      controller_request_ = mojo::MakeRequest(&controller_ptr_);
+    if (!remote_controller_.is_bound()) {
+      DCHECK(!controller_receiver_.is_valid());
+      controller_receiver_ = remote_controller_.BindNewPipeAndPassReceiver();
     }
-    return controller_ptr_.get();
+    return remote_controller_.get();
   }
 
   // Adds and removes the specified host as a controllee of this service worker.
@@ -843,11 +845,12 @@
   blink::mojom::ServiceWorkerPtr service_worker_ptr_;
 
   // Connection to the controller service worker.
-  // |controller_request_| is non-null only when the |controller_ptr_| is
+  // |controller_receiver_| is non-null only when the |remote_controller_| is
   // requested before the worker is started, it is passed to the worker (and
   // becomes null) once it's started.
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr_;
-  blink::mojom::ControllerServiceWorkerRequest controller_request_;
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller_;
+  mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+      controller_receiver_;
 
   std::unique_ptr<ServiceWorkerInstalledScriptsSender>
       installed_scripts_sender_;
diff --git a/content/browser/shape_detection/OWNERS b/content/browser/shape_detection/OWNERS
new file mode 100644
index 0000000..aee8252f
--- /dev/null
+++ b/content/browser/shape_detection/OWNERS
@@ -0,0 +1,4 @@
+file://third_party/blink/renderer/modules/shapedetection/OWNERS
+
+# COMPONENT: Blink>ShapeDetection
+# TEAM: device-dev@chromium.org
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index abcb777ca..a805b110 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -410,6 +410,20 @@
   browsing_instance_->GetSiteAndLockForURL(
       url, /* allow_default_instance */ true, &site_url, &origin_lock);
 
+  // If this is a default SiteInstance and the BrowsingInstance gives us a
+  // non-default site URL even when we explicitly allow the default SiteInstance
+  // to be considered, then |url| does not belong in the same process as this
+  // SiteInstance. This can happen when the
+  // kProcessSharingWithDefaultSiteInstances feature is not enabled and the
+  // site URL is explicitly set on a SiteInstance for a URL that would normally
+  // be directed to the default SiteInstance (e.g. a site not requiring a
+  // dedicated process). This situation typically happens when the top-level
+  // frame is a site that should be in the default SiteInstance and the
+  // SiteInstance associated with that frame is initially a SiteInstance with
+  // no site URL set.
+  if (IsDefaultSiteInstance() && site_url != GetSiteURL())
+    return true;
+
   // Note that HasProcess() may return true if process_ is null, in
   // process-per-site cases where there's an existing process available.
   // We want to use such a process in the IsSuitableHost check, so we
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index d0e14f25..a3e2fc3c 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/stl_util.h"
 #include "base/task/post_task.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/test_timeouts.h"
 #include "build/build_config.h"
@@ -2931,6 +2932,47 @@
   EXPECT_FALSE(child_frame_monitor.EventWasReceived());
 }
 
+IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
+                       RecordTimeInQueueMetric) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "/frame_tree/page_with_masked_iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child_node = root->child_at(0);
+
+  RenderWidgetHostInputEventRouter* router =
+      web_contents()->GetInputEventRouter();
+
+  WaitForHitTestDataOrChildSurfaceReady(child_node->current_frame_host());
+
+  router->GetRenderWidgetTargeterForTests()
+      ->set_async_hit_test_timeout_delay_for_testing(base::TimeDelta());
+
+  RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
+      root->current_frame_host()->GetRenderWidgetHost()->GetView());
+
+  blink::WebMouseEvent child_event(
+      blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  child_event.button = blink::WebPointerProperties::Button::kLeft;
+  SetWebEventPositions(&child_event, gfx::Point(75, 75), root_view);
+  child_event.click_count = 1;
+
+  base::HistogramTester tester;
+
+  InputEventAckWaiter waiter(root_view->GetRenderWidgetHost(),
+                             blink::WebInputEvent::kMouseDown);
+  // Need at least two events to generate a queue.
+  router->RouteMouseEvent(root_view, &child_event, ui::LatencyInfo());
+  router->RouteMouseEvent(root_view, &child_event, ui::LatencyInfo());
+  waiter.Wait();
+
+  tester.ExpectTotalCount("Event.AsyncTargeting.TimeInQueue", 1);
+}
+
 // Tooltips aren't used on Android, so no need to compile/run this test in that
 // case.
 #if !defined(OS_ANDROID)
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc
index 041a0d7..527628e 100644
--- a/content/browser/sms/sms_browsertest.cc
+++ b/content/browser/sms/sms_browsertest.cc
@@ -146,74 +146,53 @@
   ASSERT_FALSE(provider->HasObservers());
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, ReceiveMultiple) {
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest, AtMostOnePendingSmsRequest) {
   GURL url = GetTestUrl(nullptr, "simple_page.html");
   NavigateToURL(shell(), url);
 
   shell()->web_contents()->SetDelegate(&delegate_);
 
-  auto* dialog1 = new NiceMock<MockSmsDialog>();
-  auto* dialog2 = new NiceMock<MockSmsDialog>();
+  auto* dialog = new NiceMock<MockSmsDialog>();
 
-  base::OnceClosure on_continue_callback1;
-  base::OnceClosure on_continue_callback2;
+  base::OnceClosure on_continue_callback;
 
   EXPECT_CALL(delegate_, CreateSmsDialog())
-      .WillOnce(Return(ByMove(base::WrapUnique(dialog1))))
-      .WillOnce(Return(ByMove(base::WrapUnique(dialog2))));
+      .WillOnce(Return(ByMove(base::WrapUnique(dialog))));
 
-  EXPECT_CALL(*dialog1, Open(_, _, _))
-      .WillOnce(Invoke([&on_continue_callback1](content::RenderFrameHost*,
-                                                base::OnceClosure on_continue,
-                                                base::OnceClosure on_cancel) {
-        on_continue_callback1 = std::move(on_continue);
+  EXPECT_CALL(*dialog, Open(_, _, _))
+      .WillOnce(Invoke([&on_continue_callback](content::RenderFrameHost*,
+                                               base::OnceClosure on_continue,
+                                               base::OnceClosure on_cancel) {
+        on_continue_callback = std::move(on_continue);
       }));
 
-  EXPECT_CALL(*dialog2, Open(_, _, _))
-      .WillOnce(Invoke([&on_continue_callback2](content::RenderFrameHost*,
-                                                base::OnceClosure on_continue,
-                                                base::OnceClosure on_cancel) {
-        on_continue_callback2 = std::move(on_continue);
-      }));
-
-  EXPECT_CALL(*dialog1, EnableContinueButton())
-      .WillOnce(Invoke([&on_continue_callback1]() {
-        std::move(on_continue_callback1).Run();
-      }));
-  EXPECT_CALL(*dialog2, EnableContinueButton())
-      .WillOnce(Invoke([&on_continue_callback2]() {
-        std::move(on_continue_callback2).Run();
+  EXPECT_CALL(*dialog, EnableContinueButton())
+      .WillOnce(Invoke([&on_continue_callback]() {
+        std::move(on_continue_callback).Run();
       }));
 
   auto* provider = new NiceMock<MockSmsProvider>();
   BrowserMainLoop::GetInstance()->SetSmsProviderForTesting(
       base::WrapUnique(provider));
 
-  // Test that SMS content can retrieve multiple messages.
   std::string script = R"(
-    (async () => {
-      let sms1 = navigator.sms.receive();
-      let sms2 = navigator.sms.receive();
-
-      let msg1 = await sms1;
-      let msg2 = await sms2;
-
-      return [msg1.content, msg2.content];
-    }) ();
+    navigator.sms.receive().then(({content}) => { first = content; });
+    navigator.sms.receive().catch(e => e.name);
   )";
 
-  EXPECT_CALL(*provider, Retrieve())
-      .WillOnce(Invoke([&provider, &url]() {
-        provider->NotifyReceive(url::Origin::Create(url), "hello1");
-      }))
-      .WillOnce(Invoke([&provider, &url]() {
-        provider->NotifyReceive(url::Origin::Create(url), "hello2");
-      }));
+  base::RunLoop loop;
 
-  base::ListValue result = EvalJs(shell(), script).ExtractList();
-  ASSERT_EQ(2u, result.GetList().size());
-  EXPECT_EQ("hello1", result.GetList()[0].GetString());
-  EXPECT_EQ("hello2", result.GetList()[1].GetString());
+  EXPECT_CALL(*provider, Retrieve()).WillOnce(Invoke([&loop]() {
+    loop.Quit();
+  }));
+
+  EXPECT_EQ("AbortError", EvalJs(shell(), script));
+
+  loop.Run();
+
+  provider->NotifyReceive(url::Origin::Create(url), "hello");
+
+  EXPECT_EQ("hello", EvalJs(shell(), "first"));
 
   ASSERT_FALSE(provider->HasObservers());
 }
diff --git a/content/browser/tracing/background_tracing_config_impl.cc b/content/browser/tracing/background_tracing_config_impl.cc
index de6804f..86601fb 100644
--- a/content/browser/tracing/background_tracing_config_impl.cc
+++ b/content/browser/tracing/background_tracing_config_impl.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/process/process_handle.h"
 #include "base/system/sys_info.h"
 #include "base/values.h"
@@ -270,8 +271,11 @@
 
 size_t BackgroundTracingConfigImpl::GetTraceUploadLimitKb() const {
 #if defined(OS_ANDROID)
-  if (net::NetworkChangeNotifier::IsConnectionCellular(
-          net::NetworkChangeNotifier::GetConnectionType())) {
+  auto type = net::NetworkChangeNotifier::GetConnectionType();
+  UMA_HISTOGRAM_ENUMERATION(
+      "Tracing.Background.NetworkConnectionTypeWhenUploaded", type,
+      net::NetworkChangeNotifier::CONNECTION_LAST + 1);
+  if (net::NetworkChangeNotifier::IsConnectionCellular(type)) {
     return upload_limit_network_kb_;
   }
 #endif
@@ -521,8 +525,11 @@
     return low_ram_buffer_size_kb_;
   }
 #if defined(OS_ANDROID)
-  if (net::NetworkChangeNotifier::IsConnectionCellular(
-          net::NetworkChangeNotifier::GetConnectionType())) {
+  auto type = net::NetworkChangeNotifier::GetConnectionType();
+  UMA_HISTOGRAM_ENUMERATION(
+      "Tracing.Background.NetworkConnectionTypeWhenStarted", type,
+      net::NetworkChangeNotifier::CONNECTION_LAST + 1);
+  if (net::NetworkChangeNotifier::IsConnectionCellular(type)) {
     return mobile_network_buffer_size_kb_;
   }
 #endif
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 0a50495..85b12bc8 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -225,7 +225,7 @@
 
 UtilityProcessHost::~UtilityProcessHost() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (client_)
+  if (client_ && !in_process_thread_)
     client_->OnProcessTerminatedNormally();
 }
 
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index eca62cc..26e06cc 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -1200,14 +1200,14 @@
   base::test::ScopedFeatureList feature_list;
 };
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_SplitCache DISABLED_SplitCache
-#else
-#define MAYBE_SplitCache SplitCache
-#endif
-IN_PROC_BROWSER_TEST_P(WebContentsSplitCacheBrowserTestEnabled,
-                       MAYBE_SplitCache) {
+IN_PROC_BROWSER_TEST_P(WebContentsSplitCacheBrowserTestEnabled, SplitCache) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Load a cacheable resource for the first time, and it's not cached.
   EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
 
@@ -1270,14 +1270,15 @@
   EXPECT_FALSE(TestResourceLoad(blank_url, GURL()));
 }
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_SplitCache DISABLED_SplitCache
-#else
-#define MAYBE_SplitCache SplitCache
-#endif
 IN_PROC_BROWSER_TEST_F(WebContentsSplitCacheWithFrameOriginBrowserTest,
-                       MAYBE_SplitCache) {
+                       SplitCache) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Load a cacheable resource for the first time, and it's not cached.
   EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
 
@@ -1350,14 +1351,15 @@
                                GenURL("c.com", "/title1.html")));
 }
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_SplitCacheDedicatedWorkers DISABLED_SplitCacheDedicatedWorkers
-#else
-#define MAYBE_SplitCacheDedicatedWorkers SplitCacheDedicatedWorkers
-#endif
 IN_PROC_BROWSER_TEST_F(WebContentsSplitCacheWithFrameOriginBrowserTest,
-                       MAYBE_SplitCacheDedicatedWorkers) {
+                       SplitCacheDedicatedWorkers) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Load 3p.com/script from a.com's worker. The first time it's loaded from the
   // network and the second it's cached.
   EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
@@ -1390,14 +1392,15 @@
       GenURL("c.com", "/embedding_worker.js?c")));
 }
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_NavigationResources DISABLED_NavigationResources
-#else
-#define MAYBE_NavigationResources NavigationResources
-#endif
 IN_PROC_BROWSER_TEST_P(WebContentsSplitCacheBrowserTestEnabled,
-                       MAYBE_NavigationResources) {
+                       NavigationResources) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Navigate for the first time, and it's not cached.
   EXPECT_FALSE(
       NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
@@ -1429,14 +1432,15 @@
       GenURL("a.com", "/title1.html"), false));
 }
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_SubframeNavigationResources DISABLED_SubframeNavigationResources
-#else
-#define MAYBE_SubframeNavigationResources SubframeNavigationResources
-#endif
 IN_PROC_BROWSER_TEST_F(WebContentsSplitCacheWithFrameOriginBrowserTest,
-                       MAYBE_SubframeNavigationResources) {
+                       SubframeNavigationResources) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Navigate for the first time, and it's not cached.
   NavigationResourceCached(
       GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
@@ -1466,15 +1470,15 @@
       GenURL("d.com", "/title1.html"), true);
 }
 
-// See: http://crbug.com/983931
-#if defined(OS_ANDROID)
-#define MAYBE_SplitCacheDedicatedWorkers DISABLED_SplitCacheDedicatedWorkers
-#else
-#define MAYBE_SplitCacheDedicatedWorkers SplitCacheDedicatedWorkers
-#endif
-
 IN_PROC_BROWSER_TEST_P(WebContentsSplitCacheBrowserTestEnabled,
-                       MAYBE_SplitCacheDedicatedWorkers) {
+                       SplitCacheDedicatedWorkers) {
+  // This test will fail if there is no network service, as we fill the
+  // network isolation key in network::URLLoader only when there is network
+  // service. If split cache is enabled but the network isolation key is
+  // empty, then resources won't be cached.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   // Load 3p.com/script from a.com's worker. The first time it's loaded from the
   // network and the second it's cached.
   EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 41475e5..d511f80 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -18,10 +18,9 @@
 #include "base/rand_util.h"
 #include "base/strings/string_piece.h"
 #include "base/timer/timer.h"
-#include "build/build_config.h"
 #include "content/browser/bad_message.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/webauth/authenticator_environment_impl.h"
+#include "content/browser/webauth/virtual_fido_discovery_factory.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/navigation_handle.h"
@@ -59,14 +58,6 @@
 #include "url/url_constants.h"
 #include "url/url_util.h"
 
-#if defined(OS_MACOSX)
-#include "device/fido/mac/authenticator.h"
-#endif
-
-#if defined(OS_WIN)
-#include "device/fido/win/authenticator.h"
-#endif
-
 namespace content {
 
 namespace client_data {
@@ -565,12 +556,15 @@
 }
 
 void AuthenticatorCommon::StartMakeCredentialRequest() {
-  request_ = std::make_unique<device::MakeCredentialRequestHandler>(
-      connector_,
-      AuthenticatorEnvironmentImpl::GetInstance()->GetFactory(
+  device::FidoDiscoveryFactory* discovery_factory =
+      AuthenticatorEnvironmentImpl::GetInstance()->GetDiscoveryFactoryOverride(
           static_cast<RenderFrameHostImpl*>(render_frame_host_)
-              ->frame_tree_node()),
-      GetTransports(caller_origin_, transports_),
+              ->frame_tree_node());
+  if (!discovery_factory)
+    discovery_factory = request_delegate_->GetDiscoveryFactory();
+
+  request_ = std::make_unique<device::MakeCredentialRequestHandler>(
+      connector_, discovery_factory, GetTransports(caller_origin_, transports_),
       *ctap_make_credential_request_, *authenticator_selection_criteria_,
       base::BindOnce(&AuthenticatorCommon::OnRegisterResponse,
                      weak_factory_.GetWeakPtr()));
@@ -593,21 +587,19 @@
     request_delegate_->SetMightCreateResidentCredential(true);
   }
   request_->set_observer(request_delegate_.get());
-
-  request_->SetPlatformAuthenticatorOrMarkUnavailable(
-      CreatePlatformAuthenticatorIfAvailable());
 }
 
 void AuthenticatorCommon::StartGetAssertionRequest() {
-  auto opt_platform_authenticator_info =
-      CreatePlatformAuthenticatorIfAvailableAndCheckIfCredentialExists(
-          *ctap_get_assertion_request_);
-  request_ = std::make_unique<device::GetAssertionRequestHandler>(
-      connector_,
-      AuthenticatorEnvironmentImpl::GetInstance()->GetFactory(
+  device::FidoDiscoveryFactory* discovery_factory =
+      AuthenticatorEnvironmentImpl::GetInstance()->GetDiscoveryFactoryOverride(
           static_cast<RenderFrameHostImpl*>(render_frame_host_)
-              ->frame_tree_node()),
-      GetTransports(caller_origin_, transports_), *ctap_get_assertion_request_,
+              ->frame_tree_node());
+  if (!discovery_factory)
+    discovery_factory = request_delegate_->GetDiscoveryFactory();
+
+  request_ = std::make_unique<device::GetAssertionRequestHandler>(
+      connector_, discovery_factory, GetTransports(caller_origin_, transports_),
+      *ctap_get_assertion_request_,
       base::BindOnce(&AuthenticatorCommon::OnSignResponse,
                      weak_factory_.GetWeakPtr()));
 
@@ -625,10 +617,8 @@
       base::BindRepeating(
           &device::FidoRequestHandlerBase::InitiatePairingWithDevice,
           request_->GetWeakPtr()) /* ble_pairing_callback*/);
-  request_->set_observer(request_delegate_.get());
 
-  request_->SetPlatformAuthenticatorOrMarkUnavailable(
-      std::move(opt_platform_authenticator_info));
+  request_->set_observer(request_delegate_.get());
 }
 
 bool AuthenticatorCommon::IsFocused() const {
@@ -986,38 +976,12 @@
                         : maybe_request_delegate.get();
 
   const bool result =
-      IsUserVerifyingPlatformAuthenticatorAvailableImpl(request_delegate_ptr);
+      request_delegate_ptr->IsUserVerifyingPlatformAuthenticatorAvailable();
 
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), result));
 }
 
-bool AuthenticatorCommon::IsUserVerifyingPlatformAuthenticatorAvailableImpl(
-    AuthenticatorRequestClientDelegate* request_delegate) {
-  if (request_delegate->ShouldDisablePlatformAuthenticators()) {
-    return false;
-  }
-
-#if defined(OS_MACOSX)
-  // Touch ID is disabled, regardless of hardware support, if the embedder
-  // doesn't support it.
-  if (!GetContentClient()
-           ->browser()
-           ->IsWebAuthenticationTouchIdAuthenticatorSupported())
-    return false;
-
-  auto opt_config = request_delegate->GetTouchIdAuthenticatorConfig();
-  return opt_config &&
-         device::fido::mac::TouchIdAuthenticator::IsAvailable(*opt_config);
-#elif defined(OS_WIN)
-  return base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi) &&
-         device::WinWebAuthnApiAuthenticator::
-             IsUserVerifyingPlatformAuthenticatorAvailable();
-#else
-  return false;
-#endif
-}
-
 void AuthenticatorCommon::Cancel() {
   CancelWithStatus(blink::mojom::AuthenticatorStatus::ABORT_ERROR);
 }
@@ -1502,58 +1466,4 @@
       ->GetBrowserContext();
 }
 
-#if defined(OS_MACOSX)
-namespace {
-std::unique_ptr<device::fido::mac::TouchIdAuthenticator>
-CreateTouchIdAuthenticatorIfAvailable(
-    AuthenticatorRequestClientDelegate* request_delegate) {
-  // Not all embedders may provide an authenticator config.
-  auto opt_authenticator_config =
-      request_delegate->GetTouchIdAuthenticatorConfig();
-  if (!opt_authenticator_config) {
-    return nullptr;
-  }
-  return device::fido::mac::TouchIdAuthenticator::CreateIfAvailable(
-      std::move(*opt_authenticator_config));
-}
-}  // namespace
-#endif
-
-base::Optional<device::PlatformAuthenticatorInfo>
-AuthenticatorCommon::CreatePlatformAuthenticatorIfAvailable() {
-  // Incognito mode disables platform authenticators, so check for availability
-  // first.
-  if (!IsUserVerifyingPlatformAuthenticatorAvailableImpl(
-          request_delegate_.get())) {
-    return base::nullopt;
-  }
-#if defined(OS_MACOSX)
-  return device::PlatformAuthenticatorInfo(
-      CreateTouchIdAuthenticatorIfAvailable(request_delegate_.get()), false);
-#else
-  return base::nullopt;
-#endif
-}
-
-base::Optional<device::PlatformAuthenticatorInfo> AuthenticatorCommon::
-    CreatePlatformAuthenticatorIfAvailableAndCheckIfCredentialExists(
-        const device::CtapGetAssertionRequest& request) {
-  // Incognito mode disables platform authenticators, so check for availability
-  // first.
-  if (!IsUserVerifyingPlatformAuthenticatorAvailableImpl(
-          request_delegate_.get())) {
-    return base::nullopt;
-  }
-#if defined(OS_MACOSX)
-  std::unique_ptr<device::fido::mac::TouchIdAuthenticator> authenticator =
-      CreateTouchIdAuthenticatorIfAvailable(request_delegate_.get());
-  const bool has_credential =
-      authenticator->HasCredentialForGetAssertionRequest(request);
-  return device::PlatformAuthenticatorInfo(std::move(authenticator),
-                                           has_credential);
-#else
-  return base::nullopt;
-#endif
-}
-
 }  // namespace content
diff --git a/content/browser/webauth/authenticator_common.h b/content/browser/webauth/authenticator_common.h
index 61d63111..c3e48ad 100644
--- a/content/browser/webauth/authenticator_common.h
+++ b/content/browser/webauth/authenticator_common.h
@@ -35,7 +35,6 @@
 
 namespace device {
 
-struct PlatformAuthenticatorInfo;
 class FidoRequestHandlerBase;
 
 enum class FidoReturnCode : uint8_t;
@@ -87,11 +86,6 @@
           IsUserVerifyingPlatformAuthenticatorAvailableCallback callback);
   void Cancel();
 
-  // Synchronous implementation of
-  // IsUserVerifyingPlatformAuthenticatorAvailable.
-  bool IsUserVerifyingPlatformAuthenticatorAvailableImpl(
-      AuthenticatorRequestClientDelegate* request_delegate);
-
   void Cleanup();
 
   base::flat_set<device::FidoTransportProtocol> enabled_transports_for_testing()
@@ -189,12 +183,6 @@
       blink::mojom::AuthenticatorStatus status,
       blink::mojom::GetAssertionAuthenticatorResponsePtr response = nullptr);
 
-  base::Optional<device::PlatformAuthenticatorInfo>
-  CreatePlatformAuthenticatorIfAvailable();
-  base::Optional<device::PlatformAuthenticatorInfo>
-  CreatePlatformAuthenticatorIfAvailableAndCheckIfCredentialExists(
-      const device::CtapGetAssertionRequest& request);
-
   BrowserContext* browser_context() const;
 
   RenderFrameHost* const render_frame_host_;
diff --git a/content/browser/webauth/authenticator_environment_impl.cc b/content/browser/webauth/authenticator_environment_impl.cc
index fb702c2..7bbf7a9 100644
--- a/content/browser/webauth/authenticator_environment_impl.cc
+++ b/content/browser/webauth/authenticator_environment_impl.cc
@@ -29,24 +29,19 @@
 AuthenticatorEnvironmentImpl::AuthenticatorEnvironmentImpl() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableWebAuthTestingAPI)) {
-    discovery_factory_ = std::make_unique<VirtualFidoDiscoveryFactory>();
-  } else {
-    discovery_factory_ = std::make_unique<device::FidoDiscoveryFactory>();
+    replaced_discovery_factory_ =
+        std::make_unique<VirtualFidoDiscoveryFactory>();
   }
 }
 
 AuthenticatorEnvironmentImpl::~AuthenticatorEnvironmentImpl() = default;
 
-device::FidoDiscoveryFactory* AuthenticatorEnvironmentImpl::GetFactory(
-    FrameTreeNode* node) {
+device::FidoDiscoveryFactory*
+AuthenticatorEnvironmentImpl::GetDiscoveryFactoryOverride(FrameTreeNode* node) {
   auto* factory = GetVirtualFactoryFor(node);
   if (factory)
     return factory;
-  return discovery_factory_.get();
-}
-
-device::FidoDiscoveryFactory* AuthenticatorEnvironmentImpl::GetFactory() {
-  return discovery_factory_.get();
+  return replaced_discovery_factory_.get();
 }
 
 void AuthenticatorEnvironmentImpl::EnableVirtualAuthenticatorFor(
@@ -98,7 +93,7 @@
 
 void AuthenticatorEnvironmentImpl::ReplaceDefaultDiscoveryFactoryForTesting(
     std::unique_ptr<device::FidoDiscoveryFactory> factory) {
-  discovery_factory_ = std::move(factory);
+  replaced_discovery_factory_ = std::move(factory);
 }
 
 void AuthenticatorEnvironmentImpl::OnFrameTreeNodeDestroyed(
diff --git a/content/browser/webauth/authenticator_environment_impl.h b/content/browser/webauth/authenticator_environment_impl.h
index f386ee0..b468beb 100644
--- a/content/browser/webauth/authenticator_environment_impl.h
+++ b/content/browser/webauth/authenticator_environment_impl.h
@@ -35,11 +35,9 @@
  public:
   static AuthenticatorEnvironmentImpl* GetInstance();
 
-  // Returns a FidoDiscoveryFactory for the given node.
-  device::FidoDiscoveryFactory* GetFactory(FrameTreeNode* node);
-
-  // Returns the default FidoDiscoveryFactory.
-  device::FidoDiscoveryFactory* GetFactory();
+  // Returns the FidoDiscoveryFactory acting as replacement for the |node|.
+  device::FidoDiscoveryFactory* GetDiscoveryFactoryOverride(
+      FrameTreeNode* node);
 
   // Enables the scoped virtual authenticator environment for the |node| and its
   // descendants.
@@ -80,7 +78,7 @@
  private:
   friend class base::NoDestructor<AuthenticatorEnvironmentImpl>;
 
-  std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory_;
+  std::unique_ptr<device::FidoDiscoveryFactory> replaced_discovery_factory_;
 
   std::map<FrameTreeNode*, std::unique_ptr<VirtualFidoDiscoveryFactory>>
       virtual_discovery_factories_;
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index b9b6dcfee..55372f1 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -1599,12 +1599,14 @@
       base::OnceClosure action_callbacks_registered_callback,
       IndividualAttestation individual_attestation,
       AttestationConsent attestation_consent,
-      bool is_focused)
+      bool is_focused,
+      bool is_uvpaa)
       : action_callbacks_registered_callback_(
             std::move(action_callbacks_registered_callback)),
         individual_attestation_(individual_attestation),
         attestation_consent_(attestation_consent),
-        is_focused_(is_focused) {}
+        is_focused_(is_focused),
+        is_uvpaa_(is_uvpaa) {}
   ~TestAuthenticatorRequestDelegate() override {}
 
   void RegisterActionCallbacks(
@@ -1632,12 +1634,17 @@
                             AttestationConsent::GRANTED);
   }
 
+  bool IsUserVerifyingPlatformAuthenticatorAvailable() override {
+    return is_uvpaa_;
+  }
+
   bool IsFocused() override { return is_focused_; }
 
   base::OnceClosure action_callbacks_registered_callback_;
   const IndividualAttestation individual_attestation_;
   const AttestationConsent attestation_consent_;
   const bool is_focused_;
+  const bool is_uvpaa_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestAuthenticatorRequestDelegate);
@@ -1656,7 +1663,7 @@
         action_callbacks_registered_callback
             ? std::move(action_callbacks_registered_callback)
             : base::DoNothing(),
-        individual_attestation, attestation_consent, is_focused);
+        individual_attestation, attestation_consent, is_focused, is_uvpaa);
   }
 
   // If set, this closure will be called when the subsequently constructed
@@ -1668,6 +1675,8 @@
   AttestationConsent attestation_consent = AttestationConsent::DENIED;
   bool is_focused = true;
 
+  bool is_uvpaa = false;
+
   // This emulates scenarios where a nullptr RequestClientDelegate is returned
   // because a request is already in progress.
   bool return_null_delegate = false;
@@ -2072,8 +2081,8 @@
        PlatformAuthenticatorAttestation) {
   virtual_device_factory_->SetSupportedProtocol(
       device::ProtocolVersion::kCtap2);
-  virtual_device_factory_->mutable_state()->transport =
-      device::FidoTransportProtocol::kInternal;
+  virtual_device_factory_->SetTransport(
+      device::FidoTransportProtocol::kInternal);
   virtual_device_factory_->mutable_state()->self_attestation = true;
   virtual_device_factory_->mutable_state()
       ->non_zero_aaguid_with_self_attestation = true;
@@ -2305,43 +2314,20 @@
   }
 }
 
-#if defined(OS_WIN)
-TEST_F(AuthenticatorContentBrowserClientTest, WinIsUVPAA) {
-  for (const bool enable_win_webauthn_api : {false, true}) {
-    SCOPED_TRACE(enable_win_webauthn_api ? "enable_win_webauthn_api"
-                                         : "!enable_win_webauthn_api");
-    for (const bool is_uvpaa : {false, true}) {
-      SCOPED_TRACE(is_uvpaa ? "is_uvpaa" : "!is_uvpaa");
+TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAA) {
+  for (const bool is_uvpaa : {false, true}) {
+    SCOPED_TRACE(::testing::Message() << "is_uvpaa=" << is_uvpaa);
+    test_client_.is_uvpaa = is_uvpaa;
 
-      win_webauthn_api_.set_available(enable_win_webauthn_api);
-      win_webauthn_api_.set_is_uvpaa(is_uvpaa);
+    mojo::Remote<blink::mojom::Authenticator> authenticator =
+        ConnectToAuthenticator();
 
-      mojo::Remote<blink::mojom::Authenticator> authenticator =
-          ConnectToAuthenticator();
-      TestIsUvpaaCallback cb;
-      authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(
-          cb.callback());
-      cb.WaitForCallback();
-      EXPECT_EQ(enable_win_webauthn_api && is_uvpaa, cb.value());
-    }
+    TestIsUvpaaCallback cb;
+    authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(cb.callback());
+    cb.WaitForCallback();
+    EXPECT_EQ(is_uvpaa, cb.value());
   }
 }
-#endif  // defined(OS_WIN)
-
-#if !defined(OS_MACOSX) && !defined(OS_WIN)
-TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAAFalse) {
-  // There are no platform authenticators other than Windows Hello and macOS
-  // Touch ID.
-  NavigateAndCommit(GURL(kTestOrigin1));
-  mojo::Remote<blink::mojom::Authenticator> authenticator =
-      ConnectToAuthenticator();
-
-  TestIsUvpaaCallback cb;
-  authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(cb.callback());
-  cb.WaitForCallback();
-  EXPECT_FALSE(cb.value());
-}
-#endif  // !defined(OS_MACOSX) && !defined(OS_WIN)
 
 TEST_F(AuthenticatorContentBrowserClientTest,
        CryptotokenBypassesAttestationConsentPrompt) {
@@ -2386,7 +2372,8 @@
             base::DoNothing() /* did_start_request_callback */,
             IndividualAttestation::NOT_REQUESTED,
             AttestationConsent::DENIED,
-            true /* is_focused */),
+            true /* is_focused */,
+            /*is_uvpaa=*/false),
         failure_reasons_callback_(std::move(failure_reasons_callback)) {}
   ~MockAuthenticatorRequestDelegateObserver() override = default;
 
@@ -4172,98 +4159,4 @@
   }
 }
 
-#if defined(OS_MACOSX)
-class TouchIdAuthenticatorRequestDelegate
-    : public AuthenticatorRequestClientDelegate {
- public:
-  using TouchIdAuthenticatorConfig = ::device::fido::mac::AuthenticatorConfig;
-
-  explicit TouchIdAuthenticatorRequestDelegate(
-      TouchIdAuthenticatorConfig config)
-      : config_(std::move(config)) {}
-
-  base::Optional<TouchIdAuthenticatorConfig> GetTouchIdAuthenticatorConfig()
-      override {
-    return config_;
-  }
-
- private:
-  TouchIdAuthenticatorConfig config_;
-  DISALLOW_COPY_AND_ASSIGN(TouchIdAuthenticatorRequestDelegate);
-};
-
-class TouchIdAuthenticatorContentBrowserClient : public ContentBrowserClient {
- public:
-  using TouchIdAuthenticatorConfig = ::device::fido::mac::AuthenticatorConfig;
-
-  std::unique_ptr<AuthenticatorRequestClientDelegate>
-  GetWebAuthenticationRequestDelegate(
-      RenderFrameHost* render_frame_host,
-      const std::string& relying_party_id) override {
-    return std::make_unique<TouchIdAuthenticatorRequestDelegate>(
-        touch_id_config);
-  }
-
-  bool IsWebAuthenticationTouchIdAuthenticatorSupported() override {
-    return supports_touch_id;
-  }
-
-  bool supports_touch_id = true;
-
-  TouchIdAuthenticatorConfig touch_id_config;
-};
-
-class TouchIdAuthenticatorContentBrowserClientTest
-    : public AuthenticatorContentBrowserClientTest {
- protected:
-  TouchIdAuthenticatorContentBrowserClientTest() = default;
-
-  void SetUp() override {
-    AuthenticatorImplTest::SetUp();
-    old_client_ = SetBrowserClientForTesting(&test_client_);
-    NavigateAndCommit(GURL(kTestOrigin1));
-  }
-
-  void TearDown() override {
-    SetBrowserClientForTesting(old_client_);
-    AuthenticatorImplTest::TearDown();
-  }
-
-  TouchIdAuthenticatorContentBrowserClient test_client_;
-
-  API_AVAILABLE(macos(10.12.2))
-  device::fido::mac::ScopedTouchIdTestEnvironment touch_id_test_environment_;
-
- private:
-  ContentBrowserClient* old_client_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchIdAuthenticatorContentBrowserClientTest);
-};
-
-TEST_F(TouchIdAuthenticatorContentBrowserClientTest, IsUVPAA) {
-  if (__builtin_available(macOS 10.12.2, *)) {
-    for (const bool touch_id_enabled_in_browser_client : {false, true}) {
-      SCOPED_TRACE(::testing::Message() << "touch_id_enabled_in_browser_client="
-                                        << touch_id_enabled_in_browser_client);
-      for (const bool touch_id_available : {false, true}) {
-        SCOPED_TRACE(::testing::Message()
-                     << "touch_id_available=" << touch_id_available);
-        touch_id_test_environment_.SetTouchIdAvailable(touch_id_available);
-        test_client_.supports_touch_id = touch_id_enabled_in_browser_client;
-
-        mojo::Remote<blink::mojom::Authenticator> authenticator =
-            ConnectToAuthenticator();
-
-        TestIsUvpaaCallback cb;
-        authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(
-            cb.callback());
-        cb.WaitForCallback();
-        EXPECT_EQ(cb.value(),
-                  touch_id_enabled_in_browser_client && touch_id_available);
-      }
-    }
-  }
-}
-#endif  // defined(OS_MACOSX)
-
 }  // namespace content
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index d7c2ecf..6ec09b89 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -933,6 +933,7 @@
 // signal's aborted flag set after sending request, we get an AbortError.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        CreatePublicKeyCredentialWithAbortSetAfterCreate) {
+  InjectVirtualFidoDeviceFactory();
   CreateParameters parameters;
   parameters.signal = "authAbortSignal";
   std::string result;
@@ -1027,6 +1028,7 @@
 // signal's aborted flag set after sending request, we get an AbortError.
 IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
                        GetPublicKeyCredentialWithAbortSetAfterGet) {
+  InjectVirtualFidoDeviceFactory();
   GetParameters parameters;
   parameters.signal = "authAbortSignal";
   std::string result;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 7ef1dfd..6de307c 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -83,6 +83,7 @@
     DCHECK(client);
     client_ = std::move(client);
 
+    // Get a storage partition.
     auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
     if (!worker_process_host) {
       client_->OnScriptLoadStartFailed();
@@ -91,6 +92,25 @@
     auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
         worker_process_host->GetStoragePartition());
 
+    // Get a storage domain.
+    RenderFrameHostImpl* ancestor_render_frame_host =
+        GetAncestorRenderFrameHost();
+    if (!ancestor_render_frame_host) {
+      client_->OnScriptLoadStartFailed();
+      return;
+    }
+    SiteInstance* site_instance = ancestor_render_frame_host->GetSiteInstance();
+    if (!site_instance) {
+      client_->OnScriptLoadStartFailed();
+      return;
+    }
+    std::string storage_domain;
+    std::string partition_name;
+    bool in_memory;
+    GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+        storage_partition_impl->browser_context(), site_instance->GetSiteURL(),
+        /*can_be_default=*/true, &storage_domain, &partition_name, &in_memory);
+
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
     if (script_url.SchemeIsBlob()) {
       if (!blob_url_token) {
@@ -119,6 +139,7 @@
         storage_partition_impl->GetServiceWorkerContext(),
         service_worker_handle_.get(), appcache_handle_->core(),
         std::move(blob_url_loader_factory), nullptr, storage_partition_impl,
+        storage_domain,
         base::BindOnce(&DedicatedWorkerHost::DidStartScriptLoad,
                        weak_factory_.GetWeakPtr()));
   }
@@ -168,6 +189,9 @@
       return;
     }
 
+    // TODO(https://crbug.com/986188): Check if the main script's final response
+    // URL is commitable.
+
     // TODO(cammie): Change this approach when we support shared workers
     // creating dedicated workers, as there might be no ancestor frame.
     RenderFrameHostImpl* ancestor_render_frame_host =
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index d473c56..54f9a69 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -169,10 +169,23 @@
     DestroyHost(host);
   }
 
+  // Get a storage domain.
+  SiteInstance* site_instance = render_frame_host->GetSiteInstance();
+  if (!site_instance) {
+    client->OnScriptLoadFailed();
+    return;
+  }
+  std::string storage_domain;
+  std::string partition_name;
+  bool in_memory;
+  GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+      storage_partition_->browser_context(), site_instance->GetSiteURL(),
+      /*can_be_default=*/true, &storage_domain, &partition_name, &in_memory);
+
   CreateWorker(std::move(instance),
                std::move(outside_fetch_client_settings_object),
-               std::move(client), client_process_id, frame_id, message_port,
-               std::move(blob_url_loader_factory));
+               std::move(client), client_process_id, frame_id, storage_domain,
+               message_port, std::move(blob_url_loader_factory));
 }
 
 void SharedWorkerServiceImpl::DestroyHost(SharedWorkerHost* host) {
@@ -191,6 +204,7 @@
     blink::mojom::SharedWorkerClientPtr client,
     int client_process_id,
     int frame_id,
+    const std::string& storage_domain,
     const blink::MessagePortChannel& message_port,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -232,7 +246,7 @@
       ResourceType::kSharedWorker, service_worker_context_,
       service_worker_handle_raw, appcache_handle_core,
       std::move(blob_url_loader_factory), url_loader_factory_override_,
-      storage_partition_,
+      storage_partition_, storage_domain,
       base::BindOnce(&SharedWorkerServiceImpl::DidCreateScriptLoader,
                      weak_factory_.GetWeakPtr(), std::move(instance), weak_host,
                      std::move(client), client_process_id, frame_id,
@@ -262,6 +276,9 @@
     return;
   }
 
+  // TODO(https://crbug.com/986188): Check if the main script's final response
+  // URL is commitable.
+
   StartWorker(std::move(instance), std::move(host), std::move(client),
               process_id, frame_id, message_port,
               std::move(subresource_loader_factories),
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index b9a650f..553f1de 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -87,6 +87,7 @@
       blink::mojom::SharedWorkerClientPtr client,
       int client_process_id,
       int frame_id,
+      const std::string& storage_domain,
       const blink::MessagePortChannel& message_port,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
   void DidCreateScriptLoader(
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index 3ca89a1..f5602a49c0 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -18,6 +18,7 @@
 #include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/data_url_loader_factory.h"
 #include "content/browser/file_url_loader_factory.h"
+#include "content/browser/fileapi/file_system_url_loader_factory.h"
 #include "content/browser/loader/browser_initiated_resource_request.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/navigation_subresource_loader_params.h"
@@ -86,6 +87,7 @@
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_override,
     StoragePartitionImpl* storage_partition,
+    const std::string& storage_domain,
     CompletionCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
@@ -105,16 +107,22 @@
   bool constructor_uses_file_url =
       request_initiator.scheme() == url::kFileScheme;
 
+  // TODO(https://crbug.com/987517): Filesystem URL support on shared workers
+  // are now broken.
+  bool filesystem_url_support = resource_type == ResourceType::kWorker;
+
   // Set up the factory bundle for non-NetworkService URLs, e.g.,
   // chrome-extension:// URLs. One factory bundle is consumed by the browser
   // for WorkerScriptLoaderFactory, and one is sent to the renderer for
   // subresource loading.
   std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
       factory_bundle_for_browser = CreateFactoryBundle(
-          worker_process_id, storage_partition, constructor_uses_file_url);
+          worker_process_id, storage_partition, storage_domain,
+          constructor_uses_file_url, filesystem_url_support);
   std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
       subresource_loader_factories = CreateFactoryBundle(
-          worker_process_id, storage_partition, constructor_uses_file_url);
+          worker_process_id, storage_partition, storage_domain,
+          constructor_uses_file_url, filesystem_url_support);
 
   // Create a resource request for initiating worker script fetch from the
   // browser process.
@@ -226,12 +234,23 @@
 WorkerScriptFetchInitiator::CreateFactoryBundle(
     int worker_process_id,
     StoragePartitionImpl* storage_partition,
-    bool file_support) {
+    const std::string& storage_domain,
+    bool file_support,
+    bool filesystem_url_support) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
   non_network_factories[url::kDataScheme] =
       std::make_unique<DataURLLoaderFactory>();
+  if (filesystem_url_support) {
+    // TODO(https://crbug.com/986188): Pass ChildProcessHost::kInvalidUniqueID
+    // instead of valid |worker_process_id| for |factory_bundle_for_browser|
+    // once CanCommitURL-like check is implemented in PlzWorker.
+    non_network_factories[url::kFileSystemScheme] =
+        CreateFileSystemURLLoaderFactory(
+            worker_process_id, RenderFrameHost::kNoFrameTreeNodeId,
+            storage_partition->GetFileSystemContext(), storage_domain);
+  }
   GetContentClient()
       ->browser()
       ->RegisterNonNetworkSubresourceURLLoaderFactories(
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
index c2a1382..5142cb3 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -75,6 +75,7 @@
       scoped_refptr<network::SharedURLLoaderFactory>
           url_loader_factory_override,
       StoragePartitionImpl* storage_partition,
+      const std::string& storage_domain,
       CompletionCallback callback);
 
   // Returns the BrowserThread::ID that the WorkerScriptLoaderFactory will be
@@ -88,7 +89,9 @@
   static std::unique_ptr<blink::URLLoaderFactoryBundleInfo> CreateFactoryBundle(
       int worker_process_id,
       StoragePartitionImpl* storage_partition,
-      bool file_support);
+      const std::string& storage_domain,
+      bool file_support,
+      bool filesystem_url_support);
 
   // Adds additional request headers to |resource_request|. Must be called on
   // the UI thread.
diff --git a/content/child/child_process.cc b/content/child/child_process.cc
index 3d4cd9e..7765881 100644
--- a/content/child/child_process.cc
+++ b/content/child/child_process.cc
@@ -16,6 +16,7 @@
 #include "base/threading/thread_local.h"
 #include "build/build_config.h"
 #include "content/child/child_thread_impl.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace content {
 
@@ -57,7 +58,10 @@
 #if defined(OS_ANDROID)
   // TODO(reveman): Remove this in favor of setting it explicitly for each type
   // of process.
-  thread_options.priority = base::ThreadPriority::DISPLAY;
+  if (base::FeatureList::IsEnabled(
+          blink::features::kBlinkCompositorUseDisplayThreadPriority)) {
+    thread_options.priority = base::ThreadPriority::DISPLAY;
+  }
 #endif
   CHECK(io_thread_.StartWithOptions(thread_options));
 }
diff --git a/content/common/DEPS b/content/common/DEPS
index 1d1d07c6..f0157d6b 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -5,6 +5,7 @@
   "+components/viz/common",
   "+components/payments",
   "+device/base/synchronization",
+  "+services/media_session/public/cpp",
   "+services/network/loader_util.h",
   "+services/network/public/cpp",
   "+services/network/public/mojom",
diff --git a/content/common/content_param_traits.cc b/content/common/content_param_traits.cc
index 09dc04c..f6a0272 100644
--- a/content/common/content_param_traits.cc
+++ b/content/common/content_param_traits.cc
@@ -294,6 +294,7 @@
   WriteParam(m, p->data.has_user_gesture);
   WriteParam(m, !!p->data.user_activation);
   WriteParam(m, p->data.transfer_user_activation);
+  WriteParam(m, p->data.allow_autoplay);
   if (p->data.user_activation) {
     WriteParam(m, p->data.user_activation->has_been_active);
     WriteParam(m, p->data.user_activation->was_active);
@@ -325,7 +326,8 @@
       !ReadParam(m, iter, &(*r)->data.stream_channels) ||
       !ReadParam(m, iter, &(*r)->data.has_user_gesture) ||
       !ReadParam(m, iter, &has_activation) ||
-      !ReadParam(m, iter, &(*r)->data.transfer_user_activation)) {
+      !ReadParam(m, iter, &(*r)->data.transfer_user_activation) ||
+      !ReadParam(m, iter, &(*r)->data.allow_autoplay)) {
     return false;
   }
 
diff --git a/content/common/content_switches_internal.cc b/content/common/content_switches_internal.cc
index df9ba8b..4a8facef 100644
--- a/content/common/content_switches_internal.cc
+++ b/content/common/content_switches_internal.cc
@@ -88,9 +88,9 @@
 #if defined(OS_WIN)
 #if defined(GOOGLE_CHROME_BUILD)
   std::string title = "Google Chrome";
-#else   // CHROMIUM_BUILD
+#else   // BUILDFLAG(CHROMIUM_BRANDING)
   std::string title = "Chromium";
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
   title += " ";
   title += label;  // makes attaching to process easier
   std::string message = label;
diff --git a/content/common/media/media_player_delegate_messages.h b/content/common/media/media_player_delegate_messages.h
index e9219e9..1f9f5cc 100644
--- a/content/common/media/media_player_delegate_messages.h
+++ b/content/common/media/media_player_delegate_messages.h
@@ -16,6 +16,7 @@
 #include "content/common/content_export.h"
 #include "ipc/ipc_message_macros.h"
 #include "media/base/media_content_type.h"
+#include "services/media_session/public/cpp/media_position.h"
 #include "third_party/blink/public/platform/web_fullscreen_video_status.h"
 #include "ui/gfx/ipc/geometry/gfx_param_traits.h"
 
@@ -23,6 +24,13 @@
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 #define IPC_MESSAGE_START MediaPlayerDelegateMsgStart
 
+IPC_STRUCT_TRAITS_BEGIN(media_session::MediaPosition)
+  IPC_STRUCT_TRAITS_MEMBER(playback_rate_)
+  IPC_STRUCT_TRAITS_MEMBER(duration_)
+  IPC_STRUCT_TRAITS_MEMBER(position_)
+  IPC_STRUCT_TRAITS_MEMBER(last_updated_time_)
+IPC_STRUCT_TRAITS_END()
+
 IPC_ENUM_TRAITS_MAX_VALUE(media::MediaContentType, media::MediaContentType::Max)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::WebFullscreenVideoStatus,
                           blink::WebFullscreenVideoStatus::kMax)
@@ -82,6 +90,10 @@
                     int /* delegate_id, distinguishes instances */,
                     bool /* the new muted status */)
 
+IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnMediaPositionStateChanged,
+                    int /* delegate_id, distinguishes instances */,
+                    media_session::MediaPosition /* the new position state */)
+
 IPC_MESSAGE_ROUTED2(
     MediaPlayerDelegateHostMsg_OnMediaEffectivelyFullscreenChanged,
     int /* delegate_id, distinguishes instances */,
diff --git a/content/common/service_manager/service_manager_connection_impl.cc b/content/common/service_manager/service_manager_connection_impl.cc
index d8af976..647d57e 100644
--- a/content/common/service_manager/service_manager_connection_impl.cc
+++ b/content/common/service_manager/service_manager_connection_impl.cc
@@ -77,6 +77,11 @@
         FROM_HERE, base::BindOnce(&IOThreadContext::StartOnIOThread, this));
   }
 
+  void Stop() {
+    io_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&IOThreadContext::StopOnIOThread, this));
+  }
+
   // Safe to call from whichever thread called Start() (or may have called
   // Start()). Must be called before IO thread shutdown.
   void ShutDown() {
@@ -189,6 +194,11 @@
         new MessageLoopObserver(weak_factory_.GetWeakPtr());
   }
 
+  void StopOnIOThread() {
+    ClearConnectionFiltersOnIOThread();
+    request_handlers_.clear();
+  }
+
   void ShutDownOnIOThread() {
     DCHECK(io_thread_checker_.CalledOnValidThread());
 
@@ -209,9 +219,7 @@
 
     service_binding_.reset();
 
-    ClearConnectionFiltersOnIOThread();
-
-    request_handlers_.clear();
+    StopOnIOThread();
   }
 
   void ClearConnectionFiltersOnIOThread() {
@@ -399,6 +407,10 @@
                  weak_factory_.GetWeakPtr()));
 }
 
+void ServiceManagerConnectionImpl::Stop() {
+  context_->Stop();
+}
+
 service_manager::Connector* ServiceManagerConnectionImpl::GetConnector() {
   return connector_.get();
 }
diff --git a/content/common/service_manager/service_manager_connection_impl.h b/content/common/service_manager/service_manager_connection_impl.h
index 6f572250..37344d1 100644
--- a/content/common/service_manager/service_manager_connection_impl.h
+++ b/content/common/service_manager/service_manager_connection_impl.h
@@ -36,6 +36,7 @@
 
   // ServiceManagerConnection:
   void Start() override;
+  void Stop() override;
   service_manager::Connector* GetConnector() override;
   void SetConnectionLostClosure(const base::Closure& closure) override;
   int AddConnectionFilter(std::unique_ptr<ConnectionFilter> filter) override;
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index 819996d5..b61d2ab 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -8,6 +8,8 @@
 
 #include "base/callback.h"
 #include "base/strings/string_piece.h"
+#include "build/build_config.h"
+#include "device/fido/fido_discovery_factory.h"
 
 namespace content {
 
@@ -60,16 +62,34 @@
   return true;
 }
 
-bool AuthenticatorRequestClientDelegate::ShouldDisablePlatformAuthenticators() {
-  return false;
-}
-
 #if defined(OS_MACOSX)
 base::Optional<AuthenticatorRequestClientDelegate::TouchIdAuthenticatorConfig>
 AuthenticatorRequestClientDelegate::GetTouchIdAuthenticatorConfig() {
   return base::nullopt;
 }
+#endif  // defined(OS_MACOSX)
+
+bool AuthenticatorRequestClientDelegate::
+    IsUserVerifyingPlatformAuthenticatorAvailable() {
+  return false;
+}
+
+device::FidoDiscoveryFactory*
+AuthenticatorRequestClientDelegate::GetDiscoveryFactory() {
+#if defined(OS_ANDROID)
+  // Android uses an internal FIDO API to manage device discovery.
+  NOTREACHED();
+  return nullptr;
+#else
+  if (!discovery_factory_) {
+    discovery_factory_ = std::make_unique<device::FidoDiscoveryFactory>();
+#if defined(OS_MACOSX)
+    discovery_factory_->set_mac_touch_id_info(GetTouchIdAuthenticatorConfig());
+#endif  // defined(OS_MACOSX)
+  }
+  return discovery_factory_.get();
 #endif
+}
 
 void AuthenticatorRequestClientDelegate::UpdateLastTransportUsed(
     device::FidoTransportProtocol transport) {}
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index a23b507a3..eda57e6 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -133,10 +133,6 @@
   // that testing is possible.
   virtual bool IsFocused();
 
-  // Returns whether IsUVPAA() should always return false, regardless of
-  // hardware support or enrollment status.
-  virtual bool ShouldDisablePlatformAuthenticators();
-
 #if defined(OS_MACOSX)
   using TouchIdAuthenticatorConfig = device::fido::mac::AuthenticatorConfig;
 
@@ -145,7 +141,15 @@
   // available.
   virtual base::Optional<TouchIdAuthenticatorConfig>
   GetTouchIdAuthenticatorConfig();
-#endif
+#endif  // defined(OS_MACOSX)
+
+  // Returns true if a user verifying platform authenticator is available and
+  // configured.
+  virtual bool IsUserVerifyingPlatformAuthenticatorAvailable();
+
+  // Returns a FidoDiscoveryFactory that has been configured for the current
+  // environment.
+  virtual device::FidoDiscoveryFactory* GetDiscoveryFactory();
 
   // Saves transport type the user used during WebAuthN API so that the
   // WebAuthN UI will default to the same transport type during next API call.
@@ -188,6 +192,10 @@
   void FinishCollectPIN() override;
 
  private:
+#if !defined(OS_ANDROID)
+  std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory_;
+#endif  // !defined(OS_ANDROID)
+
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestClientDelegate);
 };
 
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 21da9c9c..e2b0dcd 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -868,12 +868,6 @@
   return std::make_unique<AuthenticatorRequestClientDelegate>();
 }
 
-#if defined(OS_MACOSX)
-bool ContentBrowserClient::IsWebAuthenticationTouchIdAuthenticatorSupported() {
-  return false;
-}
-#endif
-
 std::unique_ptr<net::ClientCertStore>
 ContentBrowserClient::CreateClientCertStore(ResourceContext* resource_context) {
   return nullptr;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index b0709c0..a14b357 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1435,13 +1435,6 @@
   GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host,
                                       const std::string& relying_party_id);
 
-#if defined(OS_MACOSX)
-  // Returns whether WebAuthn supports the built-in Touch ID platform
-  // authenticator. If true, the embedder must supply a configuration in
-  // |AuthenticatorRequestClientDelegate::GetTouchIdAuthenticatorConfig|.
-  virtual bool IsWebAuthenticationTouchIdAuthenticatorSupported();
-#endif
-
   // Get platform ClientCertStore. May return nullptr.
   virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       ResourceContext* resource_context);
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 3c4af592..b9833a6 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -82,6 +82,11 @@
 const base::Feature kCacheInlineScriptCode{"CacheInlineScriptCode",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables support for parallel cache_storage operations via the
+// "max_shared_ops" fieldtrial parameter.
+const base::Feature kCacheStorageParallelOps{"CacheStorageParallelOps",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If Canvas2D Image Chromium is allowed, this feature controls whether it is
 // enabled.
 const base::Feature kCanvas2DImageChromium {
@@ -270,7 +275,7 @@
 
 // If enabled, the URLLoaderRequestController lives on the UI thread.
 const base::Feature kNavigationLoaderOnUI{"NavigationLoaderOnUI",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Transmit the response body datapipe to the renderer process in
 // CommitNavigation() so that it can start reading earlier.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 99f2a31..3aa3ab6 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -29,6 +29,7 @@
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
 CONTENT_EXPORT extern const base::Feature kBundledHTTPExchanges;
 CONTENT_EXPORT extern const base::Feature kCacheInlineScriptCode;
+CONTENT_EXPORT extern const base::Feature kCacheStorageParallelOps;
 CONTENT_EXPORT extern const base::Feature kCanvas2DImageChromium;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
diff --git a/content/public/common/navigation_policy.cc b/content/public/common/navigation_policy.cc
index f437f0a..b1713d7f 100644
--- a/content/public/common/navigation_policy.cc
+++ b/content/public/common/navigation_policy.cc
@@ -69,13 +69,15 @@
 ResourceInterceptPolicy NavigationDownloadPolicy::GetResourceInterceptPolicy()
     const {
   if (disallowed_types.test(
+          static_cast<size_t>(NavigationDownloadType::kSandbox)) ||
+      disallowed_types.test(
           static_cast<size_t>(NavigationDownloadType::kSandboxNoGesture)) ||
       disallowed_types.test(
           static_cast<size_t>(NavigationDownloadType::kOpenerCrossOrigin)) ||
       disallowed_types.test(
-          static_cast<size_t>(NavigationDownloadType::kAdFrameNoGesture)) ||
+          static_cast<size_t>(NavigationDownloadType::kAdFrame)) ||
       disallowed_types.test(
-          static_cast<size_t>(NavigationDownloadType::kAdFrameGesture))) {
+          static_cast<size_t>(NavigationDownloadType::kAdFrameNoGesture))) {
     return ResourceInterceptPolicy::kAllowPluginOnly;
   }
   return disallowed_types.any() ? ResourceInterceptPolicy::kAllowNone
diff --git a/content/public/common/navigation_policy.h b/content/public/common/navigation_policy.h
index e7e3954..cad33697 100644
--- a/content/public/common/navigation_policy.h
+++ b/content/public/common/navigation_policy.h
@@ -45,11 +45,17 @@
   // activation.
   kAdFrameNoGesture = 8,
 
-  // The navigation was initiated from or occurred in an ad frame with user
-  // activation.
-  kAdFrameGesture = 9,
+  // The navigation was initiated from or occurred in an ad frame.
+  kAdFrame = 10,
 
-  kMaxValue = kAdFrameGesture
+  // The navigation was initiated from or occurred in an iframe with
+  // |WebSandboxFlags::kDownloads| flag set.
+  kSandbox = 11,
+
+  // The navigation was initiated without user activation.
+  kNoGesture = 12,
+
+  kMaxValue = kNoGesture
 };
 
 // Stores the navigation types that may be of interest to the download-related
diff --git a/content/public/common/service_manager_connection.h b/content/public/common/service_manager_connection.h
index 5fb927d..15a17230 100644
--- a/content/public/common/service_manager_connection.h
+++ b/content/public/common/service_manager_connection.h
@@ -75,6 +75,10 @@
   // below.
   virtual void Start() = 0;
 
+  // Stops accepting incoming connections. This happens asynchronously by
+  // posting to the IO thread, and cannot be undone.
+  virtual void Stop() = 0;
+
   // Returns the service_manager::Connector received via this connection's
   // Service
   // implementation. Use this to initiate connections as this object's Identity.
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index e659a2c..564b4b3 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -52,8 +52,8 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
   ServiceWorkerSubresourceLoaderFactory::Create(
       base::MakeRefCounted<ControllerServiceWorkerConnector>(
-          std::move(container_host_info), nullptr /* controller_ptr */,
-          client_id),
+          std::move(container_host_info),
+          mojo::NullRemote() /* remote_controller */, client_id),
       network::SharedURLLoaderFactory::Create(std::move(fallback_factory)),
       std::move(request), std::move(task_runner));
 }
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index 93a45c2..2e1635ee 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -111,6 +111,13 @@
                                                            delegate_id, muted));
 }
 
+void RendererWebMediaPlayerDelegate::DidPlayerMediaPositionStateChange(
+    int delegate_id,
+    const media_session::MediaPosition& position) {
+  Send(new MediaPlayerDelegateHostMsg_OnMediaPositionStateChanged(
+      routing_id(), delegate_id, position));
+}
+
 void RendererWebMediaPlayerDelegate::DidPause(int player_id) {
   DVLOG(2) << __func__ << "(" << player_id << ")";
   DCHECK(id_map_.Lookup(player_id));
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h
index 3deccaa7..8c4f71b2 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.h
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -67,7 +67,7 @@
   void DidPlayerMutedStatusChange(int delegate_id, bool muted) override;
   void DidPlayerMediaPositionStateChange(
       int delegate_id,
-      const media_session::MediaPosition& position) override {}
+      const media_session::MediaPosition& position) override;
 
   // content::RenderFrameObserver overrides.
   void WasHidden() override;
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 5425f4a..e0c2d35 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -1527,6 +1527,12 @@
   return webkit_request.RequestSucceeded();
 }
 
+void RTCPeerConnectionHandler::RestartIce() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  // The proxy invokes RestartIce() on the signaling thread.
+  native_peer_connection_->RestartIce();
+}
+
 void RTCPeerConnectionHandler::GetStandardStatsForTracker(
     scoped_refptr<webrtc::RTCStatsCollectorCallback> observer) {
   native_peer_connection_->GetStats(observer.get());
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index 5110fc0..e8861b12 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -151,6 +151,7 @@
       scoped_refptr<blink::WebRTCICECandidate> candidate) override;
   virtual void OnaddICECandidateResult(const blink::WebRTCVoidRequest& request,
                                        bool result);
+  void RestartIce() override;
 
   void GetStats(const blink::WebRTCStatsRequest& request) override;
   void GetStats(blink::WebRTCStatsReportCallback callback,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 6578966..e2a9d4a 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1752,21 +1752,33 @@
     bool blocking_downloads_in_sandbox_without_user_activation_enabled,
     bool from_ad,
     NavigationDownloadPolicy* download_policy) {
+  bool has_gesture = request.HasUserGesture();
+  if (!has_gesture) {
+    download_policy->SetAllowed(NavigationDownloadType::kNoGesture);
+  }
+
   // Disallow downloads on an opener if the requestor is cross origin.
   // See crbug.com/632514.
   if (is_opener_navigation &&
       !request.RequestorOrigin().CanAccess(current_origin)) {
     download_policy->SetDisallowed(NavigationDownloadType::kOpenerCrossOrigin);
   }
-  if (has_download_sandbox_flag && !request.HasUserGesture()) {
-    if (blocking_downloads_in_sandbox_without_user_activation_enabled) {
-      download_policy->SetDisallowed(NavigationDownloadType::kSandboxNoGesture);
-    } else {
-      download_policy->SetAllowed(NavigationDownloadType::kSandboxNoGesture);
+
+  if (has_download_sandbox_flag) {
+    download_policy->SetAllowed(NavigationDownloadType::kSandbox);
+    if (!has_gesture) {
+      if (blocking_downloads_in_sandbox_without_user_activation_enabled) {
+        download_policy->SetDisallowed(
+            NavigationDownloadType::kSandboxNoGesture);
+      } else {
+        download_policy->SetAllowed(NavigationDownloadType::kSandboxNoGesture);
+      }
     }
   }
+
   if (from_ad) {
-    if (!request.HasUserGesture()) {
+    download_policy->SetAllowed(NavigationDownloadType::kAdFrame);
+    if (!has_gesture) {
       if (base::FeatureList::IsEnabled(
               blink::features::
                   kBlockingDownloadsInAdFrameWithoutUserActivation)) {
@@ -1775,8 +1787,6 @@
       } else {
         download_policy->SetAllowed(NavigationDownloadType::kAdFrameNoGesture);
       }
-    } else {
-      download_policy->SetAllowed(NavigationDownloadType::kAdFrameGesture);
     }
   }
 }
diff --git a/content/renderer/render_process.cc b/content/renderer/render_process.cc
index 0fdf095..9992d1f2b 100644
--- a/content/renderer/render_process.cc
+++ b/content/renderer/render_process.cc
@@ -4,15 +4,28 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
 #include "content/renderer/render_process.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace content {
 
+namespace {
+
+base::ThreadPriority GetRenderIOThreadPriority() {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kBlinkCompositorUseDisplayThreadPriority))
+    return base::ThreadPriority::DISPLAY;
+  return base::ThreadPriority::NORMAL;
+}
+
+}  // namespace
+
 RenderProcess::RenderProcess(
     const std::string& thread_pool_name,
     std::unique_ptr<base::ThreadPoolInstance::InitParams>
         thread_pool_init_params)
-    : ChildProcess(base::ThreadPriority::NORMAL,
+    : ChildProcess(GetRenderIOThreadPriority(),
                    thread_pool_name,
                    std::move(thread_pool_init_params)) {}
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index f2d4f932..76de46a 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -137,6 +137,7 @@
 #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/cpp/gpu/gpu.h"
 #include "skia/ext/skia_memory_dump_provider.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_image_generator.h"
@@ -927,8 +928,11 @@
       discardable_shared_memory_manager_.get());
 
 #if defined(OS_LINUX)
-  render_message_filter()->SetThreadPriority(
-      ChildProcess::current()->io_thread_id(), base::ThreadPriority::DISPLAY);
+  if (base::FeatureList::IsEnabled(
+          blink::features::kBlinkCompositorUseDisplayThreadPriority)) {
+    render_message_filter()->SetThreadPriority(
+        ChildProcess::current()->io_thread_id(), base::ThreadPriority::DISPLAY);
+  }
 #endif
 
   process_foregrounded_count_ = 0;
diff --git a/content/renderer/service_worker/controller_service_worker_connector.cc b/content/renderer/service_worker/controller_service_worker_connector.cc
index 51a2ee1..6ade300 100644
--- a/content/renderer/service_worker/controller_service_worker_connector.cc
+++ b/content/renderer/service_worker/controller_service_worker_connector.cc
@@ -12,14 +12,15 @@
 
 ControllerServiceWorkerConnector::ControllerServiceWorkerConnector(
     blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_info,
-    blink::mojom::ControllerServiceWorkerPtr controller_ptr,
+    mojo::PendingRemote<blink::mojom::ControllerServiceWorker>
+        remote_controller,
     const std::string& client_id)
     : client_id_(client_id) {
   container_host_ptr_.Bind(std::move(container_host_info));
   container_host_ptr_.set_connection_error_handler(base::BindOnce(
       &ControllerServiceWorkerConnector::OnContainerHostConnectionClosed,
       base::Unretained(this)));
-  SetControllerServiceWorkerPtr(std::move(controller_ptr));
+  SetControllerServiceWorker(std::move(remote_controller));
 }
 
 blink::mojom::ControllerServiceWorker*
@@ -29,10 +30,13 @@
     case State::kDisconnected: {
       DCHECK(!controller_service_worker_);
       DCHECK(container_host_ptr_);
-      blink::mojom::ControllerServiceWorkerPtr controller_ptr;
+      mojo::PendingRemote<blink::mojom::ControllerServiceWorker>
+          remote_controller;
+
       container_host_ptr_->EnsureControllerServiceWorker(
-          mojo::MakeRequest(&controller_ptr), purpose);
-      SetControllerServiceWorkerPtr(std::move(controller_ptr));
+          remote_controller.InitWithNewPipeAndPassReceiver(), purpose);
+
+      SetControllerServiceWorker(std::move(remote_controller));
       return controller_service_worker_.get();
     }
     case State::kConnected:
@@ -78,19 +82,22 @@
 }
 
 void ControllerServiceWorkerConnector::UpdateController(
-    blink::mojom::ControllerServiceWorkerPtr controller_ptr) {
+    mojo::PendingRemote<blink::mojom::ControllerServiceWorker> controller) {
   if (state_ == State::kNoContainerHost)
     return;
-  SetControllerServiceWorkerPtr(std::move(controller_ptr));
+  SetControllerServiceWorker(std::move(controller));
   if (!controller_service_worker_)
     state_ = State::kNoController;
 }
 
-void ControllerServiceWorkerConnector::SetControllerServiceWorkerPtr(
-    blink::mojom::ControllerServiceWorkerPtr controller_ptr) {
-  controller_service_worker_ = std::move(controller_ptr);
+void ControllerServiceWorkerConnector::SetControllerServiceWorker(
+    mojo::PendingRemote<blink::mojom::ControllerServiceWorker> controller) {
+  controller_service_worker_.reset();
+  if (!controller)
+    return;
+  controller_service_worker_.Bind(std::move(controller));
   if (controller_service_worker_) {
-    controller_service_worker_.set_connection_error_handler(base::BindOnce(
+    controller_service_worker_.set_disconnect_handler(base::BindOnce(
         &ControllerServiceWorkerConnector::OnControllerConnectionClosed,
         base::Unretained(this)));
     state_ = State::kConnected;
diff --git a/content/renderer/service_worker/controller_service_worker_connector.h b/content/renderer/service_worker/controller_service_worker_connector.h
index 5267fa1..14c1c1db 100644
--- a/content/renderer/service_worker/controller_service_worker_connector.h
+++ b/content/renderer/service_worker/controller_service_worker_connector.h
@@ -12,6 +12,8 @@
 #include "base/observer_list.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
 
@@ -56,7 +58,7 @@
   };
 
   // This class should only be created if a controller exists for the client.
-  // |controller_ptr| may be nullptr if the caller does not yet have a Mojo
+  // |remote_controller| may be nullptr if the caller does not yet have a Mojo
   // connection to the controller. |state_| is set to kDisconnected in that
   // case.
   // Creates and holds the ownership of |container_host_ptr_| (as |this|
@@ -64,7 +66,8 @@
   // original |container_host|).
   ControllerServiceWorkerConnector(
       blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_info,
-      blink::mojom::ControllerServiceWorkerPtr controller_ptr,
+      mojo::PendingRemote<blink::mojom::ControllerServiceWorker>
+          remote_controller,
       const std::string& client_id);
 
   // This may return nullptr if the connection to the ContainerHost (in the
@@ -83,15 +86,16 @@
 
   // blink::mojom::ControllerServiceWorkerConnector:
   void UpdateController(
-      blink::mojom::ControllerServiceWorkerPtr controller_ptr) override;
+      mojo::PendingRemote<blink::mojom::ControllerServiceWorker> controller)
+      override;
 
   State state() const { return state_; }
 
   const std::string& client_id() const { return client_id_; }
 
  private:
-  void SetControllerServiceWorkerPtr(
-      blink::mojom::ControllerServiceWorkerPtr controller_ptr);
+  void SetControllerServiceWorker(
+      mojo::PendingRemote<blink::mojom::ControllerServiceWorker> controller);
 
   State state_ = State::kDisconnected;
 
@@ -105,7 +109,8 @@
 
   // Connection to the controller service worker, which lives in a renderer
   // process that's not necessarily the same as this connector.
-  blink::mojom::ControllerServiceWorkerPtr controller_service_worker_;
+  mojo::Remote<blink::mojom::ControllerServiceWorker>
+      controller_service_worker_;
 
   base::ObserverList<Observer>::Unchecked observer_list_;
 
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 69c312e4..4aa6d26 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -66,7 +66,7 @@
       !params->installed_scripts_info.is_null(),
       std::move(params->renderer_preferences),
       std::move(params->service_worker_request),
-      std::move(params->controller_request), std::move(params->instance_host),
+      std::move(params->controller_receiver), std::move(params->instance_host),
       std::move(params->provider_info), this, std::move(start_timing),
       std::move(params->preference_watcher_request),
       std::move(params->subresource_loader_factories),
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 3818e4c..faae45f 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -100,7 +100,8 @@
     bool is_starting_installed_worker,
     blink::mojom::RendererPreferencesPtr renderer_preferences,
     blink::mojom::ServiceWorkerRequest service_worker_request,
-    blink::mojom::ControllerServiceWorkerRequest controller_request,
+    mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+        controller_receiver,
     blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
     blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
     EmbeddedWorkerInstanceClientImpl* owner,
@@ -119,7 +120,7 @@
       main_thread_task_runner_(std::move(main_thread_task_runner)),
       proxy_(nullptr),
       pending_service_worker_request_(std::move(service_worker_request)),
-      pending_controller_request_(std::move(controller_request)),
+      controller_receiver_(std::move(controller_receiver)),
       pending_subresource_loader_updater_(
           std::move(subresource_loader_updater)),
       owner_(owner),
@@ -253,9 +254,8 @@
   DCHECK(pending_service_worker_request_.is_pending());
   proxy_->BindServiceWorker(pending_service_worker_request_.PassMessagePipe());
 
-  DCHECK(pending_controller_request_.is_pending());
-  proxy_->BindControllerServiceWorker(
-      pending_controller_request_.PassMessagePipe());
+  DCHECK(controller_receiver_.is_valid());
+  proxy_->BindControllerServiceWorker(controller_receiver_.PassPipe());
 }
 
 void ServiceWorkerContextClient::WillEvaluateScript() {
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 5055b7e..2ac29fd 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -21,6 +21,7 @@
 #include "content/common/content_export.h"
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom.h"
@@ -83,7 +84,8 @@
       bool is_starting_installed_worker,
       blink::mojom::RendererPreferencesPtr renderer_preferences,
       blink::mojom::ServiceWorkerRequest service_worker_request,
-      blink::mojom::ControllerServiceWorkerRequest controller_request,
+      mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+          controller_receiver,
       blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       EmbeddedWorkerInstanceClientImpl* owner,
@@ -208,7 +210,8 @@
 
   // These Mojo objects are bound on the worker thread.
   blink::mojom::ServiceWorkerRequest pending_service_worker_request_;
-  blink::mojom::ControllerServiceWorkerRequest pending_controller_request_;
+  mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+      controller_receiver_;
   mojo::PendingReceiver<blink::mojom::ServiceWorkerSubresourceLoaderUpdater>
       pending_subresource_loader_updater_;
 
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 3eaf172..9bf7b557 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -20,6 +20,7 @@
 #include "content/renderer/service_worker/service_worker_subresource_loader.h"
 #include "content/renderer/service_worker/web_service_worker_provider_impl.h"
 #include "content/renderer/worker/worker_thread_registry.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
@@ -31,16 +32,15 @@
 
 void CreateSubresourceLoaderFactoryForProviderContext(
     blink::mojom::ServiceWorkerContainerHostPtrInfo container_host_info,
-    blink::mojom::ControllerServiceWorkerPtrInfo controller_ptr_info,
+    mojo::PendingRemote<blink::mojom::ControllerServiceWorker>
+        remote_controller,
     const std::string& client_id,
     std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info,
     blink::mojom::ControllerServiceWorkerConnectorRequest connector_request,
     network::mojom::URLLoaderFactoryRequest request,
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr;
-  controller_ptr.Bind(std::move(controller_ptr_info));
   auto connector = base::MakeRefCounted<ControllerServiceWorkerConnector>(
-      std::move(container_host_info), std::move(controller_ptr), client_id);
+      std::move(container_host_info), std::move(remote_controller), client_id);
   connector->AddBinding(std::move(connector_request));
   ServiceWorkerSubresourceLoaderFactory::Create(
       std::move(connector),
@@ -95,7 +95,7 @@
 
 network::mojom::URLLoaderFactory*
 ServiceWorkerProviderContext::GetSubresourceLoaderFactoryInternal() {
-  if (!controller_endpoint_ && !controller_connector_) {
+  if (!remote_controller_ && !controller_connector_) {
     // No controller is attached.
     return nullptr;
   }
@@ -108,7 +108,7 @@
 
   if (!subresource_loader_factory_) {
     DCHECK(!controller_connector_);
-    DCHECK(controller_endpoint_);
+    DCHECK(remote_controller_);
 
     blink::mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info =
         CloneContainerHostPtrInfo();
@@ -121,12 +121,12 @@
         {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
     task_runner->PostTask(
         FROM_HERE,
-        base::BindOnce(
-            &CreateSubresourceLoaderFactoryForProviderContext,
-            std::move(host_ptr_info), std::move(controller_endpoint_),
-            client_id_, fallback_loader_factory_->Clone(),
-            mojo::MakeRequest(&controller_connector_),
-            mojo::MakeRequest(&subresource_loader_factory_), task_runner));
+        base::BindOnce(&CreateSubresourceLoaderFactoryForProviderContext,
+                       std::move(host_ptr_info), std::move(remote_controller_),
+                       client_id_, fallback_loader_factory_->Clone(),
+                       mojo::MakeRequest(&controller_connector_),
+                       mojo::MakeRequest(&subresource_loader_factory_),
+                       task_runner));
 
     DCHECK(!weak_wrapped_subresource_loader_factory_);
     weak_wrapped_subresource_loader_factory_ =
@@ -285,7 +285,7 @@
               blink::mojom::ControllerServiceWorkerMode::kNoController &&
           controller_));
   controller_mode_ = controller_info->mode;
-  controller_endpoint_ = std::move(controller_info->endpoint);
+  remote_controller_ = std::move(controller_info->remote_controller);
 
   // Propagate the controller to workers related to this provider.
   if (controller_) {
@@ -319,9 +319,7 @@
       // the existing controller or may use the new controller settings
       // depending on when the request is actually passed to the factory (this
       // part is inherently racy).
-      controller_connector_->UpdateController(
-          blink::mojom::ControllerServiceWorkerPtr(
-              std::move(controller_endpoint_)));
+      controller_connector_->UpdateController(std::move(remote_controller_));
     }
   }
 
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index e21aefc..a337bf0 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -17,6 +17,7 @@
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.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_container.mojom.h"
@@ -287,7 +288,7 @@
   // Used in |subresource_loader_factory_| to get the connection to the
   // controller service worker.
   //
-  // |controller_endpoint_| is a Mojo pipe to the controller service worker,
+  // |remote_controller_| is a Mojo pipe to the controller service worker,
   // and is to be passed to (i.e. taken by) a subresource loader factory when
   // GetSubresourceLoaderFactory() is called for the first time when a valid
   // controller exists.
@@ -297,7 +298,7 @@
   // subresource loader factory and lives on a background thread. This is
   // populated when GetSubresourceLoader() creates the subresource loader
   // factory and takes |controller_endpoint_|.
-  blink::mojom::ControllerServiceWorkerPtrInfo controller_endpoint_;
+  mojo::PendingRemote<blink::mojom::ControllerServiceWorker> remote_controller_;
   blink::mojom::ControllerServiceWorkerConnectorPtr controller_connector_;
 
   bool sent_execution_ready_ = false;
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index b6b0a1b..ca58610 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -23,6 +23,8 @@
 #include "content/renderer/service_worker/controller_service_worker_connector.h"
 #include "content/renderer/service_worker/web_service_worker_provider_impl.h"
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -193,8 +195,9 @@
     if (fetch_event_callback_)
       std::move(fetch_event_callback_).Run();
   }
-  void Clone(blink::mojom::ControllerServiceWorkerRequest request) override {
-    bindings_.AddBinding(this, std::move(request));
+  void Clone(mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+                 receiver) override {
+    receivers_.Add(this, std::move(receiver));
   }
 
   void set_fetch_callback(base::OnceClosure closure) {
@@ -205,13 +208,13 @@
     return *fetch_event_request_;
   }
 
-  void Disconnect() { bindings_.CloseAllBindings(); }
+  void Disconnect() { receivers_.Clear(); }
 
  private:
   int fetch_event_count_ = 0;
   blink::mojom::FetchAPIRequestPtr fetch_event_request_;
   base::OnceClosure fetch_event_callback_;
-  mojo::BindingSet<blink::mojom::ControllerServiceWorker> bindings_;
+  mojo::ReceiverSet<blink::mojom::ControllerServiceWorker> receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeControllerServiceWorker);
 };
@@ -242,7 +245,7 @@
     NOTIMPLEMENTED();
   }
   void EnsureControllerServiceWorker(
-      blink::mojom::ControllerServiceWorkerRequest request,
+      mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
       blink::mojom::ControllerServiceWorkerPurpose purpose) override {
     NOTIMPLEMENTED();
   }
@@ -436,12 +439,12 @@
   // Make the ControllerServiceWorkerInfo.
   FakeControllerServiceWorker fake_controller1;
   auto controller_info1 = blink::mojom::ControllerServiceWorkerInfo::New();
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr1;
-  fake_controller1.Clone(mojo::MakeRequest(&controller_ptr1));
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller1;
+  fake_controller1.Clone(remote_controller1.BindNewPipeAndPassReceiver());
   controller_info1->mode =
       blink::mojom::ControllerServiceWorkerMode::kControlled;
   controller_info1->object_info = std::move(object_info1);
-  controller_info1->endpoint = controller_ptr1.PassInterface();
+  controller_info1->remote_controller = remote_controller1.Unbind();
 
   // Make the ServiceWorkerProviderContext, pasing it the controller, container,
   // and container host.
@@ -479,12 +482,12 @@
   EXPECT_EQ(1, object_host2->GetBindingCount());
   FakeControllerServiceWorker fake_controller2;
   auto controller_info2 = blink::mojom::ControllerServiceWorkerInfo::New();
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr2;
-  fake_controller2.Clone(mojo::MakeRequest(&controller_ptr2));
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller2;
+  fake_controller2.Clone(remote_controller2.BindNewPipeAndPassReceiver());
   controller_info2->mode =
       blink::mojom::ControllerServiceWorkerMode::kControlled;
   controller_info2->object_info = std::move(object_info2);
-  controller_info2->endpoint = controller_ptr2.PassInterface();
+  controller_info2->remote_controller = remote_controller2.Unbind();
 
   // Resetting the controller will trigger many things happening, including the
   // object binding being broken.
@@ -568,12 +571,12 @@
   EXPECT_EQ(1, object_host4->GetBindingCount());
   FakeControllerServiceWorker fake_controller4;
   auto controller_info4 = blink::mojom::ControllerServiceWorkerInfo::New();
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr4;
-  fake_controller4.Clone(mojo::MakeRequest(&controller_ptr4));
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller4;
+  fake_controller4.Clone(remote_controller4.BindNewPipeAndPassReceiver());
   controller_info4->mode =
       blink::mojom::ControllerServiceWorkerMode::kControlled;
   controller_info4->object_info = std::move(object_info4);
-  controller_info4->endpoint = controller_ptr4.PassInterface();
+  controller_info4->remote_controller = remote_controller4.Unbind();
   container_ptr->SetController(std::move(controller_info4), true);
   container_ptr.FlushForTesting();
 
@@ -718,12 +721,12 @@
   // Make the ControllerServiceWorkerInfo.
   FakeControllerServiceWorker fake_controller;
   auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New();
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr;
-  fake_controller.Clone(mojo::MakeRequest(&controller_ptr));
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller;
+  fake_controller.Clone(remote_controller.BindNewPipeAndPassReceiver());
   controller_info->mode =
       blink::mojom::ControllerServiceWorkerMode::kControlled;
   controller_info->object_info = std::move(object_info);
-  controller_info->endpoint = controller_ptr.PassInterface();
+  controller_info->remote_controller = remote_controller.Unbind();
 
   // Make the container host and container pointers.
   blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
@@ -765,12 +768,12 @@
   // Make the ControllerServiceWorkerInfo.
   FakeControllerServiceWorker fake_controller;
   auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New();
-  blink::mojom::ControllerServiceWorkerPtr controller_ptr;
-  fake_controller.Clone(mojo::MakeRequest(&controller_ptr));
+  mojo::Remote<blink::mojom::ControllerServiceWorker> remote_controller;
+  fake_controller.Clone(remote_controller.BindNewPipeAndPassReceiver());
   controller_info->mode =
       blink::mojom::ControllerServiceWorkerMode::kControlled;
   controller_info->object_info = std::move(object_info);
-  controller_info->endpoint = controller_ptr.PassInterface();
+  controller_info->remote_controller = remote_controller.Unbind();
 
   // Make the container host and container pointers.
   blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 6aa478d..e518bd80 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -21,6 +21,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/renderer/service_worker/controller_service_worker_connector.h"
 #include "content/test/fake_network_url_loader_factory.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/http/http_util.h"
@@ -118,7 +119,7 @@
     return response;
   }
 
-  void CloseAllBindings() { bindings_.CloseAllBindings(); }
+  void ClearReceivers() { receivers_.Clear(); }
 
   // Tells this controller to abort the fetch event without a response.
   // i.e., simulate the service worker failing to handle the fetch event.
@@ -301,8 +302,9 @@
       std::move(fetch_event_callback_).Run();
   }
 
-  void Clone(blink::mojom::ControllerServiceWorkerRequest request) override {
-    bindings_.AddBinding(this, std::move(request));
+  void Clone(mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
+                 receiver) override {
+    receivers_.Add(this, std::move(receiver));
   }
 
   void RunUntilFetchEvent() {
@@ -334,7 +336,7 @@
   int fetch_event_count_ = 0;
   blink::mojom::FetchAPIRequestPtr fetch_event_request_;
   base::OnceClosure fetch_event_callback_;
-  mojo::BindingSet<blink::mojom::ControllerServiceWorker> bindings_;
+  mojo::ReceiverSet<blink::mojom::ControllerServiceWorker> receivers_;
 
   // For ResponseMode::kStream.
   blink::mojom::ServiceWorkerStreamHandlePtr stream_handle_;
@@ -389,12 +391,12 @@
     NOTIMPLEMENTED();
   }
   void EnsureControllerServiceWorker(
-      blink::mojom::ControllerServiceWorkerRequest request,
+      mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
       blink::mojom::ControllerServiceWorkerPurpose purpose) override {
     get_controller_service_worker_count_++;
     if (!fake_controller_)
       return;
-    fake_controller_->Clone(std::move(request));
+    fake_controller_->Clone(std::move(receiver));
   }
   void CloneContainerHost(
       blink::mojom::ServiceWorkerContainerHostRequest request) override {
@@ -455,7 +457,7 @@
       fake_container_host_.CloneContainerHost(
           mojo::MakeRequest(&host_ptr_info));
       connector_ = base::MakeRefCounted<ControllerServiceWorkerConnector>(
-          std::move(host_ptr_info), nullptr /*controller_ptr*/,
+          std::move(host_ptr_info), mojo::NullRemote() /*remote_controller*/,
           "" /*client_id*/);
     }
     network::mojom::URLLoaderFactoryPtr service_worker_url_loader_factory;
@@ -685,7 +687,7 @@
   }
 
   // Drop the connection to the ControllerServiceWorker.
-  fake_controller_.CloseAllBindings();
+  fake_controller_.ClearReceivers();
   base::RunLoop().RunUntilIdle();
 
   {
@@ -722,7 +724,7 @@
   }
 
   // Make the connector have no controller.
-  connector_->UpdateController(nullptr);
+  connector_->UpdateController(mojo::NullRemote());
   base::RunLoop().RunUntilIdle();
 
   base::HistogramTester histogram_tester;
@@ -796,7 +798,7 @@
   StartRequest(factory, request, &loader, &client);
 
   // Drop the connection to the ControllerServiceWorker.
-  fake_controller_.CloseAllBindings();
+  fake_controller_.ClearReceivers();
   base::RunLoop().RunUntilIdle();
 
   // If connection is closed during fetch event, it's restarted and successfully
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index b0c56968..f68c93a 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -189,6 +189,8 @@
     "browser/web_test/web_test_browser_main_parts.h",
     "browser/web_test/web_test_content_browser_client.cc",
     "browser/web_test/web_test_content_browser_client.h",
+    "browser/web_test/web_test_content_index_provider.cc",
+    "browser/web_test/web_test_content_index_provider.h",
     "browser/web_test/web_test_devtools_bindings.cc",
     "browser/web_test/web_test_devtools_bindings.h",
     "browser/web_test/web_test_download_manager_delegate.cc",
diff --git a/content/shell/browser/web_test/web_test_browser_context.cc b/content/shell/browser/web_test/web_test_browser_context.cc
index 0635ec4d..05ad83f 100644
--- a/content/shell/browser/web_test/web_test_browser_context.cc
+++ b/content/shell/browser/web_test/web_test_browser_context.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/resource_context.h"
 #include "content/shell/browser/shell_url_request_context_getter.h"
 #include "content/shell/browser/web_test/web_test_background_fetch_delegate.h"
+#include "content/shell/browser/web_test/web_test_content_index_provider.h"
 #include "content/shell/browser/web_test/web_test_download_manager_delegate.h"
 #include "content/shell/browser/web_test/web_test_permission_manager.h"
 #include "content/shell/browser/web_test/web_test_push_messaging_service.h"
@@ -70,9 +71,8 @@
 }
 
 PushMessagingService* WebTestBrowserContext::GetPushMessagingService() {
-  if (!push_messaging_service_) {
+  if (!push_messaging_service_)
     push_messaging_service_ = std::make_unique<WebTestPushMessagingService>();
-  }
   return push_messaging_service_.get();
 }
 
@@ -104,4 +104,10 @@
       GetPermissionControllerDelegate());
 }
 
+ContentIndexProvider* WebTestBrowserContext::GetContentIndexProvider() {
+  if (!content_index_provider_)
+    content_index_provider_ = std::make_unique<WebTestContentIndexProvider>();
+  return content_index_provider_.get();
+}
+
 }  // namespace content
diff --git a/content/shell/browser/web_test/web_test_browser_context.h b/content/shell/browser/web_test/web_test_browser_context.h
index 22e140d8..45bf4e0 100644
--- a/content/shell/browser/web_test/web_test_browser_context.h
+++ b/content/shell/browser/web_test/web_test_browser_context.h
@@ -11,21 +11,22 @@
 
 namespace device {
 class ScopedGeolocationOverrider;
-}
+}  // namespace device
 
 namespace content {
 
 class BackgroundSyncController;
+class ContentIndexProvider;
 class DownloadManagerDelegate;
+class PermissionControllerDelegate;
+class PushMessagingService;
 class WebTestBackgroundFetchDelegate;
 class WebTestPermissionManager;
 class WebTestPushMessagingService;
-class PermissionControllerDelegate;
-class PushMessagingService;
 
 class WebTestBrowserContext final : public ShellBrowserContext {
  public:
-  WebTestBrowserContext(bool off_the_record);
+  explicit WebTestBrowserContext(bool off_the_record);
   ~WebTestBrowserContext() override;
 
   // BrowserContext implementation.
@@ -34,6 +35,7 @@
   PermissionControllerDelegate* GetPermissionControllerDelegate() override;
   BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   BackgroundSyncController* GetBackgroundSyncController() override;
+  ContentIndexProvider* GetContentIndexProvider() override;
 
   WebTestPermissionManager* GetWebTestPermissionManager();
 
@@ -48,6 +50,7 @@
   std::unique_ptr<WebTestBackgroundFetchDelegate> background_fetch_delegate_;
   std::unique_ptr<BackgroundSyncController> background_sync_controller_;
   std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
+  std::unique_ptr<ContentIndexProvider> content_index_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(WebTestBrowserContext);
 };
diff --git a/content/shell/browser/web_test/web_test_content_index_provider.cc b/content/shell/browser/web_test/web_test_content_index_provider.cc
new file mode 100644
index 0000000..1da729d
--- /dev/null
+++ b/content/shell/browser/web_test/web_test_content_index_provider.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/shell/browser/web_test/web_test_content_index_provider.h"
+
+namespace content {
+
+WebTestContentIndexProvider::WebTestContentIndexProvider() = default;
+
+WebTestContentIndexProvider::~WebTestContentIndexProvider() = default;
+
+void WebTestContentIndexProvider::OnContentAdded(ContentIndexEntry entry) {
+  entries_[entry.description->id] = {
+      entry.service_worker_registration_id,
+      url::Origin::Create(entry.launch_url.GetOrigin())};
+}
+
+void WebTestContentIndexProvider::OnContentDeleted(
+    int64_t service_worker_registration_id,
+    const url::Origin& origin,
+    const std::string& description_id) {
+  entries_.erase(description_id);
+}
+
+std::pair<int64_t, url::Origin>
+WebTestContentIndexProvider::GetRegistrationDataFromId(const std::string& id) {
+  if (!entries_.count(id))
+    return {-1, url::Origin()};
+  return entries_[id];
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/shell/browser/web_test/web_test_content_index_provider.h b/content/shell/browser/web_test/web_test_content_index_provider.h
new file mode 100644
index 0000000..4755dc5
--- /dev/null
+++ b/content/shell/browser/web_test/web_test_content_index_provider.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_CONTENT_INDEX_PROVIDER_H_
+#define CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_CONTENT_INDEX_PROVIDER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/content_index_provider.h"
+#include "url/origin.h"
+
+namespace content {
+
+// When using WebTestContentIndexProvider, IDs need to be globally unique,
+// instead of per Service Worker.
+class WebTestContentIndexProvider : public ContentIndexProvider {
+ public:
+  WebTestContentIndexProvider();
+  ~WebTestContentIndexProvider() override;
+
+  // ContentIndexProvider implementation.
+  void OnContentAdded(ContentIndexEntry entry) override;
+  void OnContentDeleted(int64_t service_worker_registration_id,
+                        const url::Origin& origin,
+                        const std::string& description_id) override;
+
+  // Returns the Service Worker Registration ID and the origin of the Content
+  // Index entry registered with |id|. If |id| does not exist, invalid values
+  // are returned, which would cause DB tasks to fail.
+  std::pair<int64_t, url::Origin> GetRegistrationDataFromId(
+      const std::string& id);
+
+ private:
+  // Map from |description_id| to <|service_worker_registration_id|, |origin|>.
+  std::map<std::string, std::pair<int64_t, url::Origin>> entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebTestContentIndexProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_CONTENT_INDEX_PROVIDER_H_
diff --git a/content/shell/browser/web_test/web_test_message_filter.cc b/content/shell/browser/web_test/web_test_message_filter.cc
index b77391de..9fb4f8d 100644
--- a/content/shell/browser/web_test/web_test_message_filter.cc
+++ b/content/shell/browser/web_test/web_test_message_filter.cc
@@ -12,7 +12,9 @@
 #include "base/threading/thread_restrictions.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/content_index_context.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/web_test_support.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_content_browser_client.h"
@@ -20,6 +22,7 @@
 #include "content/shell/browser/web_test/blink_test_controller.h"
 #include "content/shell/browser/web_test/web_test_browser_context.h"
 #include "content/shell/browser/web_test/web_test_content_browser_client.h"
+#include "content/shell/browser/web_test/web_test_content_index_provider.h"
 #include "content/shell/browser/web_test/web_test_permission_manager.h"
 #include "content/shell/common/web_test/web_test_messages.h"
 #include "content/shell/test_runner/web_test_delegate.h"
@@ -29,10 +32,13 @@
 #include "storage/browser/database/database_tracker.h"
 #include "storage/browser/fileapi/isolated_context.h"
 #include "storage/browser/quota/quota_manager.h"
+#include "url/origin.h"
 
 namespace content {
 
-static MockPlatformNotificationService* GetMockPlatformNotificationService() {
+namespace {
+
+MockPlatformNotificationService* GetMockPlatformNotificationService() {
   auto* client = WebTestContentBrowserClient::Get();
   auto* context = client->GetWebTestBrowserContext();
   auto* service = client->GetPlatformNotificationService(context);
@@ -40,6 +46,23 @@
   return static_cast<MockPlatformNotificationService*>(service);
 }
 
+WebTestContentIndexProvider* GetWebTestContentIndexProvider() {
+  auto* client = WebTestContentBrowserClient::Get();
+  auto* context = client->GetWebTestBrowserContext();
+  return static_cast<WebTestContentIndexProvider*>(
+      context->GetContentIndexProvider());
+}
+
+ContentIndexContext* GetContentIndexContext(const url::Origin& origin) {
+  auto* client = WebTestContentBrowserClient::Get();
+  auto* context = client->GetWebTestBrowserContext();
+  auto* storage_partition = BrowserContext::GetStoragePartitionForSite(
+      context, origin.GetURL(), /* can_create= */ false);
+  return storage_partition->GetContentIndexContext();
+}
+
+}  // namespace
+
 WebTestMessageFilter::WebTestMessageFilter(
     int render_process_id,
     storage::DatabaseTracker* database_tracker,
@@ -67,6 +90,7 @@
       return database_tracker_->task_runner();
     case WebTestHostMsg_SimulateWebNotificationClick::ID:
     case WebTestHostMsg_SimulateWebNotificationClose::ID:
+    case WebTestHostMsg_SimulateWebContentIndexDelete::ID:
     case WebTestHostMsg_SetPermission::ID:
     case WebTestHostMsg_ResetPermissions::ID:
     case WebTestHostMsg_WebTestRuntimeFlagsChanged::ID:
@@ -91,6 +115,8 @@
                         OnSimulateWebNotificationClick)
     IPC_MESSAGE_HANDLER(WebTestHostMsg_SimulateWebNotificationClose,
                         OnSimulateWebNotificationClose)
+    IPC_MESSAGE_HANDLER(WebTestHostMsg_SimulateWebContentIndexDelete,
+                        OnSimulateWebContentIndexDelete)
     IPC_MESSAGE_HANDLER(WebTestHostMsg_DeleteAllCookies, OnDeleteAllCookies)
     IPC_MESSAGE_HANDLER(WebTestHostMsg_SetPermission, OnSetPermission)
     IPC_MESSAGE_HANDLER(WebTestHostMsg_ResetPermissions, OnResetPermissions)
@@ -165,6 +191,20 @@
   GetMockPlatformNotificationService()->SimulateClose(title, by_user);
 }
 
+void WebTestMessageFilter::OnSimulateWebContentIndexDelete(
+    const std::string& id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  auto* provider = GetWebTestContentIndexProvider();
+
+  std::pair<int64_t, url::Origin> registration_data =
+      provider->GetRegistrationDataFromId(id);
+
+  auto* context = GetContentIndexContext(registration_data.second);
+  context->OnUserDeletedItem(registration_data.first, registration_data.second,
+                             id);
+}
+
 void WebTestMessageFilter::OnDeleteAllCookies() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   cookie_manager_->DeleteCookies(network::mojom::CookieDeletionFilter::New(),
diff --git a/content/shell/browser/web_test/web_test_message_filter.h b/content/shell/browser/web_test/web_test_message_filter.h
index a1f7f1b..7ab9690 100644
--- a/content/shell/browser/web_test/web_test_message_filter.h
+++ b/content/shell/browser/web_test/web_test_message_filter.h
@@ -21,21 +21,18 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
 namespace network {
 namespace mojom {
 class NetworkContext;
-}
+}  // namespace mojom
 }  // namespace network
 
 namespace storage {
-class QuotaManager;
-}
-
-namespace storage {
 class DatabaseTracker;
-}
+class QuotaManager;
+}  // namespace storage
 
 namespace content {
 
@@ -71,6 +68,7 @@
       const base::Optional<int>& action_index,
       const base::Optional<base::string16>& reply);
   void OnSimulateWebNotificationClose(const std::string& title, bool by_user);
+  void OnSimulateWebContentIndexDelete(const std::string& id);
   void OnDeleteAllCookies();
   void OnSetPermission(const std::string& name,
                        blink::mojom::PermissionStatus status,
diff --git a/content/shell/common/web_test/web_test_messages.h b/content/shell/common/web_test/web_test_messages.h
index 2815a455..f71dfc5 100644
--- a/content/shell/common/web_test/web_test_messages.h
+++ b/content/shell/common/web_test/web_test_messages.h
@@ -34,6 +34,8 @@
 IPC_MESSAGE_ROUTED2(WebTestHostMsg_SimulateWebNotificationClose,
                     std::string /* title */,
                     bool /* by_user */)
+IPC_MESSAGE_ROUTED1(WebTestHostMsg_SimulateWebContentIndexDelete,
+                    std::string /* id */)
 IPC_MESSAGE_ROUTED1(WebTestHostMsg_BlockThirdPartyCookies, bool /* block */)
 IPC_MESSAGE_ROUTED0(WebTestHostMsg_DeleteAllCookies)
 IPC_MESSAGE_ROUTED4(WebTestHostMsg_SetPermission,
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 0cd46067..35825bf 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -312,6 +312,10 @@
                                                        by_user));
 }
 
+void BlinkTestRunner::SimulateWebContentIndexDelete(const std::string& id) {
+  Send(new WebTestHostMsg_SimulateWebContentIndexDelete(routing_id(), id));
+}
+
 void BlinkTestRunner::SetDeviceScaleFactor(float factor) {
   content::SetDeviceScaleFactor(render_view(), factor);
 }
diff --git a/content/shell/renderer/web_test/blink_test_runner.h b/content/shell/renderer/web_test/blink_test_runner.h
index ad5ee73..3ac87a6 100644
--- a/content/shell/renderer/web_test/blink_test_runner.h
+++ b/content/shell/renderer/web_test/blink_test_runner.h
@@ -27,7 +27,7 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
 namespace blink {
 class WebView;
@@ -35,7 +35,7 @@
 
 namespace test_runner {
 class AppBannerService;
-}
+}  // namespace test_runner
 
 namespace content {
 
@@ -86,6 +86,7 @@
       const base::Optional<base::string16>& reply) override;
   void SimulateWebNotificationClose(const std::string& title,
                                     bool by_user) override;
+  void SimulateWebContentIndexDelete(const std::string& id) override;
   void SetDeviceScaleFactor(float factor) override;
   void SetDeviceColorSpace(const std::string& name) override;
   float GetWindowToViewportScale() override;
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index d933c90..6f92838 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -273,6 +273,7 @@
   void InspectSecondaryWindow();
   void SimulateWebNotificationClick(gin::Arguments* args);
   void SimulateWebNotificationClose(const std::string& title, bool by_user);
+  void SimulateWebContentIndexDelete(const std::string& id);
   void UseUnfortunateSynchronousResizeMode();
   void WaitForPolicyDelegate();
   void WaitUntilDone();
@@ -605,6 +606,8 @@
                  &TestRunnerBindings::SimulateWebNotificationClick)
       .SetMethod("simulateWebNotificationClose",
                  &TestRunnerBindings::SimulateWebNotificationClose)
+      .SetMethod("simulateWebContentIndexDelete",
+                 &TestRunnerBindings::SimulateWebContentIndexDelete)
       .SetProperty("tooltipText", &TestRunnerBindings::TooltipText)
       .SetMethod("useUnfortunateSynchronousResizeMode",
                  &TestRunnerBindings::UseUnfortunateSynchronousResizeMode)
@@ -1298,6 +1301,12 @@
   runner_->SimulateWebNotificationClose(title, by_user);
 }
 
+void TestRunnerBindings::SimulateWebContentIndexDelete(const std::string& id) {
+  if (!runner_)
+    return;
+  runner_->SimulateWebContentIndexDelete(id);
+}
+
 void TestRunnerBindings::SetHighlightAds() {
   if (view_runner_)
     view_runner_->SetHighlightAds(true);
@@ -2560,6 +2569,10 @@
   delegate_->SimulateWebNotificationClose(title, by_user);
 }
 
+void TestRunner::SimulateWebContentIndexDelete(const std::string& id) {
+  delegate_->SimulateWebContentIndexDelete(id);
+}
+
 void TestRunner::SetAnimationRequiresRaster(bool do_raster) {
   animation_requires_raster_ = do_raster;
 }
diff --git a/content/shell/test_runner/test_runner.h b/content/shell/test_runner/test_runner.h
index cbb532f..cbdce21 100644
--- a/content/shell/test_runner/test_runner.h
+++ b/content/shell/test_runner/test_runner.h
@@ -506,6 +506,9 @@
   // Simulates closing a Web Notification.
   void SimulateWebNotificationClose(const std::string& title, bool by_user);
 
+  // Simulates a user deleting a content index entry.
+  void SimulateWebContentIndexDelete(const std::string& id);
+
   // Takes care of notifying the delegate after a change to web test runtime
   // flags.
   void OnWebTestRuntimeFlagsChanged();
diff --git a/content/shell/test_runner/web_test_delegate.h b/content/shell/test_runner/web_test_delegate.h
index 0c6397d1..ea49e65a 100644
--- a/content/shell/test_runner/web_test_delegate.h
+++ b/content/shell/test_runner/web_test_delegate.h
@@ -22,7 +22,7 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
 namespace blink {
 struct Manifest;
@@ -32,7 +32,7 @@
 struct WebPluginParams;
 struct WebSize;
 class WebView;
-}
+}  // namespace blink
 
 namespace test_runner {
 
@@ -121,6 +121,9 @@
   virtual void SimulateWebNotificationClose(const std::string& title,
                                             bool by_user) = 0;
 
+  // Controls Content Index entries.
+  virtual void SimulateWebContentIndexDelete(const std::string& id) = 0;
+
   // Controls the device scale factor of the main WebView for hidpi tests.
   virtual void SetDeviceScaleFactor(float factor) = 0;
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 05505a9..2f33a85e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -179,7 +179,6 @@
 crbug.com/976370 [ skia-renderer ] Pixel_CSS3DBlueBox [ Skip ]
 
 # Fails when the browser features SkiaRenderer & Vulkan are enabled on Linux
-crbug.com/984954 [ linux skia-renderer use-vulkan ] Pixel_CanvasDisplayLinearRGBAccelerated2D [ Skip ]
 crbug.com/984973 [ linux skia-renderer use-vulkan ] Pixel_CanvasDisplayLinearRGBUnaccelerated2D [ Skip ]
 crbug.com/984973 [ linux skia-renderer use-vulkan ] Pixel_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing [ Skip ]
 crbug.com/984973 [ linux skia-renderer use-vulkan ] Pixel_CanvasDisplaySRGBUnaccelerated2D [ Skip ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 6c114917..131e462f 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -262,6 +262,7 @@
 # Passthrough command decoder / OpenGL / Windows
 crbug.com/835364 [ opengl win passthrough ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
 crbug.com/835364 [ opengl win passthrough ] conformance/renderbuffers/depth-renderbuffer-initialization.html [ RetryOnFailure ]
+crbug.com/angleproject/3742 [ opengl win passthrough ] conformance2/rendering/clearbuffer-sub-source.html [ Failure ]
 
 # Passthrough command decoder / Linux / OpenGL / NVIDIA
 crbug.com/766918 [ opengl linux passthrough nvidia ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 8b48838..8a50fe4 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -276,6 +276,7 @@
 crbug.com/angleproject/2905 [ win vulkan passthrough ] WebglExtension_WEBGL_depth_texture [ Failure ]
 crbug.com/angleproject/2394 [ win vulkan passthrough ] WebglExtension_WEBGL_draw_buffers [ Failure ]
 crbug.com/angleproject/3111 [ win vulkan passthrough ] deqp/data/gles2/shaders/swizzles.html [ Skip ]
+crbug.com/angleproject/3741 [ win vulkan passthrough ] conformance/textures/misc/mipmap-fbo.html [ RetryOnFailure ]
 
 # Vulkan / Win / NVIDIA / Passthrough command decoder
 crbug.com/angleproject/2918 [ win nvidia vulkan passthrough ] conformance/canvas/to-data-url-test.html [ Failure ]
@@ -546,22 +547,6 @@
 # expectation at the top of this file to just not declare any OS.
 crbug.com/986808 [ android opengles ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
 
-# Related to SharedImages
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/misc/texture-upload-size.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/misc/texture-npot-video.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/video/tex-2d-alpha-alpha-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/987228 [ android opengles passthrough ] conformance/textures/misc/tex-video-using-tex-unit-non-zero.html [ Failure ]
-
 # Misc failures
 crbug.com/angleproject/2988 [ android opengles ] conformance/context/context-size-change.html [ Failure ]
 
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn
index e8c4026..f96b016 100644
--- a/device/fido/BUILD.gn
+++ b/device/fido/BUILD.gn
@@ -196,6 +196,8 @@
       "mac/credential_metadata.h",
       "mac/credential_store.h",
       "mac/credential_store.mm",
+      "mac/discovery.cc",
+      "mac/discovery.h",
       "mac/get_assertion_operation.h",
       "mac/get_assertion_operation.mm",
       "mac/keychain.h",
diff --git a/device/fido/ble_adapter_manager_unittest.cc b/device/fido/ble_adapter_manager_unittest.cc
index 30c78c3..a6b61424 100644
--- a/device/fido/ble_adapter_manager_unittest.cc
+++ b/device/fido/ble_adapter_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_discovery_factory.h"
 #include "device/fido/fido_request_handler_base.h"
+#include "device/fido/fido_transport_protocol.h"
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -70,7 +71,9 @@
  public:
   FakeFidoRequestHandlerBase(MockObserver* observer,
                              FidoDiscoveryFactory* fido_discovery_factory)
-      : FidoRequestHandlerBase(nullptr, fido_discovery_factory, {}) {
+      : FidoRequestHandlerBase(nullptr,
+                               fido_discovery_factory,
+                               {FidoTransportProtocol::kBluetoothLowEnergy}) {
     set_observer(observer);
   }
 
@@ -99,10 +102,6 @@
     BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
   }
 
-  std::unique_ptr<BleAdapterManager> CreateTestBleAdapterManager() {
-    return std::make_unique<BleAdapterManager>(fake_request_handler_.get());
-  }
-
   MockBluetoothDevice* AddMockBluetoothDeviceToAdapter() {
     auto mock_bluetooth_device = std::make_unique<MockBluetoothDevice>(
         adapter_.get(), 0 /* bluetooth_class */, kTestBluetoothDisplayName,
@@ -154,7 +153,7 @@
           fido_discovery_factory_.get());
 };
 
-TEST_F(FidoBleAdapterManagerTest, AdapaterNotPresent) {
+TEST_F(FidoBleAdapterManagerTest, AdapterNotPresent) {
   EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(false));
   EXPECT_CALL(*adapter(), IsPowered()).WillOnce(::testing::Return(false));
   EXPECT_CALL(*adapter(), CanPower()).WillOnce(::testing::Return(false));
@@ -163,7 +162,6 @@
   EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
       .WillOnce(::testing::SaveArg<0>(&data));
 
-  CreateTestBleAdapterManager();
   scoped_task_environment_.RunUntilIdle();
 
   EXPECT_FALSE(data.is_ble_powered);
@@ -179,7 +177,6 @@
   EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
       .WillOnce(::testing::SaveArg<0>(&data));
 
-  CreateTestBleAdapterManager();
   scoped_task_environment_.RunUntilIdle();
 
   EXPECT_TRUE(data.is_ble_powered);
@@ -195,7 +192,6 @@
   EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
       .WillOnce(::testing::SaveArg<0>(&data));
 
-  CreateTestBleAdapterManager();
   scoped_task_environment_.RunUntilIdle();
 
   EXPECT_FALSE(data.is_ble_powered);
@@ -203,7 +199,9 @@
 }
 
 TEST_F(FidoBleAdapterManagerTest, SetBluetoothPowerOn) {
-  auto power_manager = CreateTestBleAdapterManager();
+  scoped_task_environment_.RunUntilIdle();
+  auto& power_manager =
+      fake_request_handler_->get_bluetooth_adapter_manager_for_testing();
   ::testing::InSequence s;
   EXPECT_CALL(*adapter(), SetPowered(true, _, _));
   EXPECT_CALL(*adapter(), SetPowered(false, _, _));
@@ -229,7 +227,9 @@
           }));
   EXPECT_CALL(*mock_bluetooth_device, SetPinCode(kTestPinCode));
 
-  auto adapter_manager = CreateTestBleAdapterManager();
+  scoped_task_environment_.RunUntilIdle();
+  auto& adapter_manager =
+      fake_request_handler_->get_bluetooth_adapter_manager_for_testing();
   test::TestCallbackReceiver<> callback_receiver;
   adapter_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
                                    callback_receiver.callback(),
@@ -251,7 +251,9 @@
       .WillRepeatedly(::testing::Return(adapter()->GetConstMockDevices()));
   EXPECT_CALL(*mock_bluetooth_device, Pair).Times(0);
 
-  auto power_manager = CreateTestBleAdapterManager();
+  scoped_task_environment_.RunUntilIdle();
+  auto& power_manager =
+      fake_request_handler_->get_bluetooth_adapter_manager_for_testing();
   test::TestCallbackReceiver<> callback_receiver;
   power_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
                                  base::DoNothing(),
@@ -276,7 +278,9 @@
             success_callback.Run();
           }));
 
-  auto adapter_manager = CreateTestBleAdapterManager();
+  scoped_task_environment_.RunUntilIdle();
+  auto& adapter_manager =
+      fake_request_handler_->get_bluetooth_adapter_manager_for_testing();
   test::TestCallbackReceiver<> callback_receiver;
   adapter_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
                                    callback_receiver.callback(),
diff --git a/device/fido/fake_fido_discovery.cc b/device/fido/fake_fido_discovery.cc
index ded9ea9..1ea5307 100644
--- a/device/fido/fake_fido_discovery.cc
+++ b/device/fido/fake_fido_discovery.cc
@@ -80,6 +80,13 @@
   return next_cable_discovery_.get();
 }
 
+FakeFidoDiscovery* FakeFidoDiscoveryFactory::ForgeNextPlatformDiscovery(
+    FakeFidoDiscovery::StartMode mode) {
+  next_platform_discovery_ = std::make_unique<FakeFidoDiscovery>(
+      FidoTransportProtocol::kInternal, mode);
+  return next_platform_discovery_.get();
+}
+
 std::unique_ptr<FidoDiscoveryBase> FakeFidoDiscoveryFactory::Create(
     FidoTransportProtocol transport,
     ::service_manager::Connector* connector) {
@@ -94,8 +101,7 @@
       NOTREACHED() << "CaBLE should be handled by CreateCable().";
       return nullptr;
     case FidoTransportProtocol::kInternal:
-      NOTREACHED() << "Internal authenticators should be handled separately.";
-      return nullptr;
+      return std::move(next_platform_discovery_);
   }
   NOTREACHED();
   return nullptr;
diff --git a/device/fido/fake_fido_discovery.h b/device/fido/fake_fido_discovery.h
index 2576406..fe55962 100644
--- a/device/fido/fake_fido_discovery.h
+++ b/device/fido/fake_fido_discovery.h
@@ -119,6 +119,8 @@
   FakeFidoDiscovery* ForgeNextBleDiscovery(StartMode mode = StartMode::kManual);
   FakeFidoDiscovery* ForgeNextCableDiscovery(
       StartMode mode = StartMode::kManual);
+  FakeFidoDiscovery* ForgeNextPlatformDiscovery(
+      StartMode mode = StartMode::kManual);
 
   // device::FidoDiscoveryFactory:
   std::unique_ptr<FidoDiscoveryBase> Create(
@@ -132,6 +134,7 @@
   std::unique_ptr<FakeFidoDiscovery> next_nfc_discovery_;
   std::unique_ptr<FakeFidoDiscovery> next_ble_discovery_;
   std::unique_ptr<FakeFidoDiscovery> next_cable_discovery_;
+  std::unique_ptr<FakeFidoDiscovery> next_platform_discovery_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeFidoDiscoveryFactory);
 };
diff --git a/device/fido/fido_discovery_factory.cc b/device/fido/fido_discovery_factory.cc
index bfa1120a..bf94e5c 100644
--- a/device/fido/fido_discovery_factory.cc
+++ b/device/fido/fido_discovery_factory.cc
@@ -5,7 +5,6 @@
 #include "device/fido/fido_discovery_factory.h"
 
 #include "base/logging.h"
-#include "build/build_config.h"
 #include "device/fido/ble/fido_ble_discovery.h"
 #include "device/fido/cable/fido_cable_discovery.h"
 #include "device/fido/features.h"
@@ -22,6 +21,10 @@
 #include "device/fido/win/webauthn_api.h"
 #endif  // defined(OS_WIN)
 
+#if defined(OS_MACOSX)
+#include "device/fido/mac/discovery.h"
+#endif  // defined(OSMACOSX)
+
 namespace device {
 
 namespace {
@@ -40,6 +43,9 @@
 
 }  // namespace
 
+FidoDiscoveryFactory::FidoDiscoveryFactory() = default;
+FidoDiscoveryFactory::~FidoDiscoveryFactory() = default;
+
 std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::Create(
     FidoTransportProtocol transport,
     service_manager::Connector* connector) {
@@ -56,8 +62,14 @@
       // TODO(https://crbug.com/825949): Add NFC support.
       return nullptr;
     case FidoTransportProtocol::kInternal:
-      NOTREACHED() << "Internal authenticators should be handled separately.";
+#if defined(OS_MACOSX)
+      return mac_touch_id_config_
+                 ? std::make_unique<fido::mac::FidoTouchIdDiscovery>(
+                       *mac_touch_id_config_)
+                 : nullptr;
+#else
       return nullptr;
+#endif  // defined(OS_MACOSX)
   }
   NOTREACHED() << "Unhandled transport type";
   return nullptr;
diff --git a/device/fido/fido_discovery_factory.h b/device/fido/fido_discovery_factory.h
index 71a078f..236b15f 100644
--- a/device/fido/fido_discovery_factory.h
+++ b/device/fido/fido_discovery_factory.h
@@ -9,12 +9,18 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/optional.h"
 #include "build/build_config.h"
 #include "device/fido/cable/cable_discovery_data.h"
 #include "device/fido/fido_device_discovery.h"
 #include "device/fido/fido_discovery_base.h"
+#include "device/fido/fido_request_handler_base.h"
 #include "device/fido/fido_transport_protocol.h"
 
+#if defined(OS_MACOSX)
+#include "device/fido/mac/authenticator_config.h"
+#endif  // defined(OS_MACOSX)
+
 namespace service_manager {
 class Connector;
 }
@@ -25,8 +31,8 @@
 // FidoDiscoveryBase for a given |transport| protocol.
 class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory {
  public:
-  FidoDiscoveryFactory() = default;
-  virtual ~FidoDiscoveryFactory() = default;
+  FidoDiscoveryFactory();
+  virtual ~FidoDiscoveryFactory();
 
   // Instantiates a FidoDiscoveryBase for all protocols except caBLE and
   // internal/platform.
@@ -39,6 +45,15 @@
   // Instantiates a FidoDiscovery for caBLE.
   virtual std::unique_ptr<FidoDiscoveryBase> CreateCable(
       std::vector<CableDiscoveryData> cable_data);
+
+#if defined(OS_MACOSX)
+  // Configures the Touch ID authenticator. Set to base::nullopt to disable it.
+  void set_mac_touch_id_info(
+      base::Optional<fido::mac::AuthenticatorConfig> mac_touch_id_config) {
+    mac_touch_id_config_ = std::move(mac_touch_id_config);
+  }
+#endif  // defined(OS_MACOSX)
+
 #if defined(OS_WIN)
   // Instantiates a FidoDiscovery for the native Windows WebAuthn
   // API where available. Returns nullptr otherwise.
@@ -46,6 +61,9 @@
 #endif  // defined(OS_WIN)
 
  private:
+#if defined(OS_MACOSX)
+  base::Optional<fido::mac::AuthenticatorConfig> mac_touch_id_config_;
+#endif  // defined(OS_MACOSX)
 };
 
 }  // namespace device
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index c10b539..dcad2b1 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -24,20 +24,6 @@
 
 namespace device {
 
-// PlatformAuthenticatorInfo --------------------------
-
-PlatformAuthenticatorInfo::PlatformAuthenticatorInfo(
-    std::unique_ptr<FidoAuthenticator> authenticator_,
-    bool has_recognized_mac_touch_id_credential_)
-    : authenticator(std::move(authenticator_)),
-      has_recognized_mac_touch_id_credential(
-          has_recognized_mac_touch_id_credential_) {}
-PlatformAuthenticatorInfo::PlatformAuthenticatorInfo(
-    PlatformAuthenticatorInfo&&) = default;
-PlatformAuthenticatorInfo& PlatformAuthenticatorInfo::operator=(
-    PlatformAuthenticatorInfo&&) = default;
-PlatformAuthenticatorInfo::~PlatformAuthenticatorInfo() = default;
-
 // FidoRequestHandlerBase::TransportAvailabilityInfo --------------------------
 
 FidoRequestHandlerBase::TransportAvailabilityInfo::TransportAvailabilityInfo() =
@@ -77,36 +63,42 @@
     const base::flat_set<FidoTransportProtocol>& available_transports) {
   // The number of times |notify_observer_callback_| needs to be invoked before
   // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this
-  // is used to wait until all the parts of |transport_availability_info_| are
+  // is used to wait until the parts |transport_availability_info_| are
   // filled out; the |notify_observer_callback_| is invoked once for each part
   // once that part is ready, namely:
   //
-  //   1) Once the platform authenticator related fields are filled out.
-  //   2) [Optionally, if BLE or caBLE enabled] if Bluetooth adapter is present.
+  // 1) [If the platform authenticator is enabled] once the platform
+  //    authenticator is ready.
+  // 2) [If BLE or caBLE are enabled] if Bluetooth adapter is present.
   //
   // On top of that, we wait for (3) an invocation that happens when the
   // |observer_| is set, so that OnTransportAvailabilityEnumerated is never
   // called before the observer is set.
-  size_t transport_info_callback_count = 1u + 0u + 1u;
+  size_t transport_info_callback_count = 1u;
 
 #if defined(OS_WIN)
   if (transport_availability_info_.has_win_native_api_authenticator)
-    transport_info_callback_count++;
+    ++transport_info_callback_count;
 #endif  // defined(OS_WIN)
 
+  transport_availability_info_.available_transports = available_transports;
   for (const auto transport : available_transports) {
     // Construction of CaBleDiscovery is handled by the implementing class as it
     // requires an extension passed on from the relying party.
     if (transport == FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)
       continue;
 
+    std::unique_ptr<FidoDiscoveryBase> discovery =
+        fido_discovery_factory_->Create(transport, connector_);
     if (transport == FidoTransportProtocol::kInternal) {
-      // Platform authenticator availability is always indicated by
-      // |AuthenticatorImpl|.
-      continue;
+      if (discovery) {
+        ++transport_info_callback_count;
+      } else {
+        // The platform authenticator is not configured for this request.
+        transport_availability_info_.available_transports.erase(
+            FidoTransportProtocol::kInternal);
+      }
     }
-
-    auto discovery = fido_discovery_factory_->Create(transport, connector_);
     if (discovery == nullptr) {
       // This can occur in tests when a ScopedVirtualU2fDevice is in effect and
       // HID transports are not configured.
@@ -128,7 +120,6 @@
                        weak_factory_.GetWeakPtr()));
   }
 
-  transport_availability_info_.available_transports = available_transports;
   notify_observer_callback_ = base::BarrierClosure(
       transport_info_callback_count,
       base::BindOnce(
@@ -358,31 +349,12 @@
     notify_observer_callback_.Run();
   }
 #endif  // defined(OS_WIN)
-}
 
-void FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
-    base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info) {
-  DCHECK(!platform_authenticator_);
-  if (platform_authenticator_info &&
-      base::Contains(transport_availability_info_.available_transports,
-                     FidoTransportProtocol::kInternal)) {
-    DCHECK(platform_authenticator_info->authenticator);
-    DCHECK(
-        platform_authenticator_info->authenticator->AuthenticatorTransport() &&
-        *platform_authenticator_info->authenticator->AuthenticatorTransport() ==
-            FidoTransportProtocol::kInternal);
-    transport_availability_info_.has_recognized_mac_touch_id_credential =
-        platform_authenticator_info->has_recognized_mac_touch_id_credential;
-    platform_authenticator_ =
-        std::move(platform_authenticator_info->authenticator);
-    AddAuthenticator(platform_authenticator_.get());
-  } else {
-    transport_availability_info_.available_transports.erase(
-        FidoTransportProtocol::kInternal);
+  if (authenticator->AuthenticatorTransport() ==
+      FidoTransportProtocol::kInternal) {
+    DCHECK(notify_observer_callback_);
+    notify_observer_callback_.Run();
   }
-
-  DCHECK(notify_observer_callback_);
-  notify_observer_callback_.Run();
 }
 
 bool FidoRequestHandlerBase::HasAuthenticator(
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index 56523f39..af0f5e4a 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -34,17 +34,6 @@
 class FidoAuthenticator;
 class FidoDiscoveryFactory;
 
-struct COMPONENT_EXPORT(DEVICE_FIDO) PlatformAuthenticatorInfo {
-  PlatformAuthenticatorInfo(std::unique_ptr<FidoAuthenticator> authenticator,
-                            bool has_recognized_mac_touch_id_credential);
-  PlatformAuthenticatorInfo(PlatformAuthenticatorInfo&&);
-  PlatformAuthenticatorInfo& operator=(PlatformAuthenticatorInfo&& other);
-  ~PlatformAuthenticatorInfo();
-
-  std::unique_ptr<FidoAuthenticator> authenticator;
-  bool has_recognized_mac_touch_id_credential;
-};
-
 // Base class that handles authenticator discovery/removal. Its lifetime is
 // equivalent to that of a single WebAuthn request. For each authenticator, the
 // per-device work is carried out by one FidoAuthenticator instance, which is
@@ -211,13 +200,6 @@
     notify_observer_callback_.Run();
   }
 
-  // Set the platform authenticator for this request, if one is available.
-  // |AuthenticatorImpl| must call this method after invoking |set_observer|
-  // even if no platform authenticator is available, in which case it passes
-  // nullptr.
-  virtual void SetPlatformAuthenticatorOrMarkUnavailable(
-      base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info);
-
   // Returns whether FidoAuthenticator with id equal to |authenticator_id|
   // exists. Fake FidoRequestHandler objects used in testing overrides this
   // function to simulate scenarios where authenticator with |authenticator_id|
@@ -232,6 +214,11 @@
     return active_authenticators_;
   }
 
+  std::unique_ptr<BleAdapterManager>&
+  get_bluetooth_adapter_manager_for_testing() {
+    return bluetooth_adapter_manager_;
+  }
+
  protected:
   // Subclasses implement this method to dispatch their request onto the given
   // FidoAuthenticator. The FidoAuthenticator is owned by this
@@ -286,9 +273,6 @@
   TransportAvailabilityInfo transport_availability_info_;
   base::RepeatingClosure notify_observer_callback_;
   std::unique_ptr<BleAdapterManager> bluetooth_adapter_manager_;
-  // TODO(martinkr): Inject platform authenticators through a new
-  // FidoDiscoveryBase specialization and hold ownership there.
-  std::unique_ptr<FidoAuthenticator> platform_authenticator_;
   service_manager::Connector* const connector_;
 
   base::WeakPtrFactory<FidoRequestHandlerBase> weak_factory_;
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index 5bb8f4a..8517667 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -331,7 +331,6 @@
             {FidoTransportProtocol::kUsbHumanInterfaceDevice,
              FidoTransportProtocol::kBluetoothLowEnergy}),
         cb_.callback());
-    handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
     return handler;
   }
 
@@ -556,8 +555,7 @@
 // pending authenticators.
 TEST_F(FidoRequestHandlerTest,
        TestRequestWithOperationDeniedErrorInternalTransport) {
-  auto request_handler = CreateFakeHandler();
-  discovery()->WaitForCallToStartAndSimulateSuccess();
+  TestObserver observer;
 
   // Device will send CTAP2_ERR_OPERATION_DENIED.
   auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation(
@@ -567,12 +565,23 @@
                                        CreateFakeOperationDeniedError(),
                                        base::TimeDelta::FromMicroseconds(10));
 
+  ForgeNextHidDiscovery();
+  auto* platform_discovery =
+      fake_discovery_factory_.ForgeNextPlatformDiscovery();
+  auto request_handler = std::make_unique<FakeFidoRequestHandler>(
+      &fake_discovery_factory_,
+      base::flat_set<FidoTransportProtocol>(
+          {FidoTransportProtocol::kInternal,
+           FidoTransportProtocol::kUsbHumanInterfaceDevice}),
+      callback().callback());
+  request_handler->set_observer(&observer);
+
   auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
   device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
   EXPECT_CALL(*device1, Cancel(_));
 
   discovery()->AddDevice(std::move(device0));
-  discovery()->AddDevice(std::move(device1));
+  platform_discovery->AddDevice(std::move(device1));
 
   scoped_task_environment_.FastForwardUntilNoTasksRemain();
   callback().WaitForCallback();
@@ -606,9 +615,8 @@
   EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
 }
 
-// Requests should be dispatched to the authenticator passed to
-// SetPlatformAuthenticatorOrMarkUnavailable.
-TEST_F(FidoRequestHandlerTest, TestSetPlatformAuthenticator) {
+// Requests should be dispatched to the platform authenticator.
+TEST_F(FidoRequestHandlerTest, TestWithPlatformAuthenticator) {
   // A platform authenticator usually wouldn't usually use a FidoDevice, but
   // that's not the point of the test here. The test is only trying to ensure
   // the authenticator gets injected and used.
@@ -621,8 +629,7 @@
   device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                       CreateFakeSuccessDeviceResponse());
   device->SetDeviceTransport(FidoTransportProtocol::kInternal);
-  auto authenticator =
-      std::make_unique<FidoDeviceAuthenticator>(std::move(device));
+  auto* fake_discovery = fake_discovery_factory_.ForgeNextPlatformDiscovery();
 
   TestObserver observer;
   auto request_handler = std::make_unique<FakeFidoRequestHandler>(
@@ -630,8 +637,7 @@
       base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
       callback().callback());
   request_handler->set_observer(&observer);
-  request_handler->SetPlatformAuthenticatorOrMarkUnavailable(
-      PlatformAuthenticatorInfo(std::move(authenticator), false));
+  fake_discovery->AddDevice(std::move(device));
 
   observer.WaitForAndExpectAvailableTransportsAre(
       {FidoTransportProtocol::kInternal},
@@ -642,43 +648,6 @@
   EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
 }
 
-// SetPlatformAuthenticatorOrMarkUnavailable should propagate the
-// has_recognized_mac_touch_id_credential field.
-TEST_F(FidoRequestHandlerTest,
-       TestSetPlatformAuthenticatorHasTouchIdCredential) {
-  // A platform authenticator usually wouldn't usually use a FidoDevice, but
-  // that's not the point of the test here. The test is only trying to ensure
-  // the authenticator gets injected and used.
-  auto device = MockFidoDevice::MakeCtap();
-  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
-  // Device returns success response.
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
-  device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
-                                      CreateFakeSuccessDeviceResponse());
-  device->SetDeviceTransport(FidoTransportProtocol::kInternal);
-  auto authenticator =
-      std::make_unique<FidoDeviceAuthenticator>(std::move(device));
-
-  TestObserver observer;
-  auto request_handler = std::make_unique<FakeFidoRequestHandler>(
-      &fake_discovery_factory_,
-      base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
-      callback().callback());
-  request_handler->set_observer(&observer);
-  request_handler->SetPlatformAuthenticatorOrMarkUnavailable(
-      PlatformAuthenticatorInfo(std::move(authenticator), true));
-
-  observer.WaitForAndExpectAvailableTransportsAre(
-      {FidoTransportProtocol::kInternal},
-      true /* has_recognized_mac_touch_id_credential */);
-
-  callback().WaitForCallback();
-  EXPECT_TRUE(request_handler->is_complete());
-  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
-}
-
 TEST_F(FidoRequestHandlerTest, InternalTransportDisallowedIfMarkedUnavailable) {
   TestObserver observer;
   auto request_handler = std::make_unique<FakeFidoRequestHandler>(
@@ -686,7 +655,6 @@
       base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
       callback().callback());
   request_handler->set_observer(&observer);
-  request_handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
 
   observer.WaitForAndExpectAvailableTransportsAre({});
 }
@@ -756,7 +724,6 @@
     EmptyRequestHandler request_handler(
         {FidoTransportProtocol::kUsbHumanInterfaceDevice},
         &fake_discovery_factory_);
-    request_handler.SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
     request_handler.set_observer(&observer);
     scoped_task_environment_.FastForwardUntilNoTasksRemain();
 
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 0e38841..dbd2d1e 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -66,6 +66,7 @@
     ble_discovery_ = fake_discovery_factory_->ForgeNextBleDiscovery();
     cable_discovery_ = fake_discovery_factory_->ForgeNextCableDiscovery();
     nfc_discovery_ = fake_discovery_factory_->ForgeNextNfcDiscovery();
+    platform_discovery_ = fake_discovery_factory_->ForgeNextPlatformDiscovery();
   }
 
   CtapGetAssertionRequest CreateTestRequestWithCableExtension() {
@@ -102,8 +103,6 @@
         nullptr /* connector */, fake_discovery_factory_.get(),
         supported_transports_, std::move(request),
         get_assertion_cb_.callback());
-    handler->SetPlatformAuthenticatorOrMarkUnavailable(
-        CreatePlatformAuthenticator());
     return handler;
   }
 
@@ -119,6 +118,8 @@
       cable_discovery()->WaitForCallToStartAndSimulateSuccess();
     if (base::Contains(transports, Transport::kNearFieldCommunication))
       nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
+    if (base::Contains(transports, Transport::kInternal))
+      platform_discovery()->WaitForCallToStartAndSimulateSuccess();
 
     scoped_task_environment_.FastForwardUntilNoTasksRemain();
     EXPECT_FALSE(get_assertion_callback().was_called());
@@ -132,11 +133,8 @@
       EXPECT_FALSE(cable_discovery()->is_start_requested());
     if (!base::Contains(transports, Transport::kNearFieldCommunication))
       EXPECT_FALSE(nfc_discovery()->is_start_requested());
-
-    // Even with FidoTransportProtocol::kInternal allowed, unless the platform
-    // authenticator factory returns a FidoAuthenticator instance (which it will
-    // not be default), the transport will be marked `unavailable`.
-    transports.erase(Transport::kInternal);
+    if (!base::Contains(transports, Transport::kInternal))
+      EXPECT_FALSE(platform_discovery()->is_start_requested());
 
     EXPECT_THAT(
         request_handler->transport_availability_info().available_transports,
@@ -153,29 +151,19 @@
   test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
   test::FakeFidoDiscovery* cable_discovery() const { return cable_discovery_; }
   test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
+  test::FakeFidoDiscovery* platform_discovery() const {
+    return platform_discovery_;
+  }
   TestGetAssertionRequestCallback& get_assertion_callback() {
     return get_assertion_cb_;
   }
 
-  void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
-    pending_mock_platform_device_ = std::move(device);
-  }
-
   void set_supported_transports(
       base::flat_set<FidoTransportProtocol> transports) {
     supported_transports_ = std::move(transports);
   }
 
  protected:
-  base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
-    if (!pending_mock_platform_device_)
-      return base::nullopt;
-    return PlatformAuthenticatorInfo(
-        std::make_unique<FidoDeviceAuthenticator>(
-            std::move(pending_mock_platform_device_)),
-        false /* has_recognized_mac_touch_id_credential_available */);
-  }
-
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<test::FakeFidoDiscoveryFactory> fake_discovery_factory_ =
@@ -184,8 +172,8 @@
   test::FakeFidoDiscovery* ble_discovery_;
   test::FakeFidoDiscovery* cable_discovery_;
   test::FakeFidoDiscovery* nfc_discovery_;
+  test::FakeFidoDiscovery* platform_discovery_;
   scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
-  std::unique_ptr<MockFidoDevice> pending_mock_platform_device_;
   TestGetAssertionRequestCallback get_assertion_cb_;
   base::flat_set<FidoTransportProtocol> supported_transports_ =
       GetAllTransportProtocols();
@@ -654,6 +642,9 @@
 
   set_supported_transports({FidoTransportProtocol::kInternal});
 
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+
   auto device = MockFidoDevice::MakeCtap(
       ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
   EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
@@ -664,10 +655,9 @@
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion,
       test_data::kTestGetAssertionResponse);
-  set_mock_platform_device(std::move(device));
+  platform_discovery()->WaitForCallToStartAndSimulateSuccess();
+  platform_discovery()->AddDevice(std::move(device));
 
-  auto request_handler =
-      CreateGetAssertionHandlerWithRequest(std::move(request));
   get_assertion_callback().WaitForCallback();
 
   EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
@@ -684,6 +674,8 @@
 // cancelled.
 TEST_F(FidoGetAssertionHandlerTest,
        TestRequestWithOperationDeniedErrorPlatform) {
+  auto request_handler = CreateGetAssertionHandlerCtap();
+
   auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
       test_data::kTestGetInfoResponsePlatformDevice);
   platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
@@ -691,14 +683,14 @@
       CtapRequestCommand::kAuthenticatorGetAssertion,
       CtapDeviceResponseCode::kCtap2ErrOperationDenied,
       base::TimeDelta::FromMicroseconds(10));
-  set_mock_platform_device(std::move(platform_device));
+  platform_discovery()->WaitForCallToStartAndSimulateSuccess();
+  platform_discovery()->AddDevice(std::move(platform_device));
 
   auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
   other_device->ExpectCtap2CommandAndDoNotRespond(
       CtapRequestCommand::kAuthenticatorGetAssertion);
   EXPECT_CALL(*other_device, Cancel);
 
-  auto request_handler = CreateGetAssertionHandlerCtap();
   discovery()->WaitForCallToStartAndSimulateSuccess();
   discovery()->AddDevice(std::move(other_device));
 
@@ -821,7 +813,6 @@
       base::flat_set<FidoTransportProtocol>(
           {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
       std::move(request), cb.callback());
-  request_handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
 
   scoped_task_environment.FastForwardUntilNoTasksRemain();
   EXPECT_FALSE(cb.was_called());
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 8fe2362..2c322ca6 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -14,6 +14,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/cable/fido_cable_discovery.h"
@@ -24,6 +25,10 @@
 #include "device/fido/get_assertion_task.h"
 #include "device/fido/pin.h"
 
+#if defined(OS_MACOSX)
+#include "device/fido/mac/authenticator.h"
+#endif  // defined(OS_MACOSX)
+
 namespace device {
 
 namespace {
@@ -304,6 +309,23 @@
                      weak_factory_.GetWeakPtr(), authenticator));
 }
 
+void GetAssertionRequestHandler::AuthenticatorAdded(
+    FidoDiscoveryBase* discovery,
+    FidoAuthenticator* authenticator) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+
+#if defined(OS_MACOSX)
+  if (authenticator->AuthenticatorTransport() ==
+      FidoTransportProtocol::kInternal) {
+    transport_availability_info().has_recognized_mac_touch_id_credential =
+        static_cast<fido::mac::TouchIdAuthenticator*>(authenticator)
+            ->HasCredentialForGetAssertionRequest(request_);
+  }
+#endif  // defined(OS_MACOSX)
+
+  FidoRequestHandlerBase::AuthenticatorAdded(discovery, authenticator);
+}
+
 void GetAssertionRequestHandler::AuthenticatorRemoved(
     FidoDiscoveryBase* discovery,
     FidoAuthenticator* authenticator) {
diff --git a/device/fido/get_assertion_request_handler.h b/device/fido/get_assertion_request_handler.h
index 9233d84..bf4f3e7 100644
--- a/device/fido/get_assertion_request_handler.h
+++ b/device/fido/get_assertion_request_handler.h
@@ -54,6 +54,8 @@
 
   // FidoRequestHandlerBase:
   void DispatchRequest(FidoAuthenticator* authenticator) override;
+  void AuthenticatorAdded(FidoDiscoveryBase* discovery,
+                          FidoAuthenticator* authenticator) override;
   void AuthenticatorRemoved(FidoDiscoveryBase* discovery,
                             FidoAuthenticator* authenticator) override;
 
diff --git a/device/fido/mac/authenticator.h b/device/fido/mac/authenticator.h
index 787994a7..0dc83a4 100644
--- a/device/fido/mac/authenticator.h
+++ b/device/fido/mac/authenticator.h
@@ -39,12 +39,9 @@
   // off-the-record/incognito context.
   static bool IsAvailable(const AuthenticatorConfig& config);
 
-  // CreateIfAvailable returns a TouchIdAuthenticator if IsAvailable() returns
-  // true and nullptr otherwise.
-  static std::unique_ptr<TouchIdAuthenticator> CreateIfAvailable(
-      AuthenticatorConfig config);
-
-  static std::unique_ptr<TouchIdAuthenticator> CreateForTesting(
+  // CreateIfAvailable returns a TouchIdAuthenticator. Callers must check
+  // IsAvailable() first.
+  static std::unique_ptr<TouchIdAuthenticator> Create(
       AuthenticatorConfig config);
 
   ~TouchIdAuthenticator() override;
diff --git a/device/fido/mac/authenticator.mm b/device/fido/mac/authenticator.mm
index 7436b4d..6efc2db 100644
--- a/device/fido/mac/authenticator.mm
+++ b/device/fido/mac/authenticator.mm
@@ -39,17 +39,9 @@
 }
 
 // static
-std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::CreateIfAvailable(
+std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::Create(
     AuthenticatorConfig config) {
-  return IsAvailable(config) ? base::WrapUnique(new TouchIdAuthenticator(
-                                   std::move(config.keychain_access_group),
-                                   std::move(config.metadata_secret)))
-                             : nullptr;
-}
-
-// static
-std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::CreateForTesting(
-    AuthenticatorConfig config) {
+  DCHECK(IsAvailable(config));
   return base::WrapUnique(
       new TouchIdAuthenticator(std::move(config.keychain_access_group),
                                std::move(config.metadata_secret)));
diff --git a/device/fido/mac/browsing_data_deletion_unittest.mm b/device/fido/mac/browsing_data_deletion_unittest.mm
index a5f590c..02abcc9 100644
--- a/device/fido/mac/browsing_data_deletion_unittest.mm
+++ b/device/fido/mac/browsing_data_deletion_unittest.mm
@@ -130,7 +130,7 @@
 
   std::unique_ptr<TouchIdAuthenticator> MakeAuthenticator(
       std::string profile_metadata_secret) {
-    return TouchIdAuthenticator::CreateForTesting(
+    return TouchIdAuthenticator::Create(
         {kKeychainAccessGroup, std::move(profile_metadata_secret)});
   }
 
diff --git a/device/fido/mac/discovery.cc b/device/fido/mac/discovery.cc
new file mode 100644
index 0000000..d7f79396
--- /dev/null
+++ b/device/fido/mac/discovery.cc
@@ -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.
+
+#include "device/fido/mac/discovery.h"
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "device/fido/mac/authenticator.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+FidoTouchIdDiscovery::FidoTouchIdDiscovery(
+    AuthenticatorConfig authenticator_config)
+    : FidoDiscoveryBase(FidoTransportProtocol::kInternal),
+      authenticator_config_(std::move(authenticator_config)),
+      weak_factory_(this) {}
+
+FidoTouchIdDiscovery::~FidoTouchIdDiscovery() = default;
+
+void FidoTouchIdDiscovery::Start() {
+  DCHECK(!authenticator_);
+  if (!observer()) {
+    return;
+  }
+
+  if (!TouchIdAuthenticator::IsAvailable(authenticator_config_)) {
+    observer()->DiscoveryStarted(this, /*success=*/false);
+    return;
+  }
+
+  observer()->DiscoveryStarted(this, /*success=*/true);
+
+  // Start() is currently invoked synchronously in the
+  // FidoRequestHandler ctor. Invoke AddAuthenticator() asynchronously
+  // to avoid hairpinning FidoRequestHandler::AuthenticatorAdded()
+  // before the request handler has an observer.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&FidoTouchIdDiscovery::AddAuthenticator,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void FidoTouchIdDiscovery::AddAuthenticator() {
+  DCHECK(TouchIdAuthenticator::IsAvailable(authenticator_config_));
+  authenticator_ = TouchIdAuthenticator::Create(authenticator_config_);
+  observer()->AuthenticatorAdded(this, authenticator_.get());
+}
+
+}  // namespace mac
+}  // namespace fido
+}  // namespace device
diff --git a/device/fido/mac/discovery.h b/device/fido/mac/discovery.h
new file mode 100644
index 0000000..96a3402
--- /dev/null
+++ b/device/fido/mac/discovery.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 DEVICE_FIDO_MAC_DISCOVERY_H_
+#define DEVICE_FIDO_MAC_DISCOVERY_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/memory/weak_ptr.h"
+#include "device/fido/fido_discovery_base.h"
+#include "device/fido/mac/authenticator_config.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+class TouchIdAuthenticator;
+
+// Creates Touch ID authenticators.
+class COMPONENT_EXPORT(DEVICE_FIDO) FidoTouchIdDiscovery
+    : public FidoDiscoveryBase {
+ public:
+  explicit FidoTouchIdDiscovery(AuthenticatorConfig config);
+  ~FidoTouchIdDiscovery() override;
+
+  // FidoDiscoveryBase:
+  void Start() override;
+
+ private:
+  void AddAuthenticator();
+
+  AuthenticatorConfig authenticator_config_;
+  std::unique_ptr<TouchIdAuthenticator> authenticator_;
+  base::WeakPtrFactory<FidoTouchIdDiscovery> weak_factory_;
+};
+
+}  // namespace mac
+}  // namespace fido
+}  // namespace device
+
+#endif  // DEVICE_FIDO_MAC_DISCOVERY_H_
diff --git a/device/fido/mac/scoped_touch_id_test_environment.mm b/device/fido/mac/scoped_touch_id_test_environment.mm
index 6e407c4..34eb9b1 100644
--- a/device/fido/mac/scoped_touch_id_test_environment.mm
+++ b/device/fido/mac/scoped_touch_id_test_environment.mm
@@ -44,6 +44,7 @@
       touch_id_context_touch_id_available_ptr_;
 
   Keychain::ClearInstanceOverride();
+  g_current_environment = nullptr;
 }
 
 // static
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index 04cb0b3..68572c5 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -66,6 +66,7 @@
     discovery_ = fake_discovery_factory_->ForgeNextHidDiscovery();
     ble_discovery_ = fake_discovery_factory_->ForgeNextBleDiscovery();
     nfc_discovery_ = fake_discovery_factory_->ForgeNextNfcDiscovery();
+    platform_discovery_ = fake_discovery_factory_->ForgeNextPlatformDiscovery();
   }
 
   std::unique_ptr<MakeCredentialRequestHandler> CreateMakeCredentialHandler() {
@@ -91,8 +92,8 @@
         nullptr, fake_discovery_factory_.get(), supported_transports_,
         std::move(request_parameter),
         std::move(authenticator_selection_criteria), cb_.callback());
-    handler->SetPlatformAuthenticatorOrMarkUnavailable(
-        CreatePlatformAuthenticator());
+    if (pending_mock_platform_device_)
+      platform_discovery_->AddDevice(std::move(pending_mock_platform_device_));
     return handler;
   }
 
@@ -137,15 +138,6 @@
   }
 
  protected:
-  base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
-    if (!pending_mock_platform_device_)
-      return base::nullopt;
-    return PlatformAuthenticatorInfo(
-        std::make_unique<FidoDeviceAuthenticator>(
-            std::move(pending_mock_platform_device_)),
-        false /* has_recognized_mac_touch_id_credential_available */);
-  }
-
   base::test::ScopedTaskEnvironment scoped_task_environment_{
       base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<test::FakeFidoDiscoveryFactory> fake_discovery_factory_ =
@@ -153,6 +145,7 @@
   test::FakeFidoDiscovery* discovery_;
   test::FakeFidoDiscovery* ble_discovery_;
   test::FakeFidoDiscovery* nfc_discovery_;
+  test::FakeFidoDiscovery* platform_discovery_;
   scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
   std::unique_ptr<MockFidoDevice> pending_mock_platform_device_;
   TestMakeCredentialRequestCallback cb_;
@@ -262,34 +255,6 @@
             callback().status());
 }
 
-// TODO(crbug.com/873710): Platform authenticators are temporarily disabled if
-// AuthenticatorAttachment is unset (kAny).
-TEST_F(FidoMakeCredentialHandlerTest, AnyAttachment) {
-  auto platform_device = MockFidoDevice::MakeCtap(
-      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
-  platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
-  set_mock_platform_device(std::move(platform_device));
-  EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
-  auto request_handler =
-      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria(
-              AuthenticatorAttachment::kAny, /*require_resident_key=*/false,
-              UserVerificationRequirement::kPreferred));
-
-  // MakeCredentialHandler will not dispatch the kAny request to the platform
-  // authenticator since the request does not get dispatched through UI. Despite
-  // setting a platform authenticator, the internal transport never becomes
-  // available.
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_FALSE(callback().was_called());
-
-  // kCloudAssistedBluetoothLowEnergy not yet supported for MakeCredential.
-  ExpectAllowedTransportsForRequestAre(
-      request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
-                              FidoTransportProtocol::kNearFieldCommunication,
-                              FidoTransportProtocol::kUsbHumanInterfaceDevice});
-}
-
 TEST_F(FidoMakeCredentialHandlerTest, CrossPlatformAttachment) {
   EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
   auto request_handler =
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index 811acf5..aa726ca2 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -572,27 +572,4 @@
                      weak_factory_.GetWeakPtr(), authenticator_));
 }
 
-void MakeCredentialRequestHandler::SetPlatformAuthenticatorOrMarkUnavailable(
-    base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
-
-  if (platform_authenticator_info) {
-    // TODO(crbug.com/873710): In the case of a request with
-    // AuthenticatorAttachment::kAny and when there is no embedder-provided
-    // transport selection UI, disable the platform authenticator to avoid the
-    // Touch ID fingerprint prompt competing with external devices.
-    const bool has_transport_selection_ui =
-        observer() && observer()->EmbedderControlsAuthenticatorDispatch(
-                          *platform_authenticator_info->authenticator);
-    if (authenticator_selection_criteria_.authenticator_attachment() ==
-            AuthenticatorAttachment::kAny &&
-        !has_transport_selection_ui) {
-      platform_authenticator_info = base::nullopt;
-    }
-  }
-
-  FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
-      std::move(platform_authenticator_info));
-}
-
 }  // namespace device
diff --git a/device/fido/make_credential_request_handler.h b/device/fido/make_credential_request_handler.h
index 5b57f2ae..fb90ddd 100644
--- a/device/fido/make_credential_request_handler.h
+++ b/device/fido/make_credential_request_handler.h
@@ -42,11 +42,6 @@
       CompletionCallback completion_callback);
   ~MakeCredentialRequestHandler() override;
 
-  // FidoRequestHandlerBase:
-  void SetPlatformAuthenticatorOrMarkUnavailable(
-      base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info)
-      override;
-
  private:
   enum class State {
     kWaitingForTouch,
diff --git a/device/vr/oculus/oculus_gamepad_helper.cc b/device/vr/oculus/oculus_gamepad_helper.cc
index 3af77379..5a93ddf9 100644
--- a/device/vr/oculus/oculus_gamepad_helper.cc
+++ b/device/vr/oculus/oculus_gamepad_helper.cc
@@ -291,8 +291,7 @@
 
   OculusGamepadBuilder touch(input_touch, hand);
 
-  // TODO(https://crbug.com/966060): Determine if inverting the y value here is
-  // necessary.
+  // Invert the y axis because -1 is up in the Gamepad API, but down in Oculus.
   touch.AddAxis(input_touch.Thumbstick[hand].x);
   touch.AddAxis(-input_touch.Thumbstick[hand].y);
 
diff --git a/device/vr/openvr/openvr_gamepad_helper.cc b/device/vr/openvr/openvr_gamepad_helper.cc
index ca303e9..1f73992 100644
--- a/device/vr/openvr/openvr_gamepad_helper.cc
+++ b/device/vr/openvr/openvr_gamepad_helper.cc
@@ -60,8 +60,7 @@
 
     GamepadBuilder::ButtonData button_data;
 
-    // TODO(https://crbug.com/966060): Determine if inverting the y value here
-    // is necessary.
+    // Invert the y axis because -1 is up in the Gamepad API but down in OpenVR.
     double x_axis = controller_state.rAxis[j].x;
     double y_axis = -controller_state.rAxis[j].y;
 
diff --git a/device/vr/openvr/test/test_helper.cc b/device/vr/openvr/test/test_helper.cc
index 87580f0..365bc15 100644
--- a/device/vr/openvr/test/test_helper.cc
+++ b/device/vr/openvr/test/test_helper.cc
@@ -331,9 +331,7 @@
     controller_state->ulButtonTouched =
         TranslateButtonMask(controller_data.buttons_touched);
     for (unsigned int i = 0; i < device::kMaxNumAxes; ++i) {
-      // Invert the y axis because gamepad and the rest of Chrome follows the
-      // convention that -1 is up, but WMR reports -1 as down.
-      // TODO(https://crbug.com/966060): Revisit this if the convention changes.
+      // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
       controller_state->rAxis[i].x = controller_data.axis_data[i].x;
       controller_state->rAxis[i].y = -controller_data.axis_data[i].y;
     }
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 27739b2..2216ff2 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
@@ -286,8 +286,7 @@
   data.touched = data.pressed;
   data.value = data.pressed ? 1.0 : 0.0;
 
-  // TODO(https://crbug.com/966060): Determine if inverting the y value here is
-  // necessary.
+  // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
   data.has_both_axes = true;
   data.x_axis = source_state->ThumbstickX();
   data.y_axis = -source_state->ThumbstickY();
@@ -303,8 +302,7 @@
   // The Touchpad does have Axes, but if it's not touched, they are 0.
   data.has_both_axes = true;
   if (data.touched) {
-    // TODO(https://crbug.com/966060): Determine if inverting the y value here
-    // is necessary.
+    // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
     data.x_axis = source_state->TouchpadX();
     data.y_axis = -source_state->TouchpadY();
   } else {
diff --git a/device/vr/windows_mixed_reality/wrappers/test/mock_wmr_input_source_state.cc b/device/vr/windows_mixed_reality/wrappers/test/mock_wmr_input_source_state.cc
index a410918..8243aa7 100644
--- a/device/vr/windows_mixed_reality/wrappers/test/mock_wmr_input_source_state.cc
+++ b/device/vr/windows_mixed_reality/wrappers/test/mock_wmr_input_source_state.cc
@@ -68,9 +68,7 @@
   return val;
 }
 
-// Invert the y axis because gamepad and the rest of Chrome follows the
-// convention that -1 is up, but WMR reports -1 as down.
-// TODO(https://crbug.com/966060): Revisit this if the convention changes.
+// Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
 double MockWMRInputSourceState::ThumbstickY() const {
   double val = data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisPrimary)].y;
   // Should be in [-1, 1] for joysticks.
@@ -88,9 +86,7 @@
   return val;
 }
 
-// Invert the y axis because gamepad and the rest of Chrome follows the
-// convention that -1 is up, but WMR reports -1 as down.
-// TODO(https://crbug.com/966060): Revisit this if the convention changes.
+// Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
 double MockWMRInputSourceState::TouchpadY() const {
   double val =
       data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisSecondary)].y;
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 1a0a43a..b83d047e 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -2363,13 +2363,6 @@
     return true;
   }
 
-  if (request->is_web_view) {
-    const bool has_declarative_rules = !relevant_registries.empty();
-    UMA_HISTOGRAM_BOOLEAN(
-        "Extensions.DeclarativeWebRequest.WebViewRequestDeclarativeRules",
-        has_declarative_rules);
-  }
-
   bool deltas_created = false;
   for (const auto& it : relevant_registries) {
     WebRequestRulesRegistry* rules_registry = it.first;
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 4187e85..f628346 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1423,6 +1423,8 @@
   ACTION_SETBADGEBACKGROUNDCOLOR = 1360,
   AUTOTESTPRIVATE_SETARCAPPWINDOWSTATE = 1361,
   ACCESSIBILITY_PRIVATE_OPENSETTINGSSUBPAGE = 1362,
+  ACTION_ENABLE = 1363,
+  ACTION_DISABLE = 1364,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 7a04e76..23ff1550 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1844,6 +1844,9 @@
   DeleteExtensionPrefs(id);
 }
 
+const char ExtensionPrefs::kFakeObsoletePrefForTesting[] =
+    "__fake_obsolete_pref_for_testing";
+
 ExtensionPrefs::ExtensionPrefs(
     content::BrowserContext* browser_context,
     PrefService* prefs,
@@ -2105,4 +2108,29 @@
     observer.OnExtensionRegistered(extension_id, install_time, is_enabled);
 }
 
+void ExtensionPrefs::MigrateObsoleteExtensionPrefs() {
+  const base::Value* extensions_dictionary =
+      prefs_->GetDictionary(pref_names::kExtensions);
+  DCHECK(extensions_dictionary->is_dict());
+
+  // Please clean this list up periodically, removing any entries added more
+  // than a year ago (with the exception of the testing key).
+  constexpr const char* kObsoleteKeys[] = {
+      // Permanent testing-only key.
+      kFakeObsoletePrefForTesting,
+
+      // Added 2019-07.
+      "has_set_script_all_urls",
+  };
+
+  for (const auto& key_value : extensions_dictionary->DictItems()) {
+    if (!crx_file::id_util::IdIsValid(key_value.first))
+      continue;
+    ScopedExtensionPrefUpdate update(prefs_, key_value.first);
+    std::unique_ptr<prefs::DictionaryValueUpdate> inner_update = update.Get();
+    for (const char* key : kObsoleteKeys)
+      inner_update->Remove(key, nullptr);
+  }
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 25f7955..b11f1c60 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -614,12 +614,20 @@
   void SetDNRUseActionCountAsBadgeText(const ExtensionId& extension_id,
                                        bool use_action_count_as_badge_text);
 
+  // Iterates over the extension pref entries and removes any obsolete keys. We
+  // need to do this here specially (rather than in
+  // MigrateObsoleteProfilePrefs()) because these entries are subkeys of the
+  // extension's dictionary, which is keyed on the extension ID.
+  void MigrateObsoleteExtensionPrefs();
+
   // When called before the ExtensionService is created, alerts that are
   // normally suppressed in first run will still trigger.
   static void SetRunAlertsInFirstRunForTest();
 
   void ClearExternalUninstallForTesting(const ExtensionId& id);
 
+  static const char kFakeObsoletePrefForTesting[];
+
  private:
   friend class ExtensionPrefsBlacklistedExtensions;  // Unit test.
   friend class ExtensionPrefsComponentExtension;     // Unit test.
diff --git a/gpu/command_buffer/service/swap_chain_factory_dxgi_unittest.cc b/gpu/command_buffer/service/swap_chain_factory_dxgi_unittest.cc
index c27dcde..2e2ed8d 100644
--- a/gpu/command_buffer/service/swap_chain_factory_dxgi_unittest.cc
+++ b/gpu/command_buffer/service/swap_chain_factory_dxgi_unittest.cc
@@ -15,6 +15,7 @@
 #include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image_representation.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_angle_util_win.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_image_dxgi_swap_chain.h"
 #include "ui/gl/gl_surface.h"
@@ -78,9 +79,9 @@
   GLenum expected_target = GL_TEXTURE_2D;
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
   scoped_refptr<gles2::TexturePassthrough> back_passthrough_texture;
-  gles2::Texture* back_texture;
-  gl::GLImageDXGISwapChain* back_image;
-  gl::GLImageDXGISwapChain* front_image;
+  gles2::Texture* back_texture = nullptr;
+  gl::GLImageDXGISwapChain* back_image = nullptr;
+  gl::GLImageDXGISwapChain* front_image = nullptr;
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> back_factory_ref =
       shared_image_manager_.Register(std::move(backings.back_buffer),
@@ -217,7 +218,6 @@
 
   back_factory_ref->PresentSwapChain();
 
-  // TODO(ashithasantosh): Check contents of front buffer.
   {
     GLubyte pixel_color[4];
     const uint8_t expected_color[4] = {0, 0, 0, 255};
@@ -228,6 +228,49 @@
     EXPECT_EQ(expected_color[2], pixel_color[2]);
     EXPECT_EQ(expected_color[3], pixel_color[3]);
   }
+
+  {
+    // A staging texture must be used to check front buffer since it cannot be
+    // bound to an FBO or use ReadPixels.
+    D3D11_TEXTURE2D_DESC desc = {};
+    desc.Width = 1;
+    desc.Height = 1;
+    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    desc.MipLevels = 1;
+    desc.ArraySize = 1;
+    desc.Usage = D3D11_USAGE_STAGING;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+    desc.SampleDesc.Count = 1;
+
+    auto d3d11_device = gl::QueryD3D11DeviceObjectFromANGLE();
+    ASSERT_TRUE(d3d11_device);
+
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture;
+    ASSERT_TRUE(SUCCEEDED(
+        d3d11_device->CreateTexture2D(&desc, nullptr, &staging_texture)));
+
+    Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
+    d3d11_device->GetImmediateContext(&context);
+    ASSERT_TRUE(context);
+
+    context->CopyResource(staging_texture.Get(), front_image->texture().Get());
+
+    D3D11_MAPPED_SUBRESOURCE mapped_resource;
+    ASSERT_TRUE(SUCCEEDED(context->Map(staging_texture.Get(), 0, D3D11_MAP_READ,
+                                       0, &mapped_resource)));
+    ASSERT_GE(mapped_resource.RowPitch, 4u);
+    // After present, front buffer should have color rendered to back buffer.
+    const uint8_t* pixel_color =
+        static_cast<const uint8_t*>(mapped_resource.pData);
+    const uint8_t expected_color[4] = {0, 255, 0, 255};
+    EXPECT_EQ(expected_color[0], pixel_color[0]);
+    EXPECT_EQ(expected_color[1], pixel_color[1]);
+    EXPECT_EQ(expected_color[2], pixel_color[2]);
+    EXPECT_EQ(expected_color[3], pixel_color[3]);
+
+    context->Unmap(staging_texture.Get(), 0);
+  }
+
   api->glDeleteFramebuffersEXTFn(1, &fbo);
 }
 
diff --git a/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc b/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
index 67100fe..931ab3c 100644
--- a/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
+++ b/gpu/command_buffer/tests/command_buffer_gles2_tests_main.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
+#include "base/task/single_thread_task_executor.h"
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
 #endif
@@ -19,9 +19,9 @@
 
 int RunHelper(base::TestSuite* testSuite) {
 #if defined(USE_OZONE)
-  base::MessageLoopForUI main_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::UI);
 #else
-  base::MessageLoopForIO message_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::IO);
 #endif
   return testSuite->Run();
 }
diff --git a/gpu/command_buffer/tests/gl_tests_main.cc b/gpu/command_buffer/tests/gl_tests_main.cc
index 7c3b1c5c..d3e5028 100644
--- a/gpu/command_buffer/tests/gl_tests_main.cc
+++ b/gpu/command_buffer/tests/gl_tests_main.cc
@@ -6,7 +6,8 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump.h"
+#include "base/task/single_thread_task_executor.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
@@ -24,9 +25,9 @@
 int RunHelper(base::TestSuite* testSuite) {
   base::FeatureList::InitializeInstance(std::string(), std::string());
 #if defined(USE_OZONE)
-  base::MessageLoopForUI main_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::UI);
 #else
-  base::MessageLoopForIO message_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::IO);
 #endif
   gpu::GLTestHelper::InitializeGLDefault();
 
diff --git a/gpu/perftests/run_all_tests.cc b/gpu/perftests/run_all_tests.cc
index 5c6586c..c96f59e 100644
--- a/gpu/perftests/run_all_tests.cc
+++ b/gpu/perftests/run_all_tests.cc
@@ -5,7 +5,8 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump.h"
+#include "base/task/single_thread_task_executor.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
@@ -18,12 +19,12 @@
 static int RunHelper(base::TestSuite* test_suite) {
   base::FeatureList::InitializeInstance(std::string(), std::string());
 #if defined(USE_OZONE)
-  base::MessageLoopForUI main_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::UI);
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
 #else
-  base::MessageLoopForIO message_loop;
+  base::SingleThreadTaskExecutor executor(base::MessagePump::Type::IO);
 #endif
   CHECK(gl::init::InitializeGLOneOff());
   return test_suite->Run();
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 7a27466..3d517cb 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -2946,11 +2946,6 @@
       mixins: "builderless"
     }
     builders {
-      name: "chromeos-amd64-generic-rel-goma-canary"
-      dimensions: "os:Ubuntu-14.04"
-      mixins: "fyi-ci"
-    }
-    builders {
       name: "Linux MSan Builder"
       mixins: "memory-ci-goma-rbe-prod"
       mixins: "linux-xenial"
@@ -3265,45 +3260,45 @@
     # Goma RBE FYI
     builders {
       name: "Cast Linux (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
     }
     builders {
       name: "chromeos-amd64-generic-rel (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-ats"
+      mixins: "linux-xenial"
     }
     builders {
       name: "fuchsia-fyi-arm64-rel (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
     }
     builders {
       name: "fuchsia-fyi-x64-rel (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
     }
     builders {
       name: "Linux ASan LSan Builder (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
     }
     builders {
       name: "Linux MSan Builder (Goma RBE FYI)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "builderless"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod-ats"
+      mixins: "linux-xenial"
     }
     # Goma Staging
     builders {
@@ -3342,9 +3337,16 @@
       mixins: "fyi-ci"
     }
     builders {
-      name: "Android Builder (dbg) Goma Canary"
-      dimensions: "os:Ubuntu-14.04"
+      name: "chromeos-amd64-generic-rel-goma-canary"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+    }
+    builders {
+      name: "Android Builder (dbg) Goma Canary"
+      mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Mac Goma Canary LocalOutputCache"
@@ -3370,13 +3372,17 @@
     }
     builders {
       name: "Linux x64 Goma Canary LocalOutputCache"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "builderless"
+      mixins: "linux-xenial"
     }
     builders {
       name: "Linux Builder Goma Canary"
+      # keep to use trusty for this until chrome drops support of development
+      # on trusty.
       dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "builderless"
     }
     builders {
       name: "Win cl.exe Goma Canary LocalOutputCache"
@@ -3406,8 +3412,9 @@
     }
     builders {
       name: "Linux x64 Goma Canary (clobber)"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     # Goma Latest Client
     builders {
diff --git a/ios/build/bots/scripts/xcode_log_parser.py b/ios/build/bots/scripts/xcode_log_parser.py
index 1eb77d8f..442281e 100644
--- a/ios/build/bots/scripts/xcode_log_parser.py
+++ b/ios/build/bots/scripts/xcode_log_parser.py
@@ -30,7 +30,8 @@
   passed_tests = []
   # Test has format:
   # [09:04:42:INFO] Test case '-[Test_class test_method]' passed.
-  passed_test_regex = re.compile(r'Test case \'\-\[(.+?)\s(.+?)\]\' passed')
+  # [09:04:42:INFO] Test Case '-[Test_class test_method]' passed.
+  passed_test_regex = re.compile(r'Test [Cc]ase \'\-\[(.+?)\s(.+?)\]\' passed')
 
   for test_line in output:
     m_test = passed_test_regex.search(test_line)
diff --git a/ios/build/bots/scripts/xcode_log_parser_test.py b/ios/build/bots/scripts/xcode_log_parser_test.py
index bc5bb4cb..cd37b17 100644
--- a/ios/build/bots/scripts/xcode_log_parser_test.py
+++ b/ios/build/bots/scripts/xcode_log_parser_test.py
@@ -256,7 +256,7 @@
     mock_path_exists.side_effect = [True, False]
     output = [
         '[09:03:42:INFO] Test case \'-[TestCase1 method1]\' passed on device.',
-        '[09:06:40:INFO] Test case \'-[TestCase2 method1]\' passed on device.',
+        '[09:06:40:INFO] Test Case \'-[TestCase2 method1]\' passed on device.',
         '[09:09:00:INFO] Test case \'-[TestCase2 method1]\' failed on device.',
         '** BUILD INTERRUPTED **',
     ]
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.h b/ios/chrome/app/application_delegate/mock_tab_opener.h
index fe7bc7b1..85f0173 100644
--- a/ios/chrome/app/application_delegate/mock_tab_opener.h
+++ b/ios/chrome/app/application_delegate/mock_tab_opener.h
@@ -19,7 +19,7 @@
 // -dismissModalsAndOpenSelectedTabInMode:withUrlLoadParams:dismissOmnibox:
 //  completion:.
 @property(nonatomic, readonly) UrlLoadParams urlLoadParams;
-@property(nonatomic, readonly) ApplicationMode applicationMode;
+@property(nonatomic, readonly) ApplicationModeForTabOpening applicationMode;
 @property(nonatomic, strong, readonly) void (^completionBlock)(void);
 
 // Clear the URL.
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.mm b/ios/chrome/app/application_delegate/mock_tab_opener.mm
index d8697b5..21121972 100644
--- a/ios/chrome/app/application_delegate/mock_tab_opener.mm
+++ b/ios/chrome/app/application_delegate/mock_tab_opener.mm
@@ -17,7 +17,8 @@
 
 @implementation MockTabOpener
 
-- (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode
+- (void)dismissModalsAndOpenSelectedTabInMode:
+            (ApplicationModeForTabOpening)targetMode
                             withUrlLoadParams:
                                 (const UrlLoadParams&)urlLoadParams
                                dismissOmnibox:(BOOL)dismissOmnibox
diff --git a/ios/chrome/app/application_delegate/tab_opening.h b/ios/chrome/app/application_delegate/tab_opening.h
index 96e85829..cb44995e 100644
--- a/ios/chrome/app/application_delegate/tab_opening.h
+++ b/ios/chrome/app/application_delegate/tab_opening.h
@@ -15,6 +15,8 @@
 @protocol StartupInformation;
 struct UrlLoadParams;
 
+enum class ApplicationModeForTabOpening { NORMAL, INCOGNITO, CURRENT };
+
 // Protocol for object that can open new tabs during application launch.
 @protocol TabOpening<NSObject>
 
@@ -22,7 +24,8 @@
 // then opens either a normal or incognito tab with |url|. After opening |url|,
 // run completion |handler| if it is not nil. After Tab is opened the virtual
 // URL is set to the pending navigation item.
-- (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode
+- (void)dismissModalsAndOpenSelectedTabInMode:
+            (ApplicationModeForTabOpening)targetMode
                             withUrlLoadParams:
                                 (const UrlLoadParams&)urlLoadParams
                                dismissOmnibox:(BOOL)dismissOmnibox
diff --git a/ios/chrome/app/application_delegate/url_opener.mm b/ios/chrome/app/application_delegate/url_opener.mm
index 6209b46..ca5f2d40 100644
--- a/ios/chrome/app/application_delegate/url_opener.mm
+++ b/ios/chrome/app/application_delegate/url_opener.mm
@@ -80,10 +80,17 @@
         URL = [params externalURL];
       }
       UrlLoadParams urlLoadParams = UrlLoadParams::InNewTab(URL, virtualURL);
+
+      ApplicationModeForTabOpening targetMode =
+          [params launchInIncognito] ? ApplicationModeForTabOpening::INCOGNITO
+                                     : ApplicationModeForTabOpening::NORMAL;
+      // If the call is coming from the app, it should be opened in the current
+      // mode to avoid changing mode.
+      if (callerApp == CALLER_APP_GOOGLE_CHROME)
+        targetMode = ApplicationModeForTabOpening::CURRENT;
+
       [tabOpener
-          dismissModalsAndOpenSelectedTabInMode:[params launchInIncognito]
-                                                    ? ApplicationMode::INCOGNITO
-                                                    : ApplicationMode::NORMAL
+          dismissModalsAndOpenSelectedTabInMode:targetMode
                               withUrlLoadParams:urlLoadParams
                                  dismissOmnibox:[params postOpeningAction] !=
                                                 FOCUS_OMNIBOX
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm
index f7b45d0..d7d33fa4 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -172,10 +172,10 @@
   if (applicationIsActive && ![startupInformation isPresentingFirstRunUI]) {
     // The app is already active so the applicationDidBecomeActive: method will
     // never be called. Open the requested URL immediately.
-    ApplicationMode targetMode =
+    ApplicationModeForTabOpening targetMode =
         [[startupInformation startupParameters] launchInIncognito]
-            ? ApplicationMode::INCOGNITO
-            : ApplicationMode::NORMAL;
+            ? ApplicationModeForTabOpening::INCOGNITO
+            : ApplicationModeForTabOpening::NORMAL;
     UrlLoadParams params = UrlLoadParams::InNewTab(webpageGURL);
     [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode
                                    withUrlLoadParams:params
@@ -262,10 +262,10 @@
     // will never be called. Open the requested URL after all modal UIs have
     // been dismissed. |_startupParameters| must be retained until all deferred
     // modal UIs are dismissed and tab opened with requested URL.
-    ApplicationMode targetMode =
+    ApplicationModeForTabOpening targetMode =
         [[startupInformation startupParameters] launchInIncognito]
-            ? ApplicationMode::INCOGNITO
-            : ApplicationMode::NORMAL;
+            ? ApplicationModeForTabOpening::INCOGNITO
+            : ApplicationModeForTabOpening::NORMAL;
     GURL URL;
     GURL virtualURL;
     GURL completeURL = startupInformation.startupParameters.completeURL;
diff --git a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
index c087b51..04f8229 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
@@ -385,7 +385,7 @@
   GURL newTabURL(kChromeUINewTabURL);
   EXPECT_EQ(newTabURL, tabOpener.urlLoadParams.web_params.url);
   // AppStartupParameters default to opening pages in non-Incognito mode.
-  EXPECT_EQ(ApplicationMode::NORMAL, [tabOpener applicationMode]);
+  EXPECT_EQ(ApplicationModeForTabOpening::NORMAL, [tabOpener applicationMode]);
   EXPECT_TRUE(result);
   // Verifies that a new tab is being requested.
   EXPECT_EQ(newTabURL,
@@ -493,7 +493,8 @@
   // and omnibox shows virtual URL.
   EXPECT_EQ(completeURL, tabOpener.urlLoadParams.web_params.url);
   EXPECT_EQ(externalURL, tabOpener.urlLoadParams.web_params.virtual_url);
-  EXPECT_EQ(ApplicationMode::INCOGNITO, [tabOpener applicationMode]);
+  EXPECT_EQ(ApplicationModeForTabOpening::INCOGNITO,
+            [tabOpener applicationMode]);
 }
 
 // Tests that handleStartupParameters with a non-U2F url opens a new tab.
@@ -528,7 +529,8 @@
   EXPECT_OCMOCK_VERIFY(startupInformationMock);
   EXPECT_EQ(gurl, tabOpener.urlLoadParams.web_params.url);
   EXPECT_TRUE(tabOpener.urlLoadParams.web_params.virtual_url.is_empty());
-  EXPECT_EQ(ApplicationMode::INCOGNITO, [tabOpener applicationMode]);
+  EXPECT_EQ(ApplicationModeForTabOpening::INCOGNITO,
+            [tabOpener applicationMode]);
 }
 
 // Tests that handleStartupParameters with a U2F url opens in the correct tab.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 407ae1a..4ffa5a5 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -461,7 +461,7 @@
 // If the current tab in |targetMode| is a NTP, it can be reused to open URL.
 // |completion| is executed after the tab is opened. After Tab is open the
 // virtual URL is set to the pending navigation item.
-- (void)openSelectedTabInMode:(ApplicationMode)targetMode
+- (void)openSelectedTabInMode:(ApplicationModeForTabOpening)targetMode
             withUrlLoadParams:(const UrlLoadParams&)urlLoadParams
                    completion:(ProceduralBlock)completion;
 // Checks the target BVC's current tab's URL. If this URL is chrome://newtab,
@@ -838,7 +838,8 @@
   if (_startupParameters) {
     UrlLoadParams params =
         UrlLoadParams::InNewTab(_startupParameters.externalURL);
-    [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL
+    [self dismissModalsAndOpenSelectedTabInMode:ApplicationModeForTabOpening::
+                                                    NORMAL
                               withUrlLoadParams:params
                                  dismissOmnibox:YES
                                      completion:^{
@@ -1838,7 +1839,8 @@
   UrlLoadParams params = UrlLoadParams::InNewTab([command URL]);
   params.web_params.transition_type = ui::PAGE_TRANSITION_TYPED;
   ProceduralBlock completion = ^{
-    [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL
+    [self dismissModalsAndOpenSelectedTabInMode:ApplicationModeForTabOpening::
+                                                    NORMAL
                               withUrlLoadParams:params
                                  dismissOmnibox:YES
                                      completion:nil];
@@ -2351,9 +2353,21 @@
   }
 }
 
-- (void)openSelectedTabInMode:(ApplicationMode)targetMode
+- (void)openSelectedTabInMode:(ApplicationModeForTabOpening)tabOpeningTargetMode
             withUrlLoadParams:(const UrlLoadParams&)urlLoadParams
                    completion:(ProceduralBlock)completion {
+  ApplicationMode targetMode;
+
+  if (tabOpeningTargetMode == ApplicationModeForTabOpening::CURRENT) {
+    targetMode = self.interfaceProvider.currentInterface.incognito
+                     ? ApplicationMode::INCOGNITO
+                     : ApplicationMode::NORMAL;
+  } else if (tabOpeningTargetMode == ApplicationModeForTabOpening::NORMAL) {
+    targetMode = ApplicationMode::NORMAL;
+  } else {
+    targetMode = ApplicationMode::INCOGNITO;
+  }
+
   id<BrowserInterface> targetInterface =
       targetMode == ApplicationMode::NORMAL
           ? self.interfaceProvider.mainInterface
@@ -2532,7 +2546,8 @@
 
 #pragma mark - TabOpening implementation.
 
-- (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode
+- (void)dismissModalsAndOpenSelectedTabInMode:
+            (ApplicationModeForTabOpening)targetMode
                             withUrlLoadParams:
                                 (const UrlLoadParams&)urlLoadParams
                                dismissOmnibox:(BOOL)dismissOmnibox
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.h b/ios/chrome/app/startup/chrome_app_startup_parameters.h
index 3b5cb04..aad5aca0 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.h
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.h
@@ -28,6 +28,7 @@
   CALLER_APP_GOOGLE_CHROME_SEARCH_EXTENSION,
   CALLER_APP_GOOGLE_CHROME_CONTENT_EXTENSION,
   CALLER_APP_GOOGLE_CHROME_SHARE_EXTENSION,
+  CALLER_APP_GOOGLE_CHROME,
   MOBILE_SESSION_CALLER_APP_COUNT,
 };
 
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
index c0c0372..7fbc9c4 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -389,6 +389,9 @@
 
   if (![_declaredSourceApp length])
     return CALLER_APP_NOT_AVAILABLE;
+  if ([_declaredSourceApp
+          isEqualToString:[[NSBundle mainBundle] bundleIdentifier]])
+    return CALLER_APP_GOOGLE_CHROME;
   if ([_declaredSourceApp isEqualToString:@"com.google.GoogleMobile"])
     return CALLER_APP_GOOGLE_SEARCH;
   if ([_declaredSourceApp isEqualToString:@"com.google.Gmail"])
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 8cf184a1..ca53b74 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -68,6 +68,7 @@
   deps = [
     ":settings_resources",
     "//base",
+    "//build:branding_buildflags",
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//components/autofill/ios/browser",
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc b/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
index 9dda9484..79f25d2 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
@@ -69,9 +69,6 @@
   return base.Append(kIOSChromeCacheDirname);
 }
 
-const base::FilePath::CharType kIOSChromeChannelIDFilename[] =
-    FILE_PATH_LITERAL("Origin Bound Certs");
-
 }  // namespace
 
 ChromeBrowserStateImpl::ChromeBrowserStateImpl(
@@ -116,14 +113,6 @@
   base::FilePath cache_path = GetCachePath(base_cache_path);
   int cache_max_size = 0;
 
-  // TODO(crbug.com/903642): Remove the following when no longer needed.
-  base::FilePath channel_id_path =
-      state_path_.Append(kIOSChromeChannelIDFilename);
-  base::DeleteFile(channel_id_path, false);
-  base::DeleteFile(
-      base::FilePath(channel_id_path.value() + FILE_PATH_LITERAL("-journal")),
-      false);
-
   // Make sure we initialize the io_data_ after everything else has been
   // initialized that we might be reading from the IO thread.
   io_data_->Init(cookie_path, cache_path, cache_max_size, state_path_);
diff --git a/ios/chrome/browser/memory/BUILD.gn b/ios/chrome/browser/memory/BUILD.gn
index e11599c..ee0d8a2 100644
--- a/ios/chrome/browser/memory/BUILD.gn
+++ b/ios/chrome/browser/memory/BUILD.gn
@@ -14,6 +14,7 @@
   ]
   deps = [
     "//base",
+    "//build:branding_buildflags",
     "//components/prefs",
     "//ios/chrome/browser",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/memory/memory_debugger.mm b/ios/chrome/browser/memory/memory_debugger.mm
index ac46264..fb52dff 100644
--- a/ios/chrome/browser/memory/memory_debugger.mm
+++ b/ios/chrome/browser/memory/memory_debugger.mm
@@ -8,6 +8,7 @@
 
 #include <memory>
 
+#import "build/branding_buildflags.h"
 #import "ios/chrome/browser/memory/memory_metrics.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
@@ -116,7 +117,7 @@
 // official builds.
 // TODO(lliabraa): Figure out how to support memory warnings (or something
 // like them) in official builds.
-#if CHROMIUM_BUILD
+#if BUILDFLAG(CHROMIUM_BRANDING)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wundeclared-selector"
   [self addButtonWithTitle:@"Trigger Memory Warning"
@@ -124,7 +125,7 @@
                     action:@selector(_performMemoryWarning)
                 withOrigin:[self originForSubviewAtIndex:index++]];
 #pragma clang diagnostic pop
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
 
   // Display a text input to set the amount of artificial memory bloat and a
   // button to reset the bloat to zero.
@@ -144,7 +145,7 @@
 // official builds.
 // TODO(lliabraa): Figure out how to support memory warnings (or something
 // like them) in official builds.
-#if CHROMIUM_BUILD
+#if BUILDFLAG(CHROMIUM_BRANDING)
   // Display a text input to control the rate of continuous memory warnings.
   _continuousMemoryWarningField =
       [[UITextField alloc] initWithFrame:CGRectZero];
@@ -154,7 +155,7 @@
              inputAction:@selector(updateMemoryWarningInterval)
                  atIndex:index++];
   [_continuousMemoryWarningField setText:@"0.0"];
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
 
   // Display a text input to control the refresh rate of the memory debugger.
   _refreshField = [[UITextField alloc] initWithFrame:CGRectZero];
@@ -475,7 +476,7 @@
 // official builds.
 // TODO(lliabraa): Figure out how to support memory warnings (or something
 // like them) in official builds.
-#if CHROMIUM_BUILD
+#if BUILDFLAG(CHROMIUM_BRANDING)
 - (void)updateMemoryWarningInterval {
   [_memoryWarningTimer invalidate];
   double timerValue;
@@ -511,7 +512,7 @@
                                       repeats:YES];
 #pragma clang diagnostic push
 }
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
 
 #pragma mark UITextViewDelegate methods
 
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
index c7a495c..bc64993d 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/system/sys_info.h"
 #include "base/version.h"
 #include "components/metrics/metrics_service.h"
 #include "components/version_info/version_info.h"
@@ -70,7 +71,7 @@
 // Logs the device's |available_storage| as a UTE stability metric.
 void LogAvailableStorage(NSInteger available_storage) {
   UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS("Stability.iOS.UTE.AvailableStorage",
-                                        available_storage, 1, 100000, 100);
+                                        available_storage, 1, 200000, 100);
 }
 
 // Logs the OS version change between |os_version| and the current os version.
@@ -79,7 +80,7 @@
 void LogOSVersionChange(std::string os_version) {
   base::Version previous_os_version = base::Version(os_version);
   base::Version current_os_version =
-      base::Version(version_info::GetVersionNumber());
+      base::Version(base::SysInfo::OperatingSystemVersion());
 
   VersionComparison difference = VersionComparison::kSameVersion;
   if (previous_os_version.CompareTo(current_os_version) != 0) {
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index d6559435..30b29552 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -198,4 +198,11 @@
 
   // Added 07/2019.
   syncer::MigrateSyncSuppressedPref(prefs);
+  syncer::ClearObsoleteMemoryPressurePrefs(prefs);
+  syncer::MigrateSessionsToProxyTabsPrefs(prefs);
+  syncer::ClearObsoleteUserTypePrefs(prefs);
+  syncer::ClearObsoleteClearServerDataPrefs(prefs);
+  syncer::ClearObsoleteAuthErrorPrefs(prefs);
+  syncer::ClearObsoleteFirstSyncTime(prefs);
+  syncer::ClearObsoleteSyncLongPollIntervalSeconds(prefs);
 }
diff --git a/ios/chrome/browser/system_flags.mm b/ios/chrome/browser/system_flags.mm
index 7bb4d91e..52edc3d5 100644
--- a/ios/chrome/browser/system_flags.mm
+++ b/ios/chrome/browser/system_flags.mm
@@ -18,6 +18,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "build/branding_buildflags.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/variations/variations_associated_data.h"
@@ -84,12 +85,12 @@
 bool IsMemoryDebuggingEnabled() {
 // Always return true for Chromium builds, but check the user default for
 // official builds because memory debugging should never be enabled on stable.
-#if CHROMIUM_BUILD
+#if BUILDFLAG(CHROMIUM_BRANDING)
   return true;
 #else
   return [[NSUserDefaults standardUserDefaults]
       boolForKey:@"EnableMemoryDebugging"];
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
 }
 
 bool IsStartupCrashEnabled() {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_observer_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/keyboard_observer_egtest.mm
index 064d50f..677a748f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_observer_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/keyboard_observer_egtest.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #import <EarlGrey/EarlGrey.h>
+#import <EarlGrey/GREYKeyboard.h>
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -75,6 +76,9 @@
 // Observer to be tested.
 @property(nonatomic, strong) KeyboardObserverHelper* keyboardObserver;
 
+// Token to register a NSNotificationCenter observer.
+@property(nonatomic, strong) id<NSObject> notificationToken;
+
 // Delegate mock to confirm the observer callbacks.
 @property(nonatomic, strong)
     OCMockObject<KeyboardObserverHelperConsumer>* keyboardObserverDelegateMock;
@@ -103,6 +107,7 @@
 }
 
 - (void)tearDown {
+  self.notificationToken = nil;
   self.keyboardObserverDelegateMock = nil;
   self.keyboardObserver = nil;
 
@@ -118,32 +123,44 @@
   // Brings up the keyboard by tapping on one of the form's field.
   TapOnWebElementWithID(kFormElementID1);
 
+  // Wait for keyboard to finish animating.
+  __block BOOL keyboardDidAppear = NO;
+  self.notificationToken = [[NSNotificationCenter defaultCenter]
+      addObserverForName:UIKeyboardDidShowNotification
+                  object:nil
+                   queue:nil
+              usingBlock:^(NSNotification* note) {
+                keyboardDidAppear = YES;
+              }];
+  ConditionBlock condition = ^{
+    return keyboardDidAppear;
+  };
+  using base::test::ios::WaitUntilConditionOrTimeout;
+  using base::test::ios::kWaitForUIElementTimeout;
+  GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
+             @"Wait for keyboard did show notification");
+
   // Verifies that the taped element is focused.
   AssertElementIsFocused(kFormElementID1);
 
-  // Create the callback expectation.
+  // Create a new callback expectation.
   OCMExpect([self.keyboardObserverDelegateMock keyboardDidStayOnScreen]);
 
+  // Reset our keyboard boolean.
+  keyboardDidAppear = NO;
+
   // Tap the second field.
   TapOnWebElementWithID(kFormElementID2);
 
+  // Wait for keyboard to finish animating.
+  GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
+             @"Wait for keyboard did show notification");
+
   // Verifies that the taped element is focused.
   AssertElementIsFocused(kFormElementID2);
 
   // Verify the delegate call was made.
   [self.keyboardObserverDelegateMock verify];
-
-  // Add another callback expectation.
-  OCMExpect([self.keyboardObserverDelegateMock keyboardDidStayOnScreen]);
-
-  // Tap the first field.
-  TapOnWebElementWithID(kFormElementID1);
-
-  // Verifies that the taped element is focused.
-  AssertElementIsFocused(kFormElementID1);
-
-  // Verify the delegate call was made.
-  [self.keyboardObserverDelegateMock verify];
 }
 
 // Tests that when the keyboard actually dismiss the right callback is done.
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 bb45c3e..ee44ebdb 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -808,10 +808,7 @@
     _downloadManagerCoordinator.presenter =
         [[VerticalAnimationContainer alloc] init];
 
-    if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
-      _javaScriptDialogPresenter =
-          std::make_unique<OverlayJavaScriptDialogPresenter>();
-    } else {
+    if (!base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
       _dialogPresenter = [[DialogPresenter alloc] initWithDelegate:self
                                           presentingViewController:self];
       _javaScriptDialogPresenter =
@@ -3393,6 +3390,11 @@
 
 - (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
     (web::WebState*)webState {
+  if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
+    return WebStateDelegateTabHelper::FromWebState(webState)
+        ->GetJavaScriptDialogPresenter(webState);
+  }
+  DCHECK(_javaScriptDialogPresenter.get());
   return _javaScriptDialogPresenter.get();
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index 41e3d027..e53ae4f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -216,6 +216,12 @@
   self.view = [[ContentSuggestionsHeaderView alloc] init];
 }
 
+- (void)viewDidAppear:(BOOL)animated {
+  [super viewDidAppear:animated];
+  UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
+                                  self.fakeOmnibox);
+}
+
 - (CGFloat)headerHeight {
   return content_suggestions::heightForLogoHeader(
       self.logoIsShowing, self.promoCanShow, YES, [self topInset]);
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index c298336a..40f19bd 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -54,6 +54,7 @@
     "resources:omnibox_completion_default_favicon",
     "resources:omnibox_completion_history",
     "resources:omnibox_completion_search",
+    "resources:omnibox_popup_recent_query",
     "resources:search",
     "//base",
   ]
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
index 67c9d9e..2f693108 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h
@@ -14,6 +14,7 @@
   DEFAULT_FAVICON,
   HISTORY,
   SEARCH,
+  SEARCH_HISTORY,
   CONVERSION,
   DICTIONARY,
   STOCK,
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
index 2daf584..1d737bd 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.mm
@@ -49,6 +49,7 @@
     case FALLBACK_ANSWER:
       NOTREACHED();
       return @"omnibox_completion_default_favicon";
+    case SEARCH_HISTORY:
     case OMNIBOX_SUGGESTION_ICON_TYPE_COUNT:
       NOTREACHED();
       return @"omnibox_completion_default_favicon";
@@ -84,6 +85,8 @@
       return @"answer_translation";
     case FALLBACK_ANSWER:
       return @"search";
+    case SEARCH_HISTORY:
+      return @"omnibox_popup_recent_query";
     case OMNIBOX_SUGGESTION_ICON_TYPE_COUNT:
       NOTREACHED();
       return @"favicon_fallback";
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
index 8885915a..eb32628 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
@@ -55,8 +55,9 @@
     case AutocompleteMatchType::CLIPBOARD_IMAGE:
       return SEARCH;
     case AutocompleteMatchType::SEARCH_HISTORY:
-      return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout) ? SEARCH
-                                                                  : HISTORY;
+      return base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)
+                 ? SEARCH_HISTORY
+                 : HISTORY;
     case AutocompleteMatchType::CALCULATOR:
       return CALCULATOR;
     case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
index 96531373..f72c7d79 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
@@ -64,7 +64,6 @@
     case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
       return DEFAULT_FAVICON;
     case AutocompleteMatchType::CONTACT_DEPRECATED:
-    case AutocompleteMatchType::SEARCH_HISTORY:
     case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
     case AutocompleteMatchType::SEARCH_SUGGEST:
     case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
@@ -76,6 +75,8 @@
     case AutocompleteMatchType::CLIPBOARD_TEXT:
     case AutocompleteMatchType::CLIPBOARD_IMAGE:
       return SEARCH;
+    case AutocompleteMatchType::SEARCH_HISTORY:
+      return HISTORY;
     case AutocompleteMatchType::CALCULATOR:
       return CALCULATOR;
     case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
diff --git a/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm b/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
index 31262c7..7e6e2a4 100644
--- a/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.mm
@@ -62,6 +62,7 @@
     case DEFAULT_FAVICON:
     case HISTORY:
     case SEARCH:
+    case SEARCH_HISTORY:
       return NO;
     case CALCULATOR:
     case CONVERSION:
diff --git a/ios/chrome/browser/ui/omnibox/resources/BUILD.gn b/ios/chrome/browser/ui/omnibox/resources/BUILD.gn
index 43c652a..2301d851 100644
--- a/ios/chrome/browser/ui/omnibox/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/resources/BUILD.gn
@@ -170,3 +170,11 @@
     "background_stroke.imageset/background_stroke@3x.png",
   ]
 }
+
+imageset("omnibox_popup_recent_query") {
+  sources = [
+    "omnibox_popup_recent_query.imageset/Contents.json",
+    "omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@2x.png",
+    "omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/Contents.json b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/Contents.json
new file mode 100644
index 0000000..2eb7869
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "omnibox_popup_recent_query@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "omnibox_popup_recent_query@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@2x.png b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@2x.png
new file mode 100644
index 0000000..34852fb
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@3x.png b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@3x.png
new file mode 100644
index 0000000..7f4c791
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/resources/omnibox_popup_recent_query.imageset/omnibox_popup_recent_query@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/open_in/open_in_mediator.mm b/ios/chrome/browser/ui/open_in/open_in_mediator.mm
index 8ebf649..ef70af6e 100644
--- a/ios/chrome/browser/ui/open_in/open_in_mediator.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_mediator.mm
@@ -37,6 +37,11 @@
   self = [super init];
   if (self) {
     _webStateList = webStateList;
+    // Set the delegates for all existing webstates in the |_webStateList|.
+    for (int i = 0; i < _webStateList->count(); i++) {
+      web::WebState* webState = _webStateList->GetWebStateAt(i);
+      OpenInTabHelper::FromWebState(webState)->SetDelegate(self);
+    }
     _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
     _webStateList->AddObserver(_webStateListObserver.get());
   }
@@ -69,7 +74,6 @@
               atIndex:(int)index
            activating:(BOOL)activating {
   DCHECK_EQ(_webStateList, webStateList);
-
   OpenInTabHelper::FromWebState(webState)->SetDelegate(self);
 }
 
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
index f9d5403..e643beaa 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
@@ -584,6 +584,7 @@
       self.selectedUnreadItemCount, self.selectedReadItemCount);
   if (self.toolbarManager.buttonItemsUpdated)
     [self setToolbarItems:[self.toolbarManager buttonItems] animated:YES];
+  [self.toolbarManager updateMarkButtonTitle];
 }
 
 #pragma mark - Item Editing Helpers
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.h b/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.h
index cda7abd5..68366052d 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.h
@@ -40,6 +40,11 @@
 // to |self.commandHandler|.
 - (NSArray<UIBarButtonItem*>*)buttonItems;
 
+// Updates the title of the mark button based on the selection state. This
+// method isn't part of the update of the selection state to avoid updating it
+// too soon and messing with VoiceOver. See https://crbug.com/985744 .
+- (void)updateMarkButtonTitle;
+
 // Returns an empty ActionSheetCoordinator anchored to the mark button with no
 // message and no title.
 - (ActionSheetCoordinator*)markButtonConfirmationWithBaseViewController:
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.mm b/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.mm
index a9c4e3a..d18cca1 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.mm
@@ -116,7 +116,6 @@
     return;
   BOOL hadSelectedItems = _selectionState != ReadingListSelectionState::NONE;
   _selectionState = selectionState;
-  _markButton.title = GetMarkButtonTitleForSelectionState(_selectionState);
   // Check whether selection status has changed to or from NONE.
   if ((_selectionState != ReadingListSelectionState::NONE) != hadSelectedItems)
     _buttonItems = nil;
@@ -144,6 +143,12 @@
     self.selectionState = ReadingListSelectionState::NONE;
 }
 
+- (void)updateMarkButtonTitle {
+  if (!_editing)
+    return;
+  _markButton.title = GetMarkButtonTitleForSelectionState(_selectionState);
+}
+
 - (BOOL)buttonItemsUpdated {
   // When the changes to the values of this class's public properties require
   // an updated buttons array, |_buttonItems| will be reset to nil.  Subsequent
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index d0f90ada..1c386ca 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -97,6 +97,7 @@
     "resources:sync_and_google_services_sync_on",
     "//base",
     "//base:i18n",
+    "//build:branding_buildflags",
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//components/browsing_data/core",
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 752bf6ef..de48c09f 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -9,6 +9,7 @@
 #include "base/feature_list.h"
 #import "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "build/branding_buildflags.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/password_manager/core/browser/password_store.h"
@@ -150,9 +151,9 @@
   ItemTypeArticlesForYou,
 };
 
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 NSString* kDevViewSourceKey = @"DevViewSource";
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 
 }  // namespace
 
@@ -435,14 +436,14 @@
         toSectionWithIdentifier:SectionIdentifierDebug];
   }
 
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
   [model addItem:[self viewSourceSwitchItem]
       toSectionWithIdentifier:SectionIdentifierDebug];
   [model addItem:[self collectionViewCatalogDetailItem]
       toSectionWithIdentifier:SectionIdentifierDebug];
   [model addItem:[self tableViewCatalogDetailItem]
       toSectionWithIdentifier:SectionIdentifierDebug];
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 }
 
 #pragma mark - Model Items
@@ -645,7 +646,7 @@
 
   return articlesForYouSwitchItem;
 }
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 
 - (SettingsSwitchItem*)viewSourceSwitchItem {
   return [self switchItemWithType:ItemTypeViewSource
@@ -667,7 +668,7 @@
                        detailText:nil
                     iconImageName:kSettingsDebugImageName];
 }
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 
 #pragma mark Item Constructors
 
@@ -751,7 +752,7 @@
       break;
     }
     case ItemTypeViewSource: {
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
       SettingsSwitchCell* switchCell =
           base::mac::ObjCCastStrict<SettingsSwitchCell>(cell);
       [switchCell.switchView addTarget:self
@@ -759,7 +760,7 @@
                       forControlEvents:UIControlEventValueChanged];
 #else
       NOTREACHED();
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
       break;
     }
     default:
@@ -901,7 +902,7 @@
   [_articlesEnabled setValue:newSwitchValue];
 }
 
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 - (void)viewSourceSwitchToggled:(UISwitch*)sender {
   NSIndexPath* switchPath =
       [self.tableViewModel indexPathForItemType:ItemTypeViewSource
@@ -915,7 +916,7 @@
   switchItem.on = newSwitchValue;
   [self setBooleanNSUserDefaultsValue:newSwitchValue forKey:kDevViewSourceKey];
 }
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 
 #pragma mark Private methods
 
@@ -944,14 +945,14 @@
 // Chromium builds, but for official builds it is gated by an experimental flag
 // because the "Debug" section should never be showing in stable channel.
 - (BOOL)hasDebugSection {
-#if CHROMIUM_BUILD && !defined(NDEBUG)
+#if BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
   return YES;
 #else
   if (experimental_flags::IsMemoryDebuggingEnabled()) {
     return YES;
   }
   return NO;
-#endif  // CHROMIUM_BUILD && !defined(NDEBUG)
+#endif  // BUILDFLAG(CHROMIUM_BRANDING) && !defined(NDEBUG)
 }
 
 // Updates the identity cell.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.h b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.h
index 5427496..616e7ca 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.h
@@ -20,10 +20,10 @@
 //   Remote page:              [                             trailingButton]
 //
 // Other screen size:
-//   Large newTabButton, transparent background.
+//   Large newTabButton, floating layout without UIToolbar.
 //   Incognito & Regular page: [                               newTabButton]
 //   Remote page:              [                                           ]
-@interface TabGridBottomToolbar : UIToolbar
+@interface TabGridBottomToolbar : UIView
 // This property together with self.traitCollection control the items shown
 // in toolbar and its background color. Setting this property will also set it
 // on |newTabButton|.
@@ -32,11 +32,9 @@
 // contents, visibility and actions.
 @property(nonatomic, strong, readonly) UIBarButtonItem* leadingButton;
 @property(nonatomic, strong, readonly) UIBarButtonItem* trailingButton;
-// Clang does not allow property getters to start with the reserved word "new",
-// but provides a workaround. The getter must be set before the property is
-// declared.
-- (TabGridNewTabButton*)newTabButton __attribute__((objc_method_family(none)));
-@property(nonatomic, strong, readonly) TabGridNewTabButton* newTabButton;
+
+// Sets target/action for tapping event on new tab button.
+- (void)setNewTabButtonTarget:(id)target action:(SEL)action;
 
 // Hides components and uses a black background color for tab grid transition
 // animation.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
index 1200936..86f0239 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
@@ -13,33 +13,17 @@
 #endif
 
 @implementation TabGridBottomToolbar {
+  UIToolbar* _toolbar;
+  UIBarButtonItem* _newTabButtonItem;
   UIBarButtonItem* _spaceItem;
-  UIImage* _transparentBackground;
-  UIImage* _translucentBackground;
-}
-
-- (void)hide {
-  self.newTabButton.button.alpha = 0.0;
-}
-
-- (void)show {
-  self.newTabButton.button.alpha = 1.0;
+  NSArray<NSLayoutConstraint*>* _compactConstraints;
+  NSArray<NSLayoutConstraint*>* _floatingConstraints;
+  TabGridNewTabButton* _smallNewTabButton;
+  TabGridNewTabButton* _largeNewTabButton;
 }
 
 #pragma mark - UIView
 
-// Controls hit testing of the bottom toolbar. When the toolbar is transparent,
-// only respond to tapping on the new tab button.
-- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
-  // The toolbar is not tranparent under compact layout.
-  if ([self shouldUseCompactLayout]) {
-    return [super pointInside:point withEvent:event];
-  }
-  return [self.newTabButton.button
-      pointInside:[self convertPoint:point toView:self.newTabButton.button]
-        withEvent:event];
-}
-
 - (void)willMoveToSuperview:(UIView*)newSuperview {
   // The first time this moves to a superview, perform the view setup.
   if (newSuperview && self.subviews.count == 0) {
@@ -52,63 +36,64 @@
   [self updateLayout];
 }
 
+// Controls hit testing of the bottom toolbar. When the toolbar is transparent,
+// only respond to tapping on the new tab button.
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
+  if ([self shouldUseCompactLayout]) {
+    return [super pointInside:point withEvent:event];
+  }
+  // Only floating new tab button is tappable.
+  return [_largeNewTabButton pointInside:[self convertPoint:point
+                                                     toView:_largeNewTabButton]
+                               withEvent:event];
+}
+
 #pragma mark - Public
 
+// TODO(crbug.com/929981): "traitCollectionDidChange:" method won't get called
+// when the view is not displayed, and in that case the only chance
+// TabGridBottomToolbar can update its layout is when the TabGrid sets its
+// "page" property in the
+// "viewWillTransitionToSize:withTransitionCoordinator:" method. An early
+// return for "self.page == page" can be added here since iOS 13 where the bug
+// is fixed in UIKit.
 - (void)setPage:(TabGridPage)page {
-  // TODO(crbug.com/929981): "traitCollectionDidChange:" method won't get called
-  // when the view is not displayed, and in that case the only chance
-  // TabGridBottomToolbar can update its layout is when the TabGrid sets its
-  // "page" property in the
-  // "viewWillTransitionToSize:withTransitionCoordinator:" method. An early
-  // return for "self.page == page" can be added here once UIKit fixes its
-  // issue or TabGridBottomToolbar is turned into a view controller.
   _page = page;
-  self.newTabButton.page = page;
+  _smallNewTabButton.page = page;
+  _largeNewTabButton.page = page;
   [self updateLayout];
 }
 
+- (void)setNewTabButtonTarget:(id)target action:(SEL)action {
+  [_smallNewTabButton addTarget:target
+                         action:action
+               forControlEvents:UIControlEventTouchUpInside];
+  [_largeNewTabButton addTarget:target
+                         action:action
+               forControlEvents:UIControlEventTouchUpInside];
+}
+
+- (void)hide {
+  _smallNewTabButton.alpha = 0.0;
+  _largeNewTabButton.alpha = 0.0;
+}
+
+- (void)show {
+  _smallNewTabButton.alpha = 1.0;
+  _largeNewTabButton.alpha = 1.0;
+}
+
 #pragma mark - Private
 
-- (void)updateLayout {
-  if (self.page == TabGridPageRemoteTabs) {
-    if ([self shouldUseCompactLayout]) {
-      [self setItems:@[ _spaceItem, self.trailingButton ]];
-      [self setBackgroundImage:_translucentBackground
-            forToolbarPosition:UIBarPositionAny
-                    barMetrics:UIBarMetricsDefault];
-    } else {
-      [self setItems:@[]];
-      [self setBackgroundImage:_transparentBackground
-            forToolbarPosition:UIToolbarPositionAny
-                    barMetrics:UIBarMetricsDefault];
-    }
-  } else {
-    if ([self shouldUseCompactLayout]) {
-      self.newTabButton.sizeClass = TabGridNewTabButtonSizeClassSmall;
-      [self setItems:@[
-        self.leadingButton, _spaceItem, _newTabButton, _spaceItem,
-        self.trailingButton
-      ]];
-      [self setBackgroundImage:_translucentBackground
-            forToolbarPosition:UIBarPositionAny
-                    barMetrics:UIBarMetricsDefault];
-    } else {
-      self.newTabButton.sizeClass = TabGridNewTabButtonSizeClassLarge;
-      [self setItems:@[ _spaceItem, _newTabButton ]];
-      [self setBackgroundImage:_transparentBackground
-            forToolbarPosition:UIToolbarPositionAny
-                    barMetrics:UIBarMetricsDefault];
-    }
-  }
-}
-
 - (void)setupViews {
-  self.translatesAutoresizingMaskIntoConstraints = NO;
-  self.barStyle = UIBarStyleBlack;
-  self.translucent = YES;
+  // For Regular(V) x Compact(H) layout, display UIToolbar.
+  _toolbar = [[UIToolbar alloc] init];
+  _toolbar.translatesAutoresizingMaskIntoConstraints = NO;
+  _toolbar.barStyle = UIBarStyleBlack;
+  _toolbar.translucent = YES;
   // Remove the border of UIToolbar.
-  [self setShadowImage:[[UIImage alloc] init]
-      forToolbarPosition:UIBarPositionAny];
+  [_toolbar setShadowImage:[[UIImage alloc] init]
+        forToolbarPosition:UIBarPositionAny];
 
   _leadingButton = [[UIBarButtonItem alloc] init];
   _leadingButton.tintColor = UIColorFromRGB(kTabGridToolbarTextButtonColor);
@@ -117,20 +102,86 @@
   _trailingButton.style = UIBarButtonItemStyleDone;
   _trailingButton.tintColor = UIColorFromRGB(kTabGridToolbarTextButtonColor);
 
-  _newTabButton = [[TabGridNewTabButton alloc] init];
-
   _spaceItem = [[UIBarButtonItem alloc]
       initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                            target:nil
                            action:nil];
 
-  // Store the translucent background generated by self.translucent=YES.
-  _translucentBackground =
-      [self backgroundImageForToolbarPosition:UIBarPositionAny
-                                   barMetrics:UIBarMetricsDefault];
-  _transparentBackground = [[UIImage alloc] init];
+  UIImage* regularImage = [UIImage imageNamed:@"new_tab_toolbar_button"];
+  UIImage* incognitoImage =
+      [UIImage imageNamed:@"new_tab_toolbar_button_incognito"];
+  _smallNewTabButton =
+      [[TabGridNewTabButton alloc] initWithRegularImage:regularImage
+                                         incognitoImage:incognitoImage];
+  _smallNewTabButton.translatesAutoresizingMaskIntoConstraints = NO;
+  _smallNewTabButton.page = self.page;
 
-  [self updateLayout];
+  _newTabButtonItem =
+      [[UIBarButtonItem alloc] initWithCustomView:_smallNewTabButton];
+  // Set UIBarButtonItem.image to get a built-in accessibility modal panel.
+  // The panel will be shown when user long press on the button, under
+  // accessibility font size. The image will be normalized into a bi-color
+  // image, so the incognito image is suitable because it has a transparent
+  // "+". Use the larger image for higher resolution.
+  _newTabButtonItem.image = incognitoImage;
+  _newTabButtonItem.title = _smallNewTabButton.accessibilityLabel;
+
+  _compactConstraints = @[
+    [_toolbar.topAnchor constraintEqualToAnchor:self.topAnchor],
+    [_toolbar.bottomAnchor
+        constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor],
+    [_toolbar.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+    [_toolbar.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
+  ];
+
+  // For other layout, display a floating new tab button.
+  _largeNewTabButton = [[TabGridNewTabButton alloc]
+      initWithRegularImage:[UIImage imageNamed:@"new_tab_floating_button"]
+            incognitoImage:
+                [UIImage imageNamed:@"new_tab_floating_button_incognito"]];
+  _largeNewTabButton.translatesAutoresizingMaskIntoConstraints = NO;
+  _largeNewTabButton.page = self.page;
+
+  _floatingConstraints = @[
+    [_largeNewTabButton.topAnchor constraintEqualToAnchor:self.topAnchor],
+    [_largeNewTabButton.bottomAnchor
+        constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor
+                       constant:-kTabGridFloatingButtonVerticalInset],
+    [_largeNewTabButton.trailingAnchor
+        constraintEqualToAnchor:self.trailingAnchor
+                       constant:-kTabGridFloatingButtonHorizontalInset],
+  ];
+}
+
+- (void)updateLayout {
+  if ([self shouldUseCompactLayout]) {
+    // For incognito/regular pages, display all 3 buttons;
+    // For remote tabs page, only display new tab button.
+    if (self.page == TabGridPageRemoteTabs) {
+      [_toolbar setItems:@[ _spaceItem, self.trailingButton ]];
+    } else {
+      [_toolbar setItems:@[
+        self.leadingButton, _spaceItem, _newTabButtonItem, _spaceItem,
+        self.trailingButton
+      ]];
+    }
+
+    [NSLayoutConstraint deactivateConstraints:_floatingConstraints];
+    [_largeNewTabButton removeFromSuperview];
+    [self addSubview:_toolbar];
+    [NSLayoutConstraint activateConstraints:_compactConstraints];
+  } else {
+    [NSLayoutConstraint deactivateConstraints:_compactConstraints];
+    [_toolbar removeFromSuperview];
+
+    if (self.page == TabGridPageRemoteTabs) {
+      [NSLayoutConstraint deactivateConstraints:_floatingConstraints];
+      [_largeNewTabButton removeFromSuperview];
+    } else {
+      [self addSubview:_largeNewTabButton];
+      [NSLayoutConstraint activateConstraints:_floatingConstraints];
+    }
+  }
 }
 
 // Returns YES if should use compact bottom toolbar layout.
@@ -139,8 +190,7 @@
   // contradict the keyWindow's |traitCollection| because UIView's
   // |-traitCollectionDidChange:| is not properly called when the view rotates
   // while it is in a ViewController deeper in the ViewController hierarchy. Use
-  // self.traitCollection once this is fixed by UIKit, or remove this function
-  // if TabGridBottomToolbar is turned into a view controller.
+  // self.traitCollection since iOS 13 where the bug is fixed in UIKit.
   return UIApplication.sharedApplication.keyWindow.traitCollection
                  .verticalSizeClass == UIUserInterfaceSizeClassRegular &&
          UIApplication.sharedApplication.keyWindow.traitCollection
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.h b/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.h
index b55b3b5..c82ae31c 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.h
@@ -9,19 +9,18 @@
 
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_paging.h"
 
-// The size class determines the intrinsic size of the button.
-typedef NS_ENUM(NSUInteger, TabGridNewTabButtonSizeClass) {
-  TabGridNewTabButtonSizeClassSmall = 1,
-  TabGridNewTabButtonSizeClassLarge,
-};
+@interface TabGridNewTabButton : UIButton
 
-// The "new tab" button is a button that the user taps when they want to create
-// a new tab. Every combination of |sizeClass| and |page| results in a
-// differently configured button.
-@interface TabGridNewTabButton : UIBarButtonItem
-@property(nonatomic, strong, readonly) UIButton* button;
 @property(nonatomic, assign) TabGridPage page;
-@property(nonatomic, assign) TabGridNewTabButtonSizeClass sizeClass;
+
+// Init with image for regular/incognito page.
+- (instancetype)initWithRegularImage:(UIImage*)regularImage
+                      incognitoImage:(UIImage*)incognitoImage
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_TAB_GRID_NEW_TAB_BUTTON_H_
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.mm
index ad3f021..0e9b01c 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_new_tab_button.mm
@@ -13,66 +13,19 @@
 #endif
 
 @interface TabGridNewTabButton () {
-  UIView* _container;
-
   UIImage* _regularImage;
   UIImage* _incognitoImage;
-  UIImage* _smallRegularImage;
-  UIImage* _smallIncognitoImage;
-  UIImage* _largeRegularImage;
-  UIImage* _largeIncognitoImage;
-
-  NSArray* _smallButtonConstraints;
-  NSArray* _largeButtonConstraints;
 }
 @end
 
 @implementation TabGridNewTabButton
 
-- (instancetype)init {
-  self = [super init];
+- (instancetype)initWithRegularImage:(UIImage*)regularImage
+                      incognitoImage:(UIImage*)incognitoImage {
+  self = [super initWithFrame:CGRectZero];
   if (self) {
-    _smallRegularImage = [UIImage imageNamed:@"new_tab_toolbar_button"];
-    _smallIncognitoImage =
-        [UIImage imageNamed:@"new_tab_toolbar_button_incognito"];
-    _largeRegularImage = [UIImage imageNamed:@"new_tab_floating_button"];
-    _largeIncognitoImage =
-        [UIImage imageNamed:@"new_tab_floating_button_incognito"];
-    // Set UIBarButtonItem.image to get a built-in accessibility modal panel.
-    // The panel will be shown when user long press on the button, under
-    // accessibility font size. The image will be normalized into a bi-color
-    // image, so the incognito image is suitable because it has a transparent
-    // "+". Use the larger image for higher resolution.
-    self.image = _largeIncognitoImage;
-
-    _container = [[UIView alloc] init];
-    _container.translatesAutoresizingMaskIntoConstraints = NO;
-    self.customView = _container;
-
-    _button = [[UIButton alloc] init];
-    _button.translatesAutoresizingMaskIntoConstraints = NO;
-    // Set a high compression resistance priority otherwise the button will be
-    // compressed by UIToolbar's height constraint.
-    [_button
-        setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh + 1
-                                        forAxis:UILayoutConstraintAxisVertical];
-    [_container addSubview:_button];
-
-    _smallButtonConstraints = @[
-      [_button.topAnchor constraintEqualToAnchor:_container.topAnchor],
-      [_button.bottomAnchor constraintEqualToAnchor:_container.bottomAnchor],
-      [_button.leadingAnchor constraintEqualToAnchor:_container.leadingAnchor],
-      [_button.trailingAnchor constraintEqualToAnchor:_container.trailingAnchor]
-    ];
-
-    _largeButtonConstraints = @[
-      [_button.topAnchor constraintEqualToAnchor:_container.topAnchor],
-      [_button.bottomAnchor
-          constraintEqualToAnchor:_container.bottomAnchor
-                         constant:-kTabGridFloatingButtonVerticalInset],
-      [_button.leadingAnchor constraintEqualToAnchor:_container.leadingAnchor],
-      [_button.trailingAnchor constraintEqualToAnchor:_container.trailingAnchor]
-    ];
+    _regularImage = regularImage;
+    _incognitoImage = incognitoImage;
   }
   return self;
 }
@@ -82,61 +35,25 @@
 - (void)setPage:(TabGridPage)page {
   if (page == _page)
     return;
+  UIImage* renderedImage;
   switch (page) {
     case TabGridPageIncognitoTabs:
-      self.button.accessibilityLabel =
+      self.accessibilityLabel =
           l10n_util::GetNSString(IDS_IOS_TAB_GRID_CREATE_NEW_INCOGNITO_TAB);
-      break;
-    case TabGridPageRegularTabs:
-      self.button.accessibilityLabel =
-          l10n_util::GetNSString(IDS_IOS_TAB_GRID_CREATE_NEW_TAB);
-      break;
-    case TabGridPageRemoteTabs:
-      break;
-  }
-  self.title = self.button.accessibilityLabel;
-  _page = page;
-  [self updateButtonImage];
-}
-
-- (void)setSizeClass:(TabGridNewTabButtonSizeClass)sizeClass {
-  if (sizeClass == _sizeClass)
-    return;
-  switch (sizeClass) {
-    case TabGridNewTabButtonSizeClassSmall:
-      _regularImage = _smallRegularImage;
-      _incognitoImage = _smallIncognitoImage;
-      [NSLayoutConstraint deactivateConstraints:_largeButtonConstraints];
-      [NSLayoutConstraint activateConstraints:_smallButtonConstraints];
-      break;
-    case TabGridNewTabButtonSizeClassLarge:
-      _regularImage = _largeRegularImage;
-      _incognitoImage = _largeIncognitoImage;
-      [NSLayoutConstraint deactivateConstraints:_smallButtonConstraints];
-      [NSLayoutConstraint activateConstraints:_largeButtonConstraints];
-      break;
-  }
-  _sizeClass = sizeClass;
-  [self updateButtonImage];
-}
-
-#pragma mark - Private
-
-- (void)updateButtonImage {
-  UIImage* renderedImage;
-  switch (self.page) {
-    case TabGridPageIncognitoTabs:
       renderedImage = [_incognitoImage
           imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
       break;
     case TabGridPageRegularTabs:
+      self.accessibilityLabel =
+          l10n_util::GetNSString(IDS_IOS_TAB_GRID_CREATE_NEW_TAB);
       renderedImage = [_regularImage
           imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
       break;
-    default:
+    case TabGridPageRemoteTabs:
       break;
   }
-  [self.button setImage:renderedImage forState:UIControlStateNormal];
+  _page = page;
+  [self setImage:renderedImage forState:UIControlStateNormal];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 3b9b835d..8655386 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -99,8 +99,8 @@
 }
 }  // namespace
 
-@interface TabGridViewController ()<GridViewControllerDelegate,
-                                    UIScrollViewAccessibilityDelegate>
+@interface TabGridViewController () <GridViewControllerDelegate,
+                                     UIScrollViewAccessibilityDelegate>
 // It is programmer error to broadcast incognito content visibility when the
 // view is not visible. Bookkeeping is based on |-viewWillAppear:| and
 // |-viewWillDisappear methods. Note that the |Did| methods are not reliably
@@ -744,9 +744,16 @@
 // Adds the bottom toolbar and sets constraints.
 - (void)setupBottomToolbar {
   TabGridBottomToolbar* bottomToolbar = [[TabGridBottomToolbar alloc] init];
+  self.bottomToolbar = bottomToolbar;
   bottomToolbar.translatesAutoresizingMaskIntoConstraints = NO;
   [self.view addSubview:bottomToolbar];
-  self.bottomToolbar = bottomToolbar;
+  [NSLayoutConstraint activateConstraints:@[
+    [bottomToolbar.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
+    [bottomToolbar.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor],
+    [bottomToolbar.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor],
+  ]];
 
   bottomToolbar.leadingButton.target = self;
   bottomToolbar.leadingButton.action = @selector(closeAllButtonTapped:);
@@ -757,18 +764,8 @@
   bottomToolbar.trailingButton.target = self;
   bottomToolbar.trailingButton.action = @selector(doneButtonTapped:);
 
-  [bottomToolbar.newTabButton.button addTarget:self
-                                        action:@selector(newTabButtonTapped:)
-                              forControlEvents:UIControlEventTouchUpInside];
-
-  [NSLayoutConstraint activateConstraints:@[
-    [bottomToolbar.bottomAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
-    [bottomToolbar.leadingAnchor
-        constraintEqualToAnchor:self.view.leadingAnchor],
-    [bottomToolbar.trailingAnchor
-        constraintEqualToAnchor:self.view.trailingAnchor],
-  ]];
+  [bottomToolbar setNewTabButtonTarget:self
+                                action:@selector(newTabButtonTapped:)];
 }
 
 - (void)configureViewControllerForCurrentSizeClassesAndPage {
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 447abb7..6a0cae91 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -37,6 +37,7 @@
   deps = [
     "//base",
     "//base:i18n",
+    "//build:branding_buildflags",
     "//components/autofill/core/browser",
     "//components/crash/core/browser",
     "//components/flags_ui",
diff --git a/ios/chrome/browser/ui/webui/flags_ui.cc b/ios/chrome/browser/ui/webui/flags_ui.cc
index f852803..70fb2f84 100644
--- a/ios/chrome/browser/ui/webui/flags_ui.cc
+++ b/ios/chrome/browser/ui/webui/flags_ui.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "build/branding_buildflags.h"
 #include "components/flags_ui/flags_ui_constants.h"
 #include "components/flags_ui/flags_ui_pref_names.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
@@ -170,9 +171,9 @@
 }
 
 void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) {
-#if CHROMIUM_BUILD
+#if BUILDFLAG(CHROMIUM_BRANDING)
   CHECK(false);
-#endif  // CHROMIUM_BUILD
+#endif  // BUILDFLAG(CHROMIUM_BRANDING)
 }
 
 void FlagsDOMHandler::HandleResetAllFlags(const base::ListValue* args) {
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper.h b/ios/chrome/browser/web/web_state_delegate_tab_helper.h
index 9d768db..528da58 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper.h
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_WEB_WEB_STATE_DELEGATE_TAB_HELPER_H_
 
 #include "base/memory/weak_ptr.h"
+#import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
 #include "ios/web/public/web_state/web_state_user_data.h"
 
@@ -22,6 +23,8 @@
   // into this tab helper.
 
   // web::WebStateDelegate:
+  web::JavaScriptDialogPresenter* GetJavaScriptDialogPresenter(
+      web::WebState* source) override;
   void OnAuthRequired(
       web::WebState* source,
       NSURLProtectionSpace* protection_space,
@@ -37,6 +40,7 @@
   void OnHTTPAuthOverlayFinished(web::WebStateDelegate::AuthCallback callback,
                                  OverlayResponse* response);
 
+  OverlayJavaScriptDialogPresenter java_script_dialog_presenter_;
   base::WeakPtrFactory<WebStateDelegateTabHelper> weak_factory_;
 };
 
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper.mm b/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
index 935851c..6abae5e 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper.mm
@@ -23,6 +23,11 @@
 
 WebStateDelegateTabHelper::~WebStateDelegateTabHelper() = default;
 
+web::JavaScriptDialogPresenter*
+WebStateDelegateTabHelper::GetJavaScriptDialogPresenter(web::WebState* source) {
+  return &java_script_dialog_presenter_;
+}
+
 void WebStateDelegateTabHelper::OnAuthRequired(
     web::WebState* source,
     NSURLProtectionSpace* protection_space,
diff --git a/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm b/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
index b01450e6..d96710a 100644
--- a/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
+++ b/ios/chrome/browser/web/web_state_delegate_tab_helper_unittest.mm
@@ -7,6 +7,9 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #import "ios/chrome/browser/overlays/public/web_content_area/http_auth_overlay.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_overlay.h"
+#include "ios/web/public/java_script_dialog_presenter.h"
+#include "ios/web/public/java_script_dialog_type.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
@@ -60,3 +63,30 @@
   EXPECT_TRUE(request);
   EXPECT_TRUE(request->GetConfig<HTTPAuthOverlayRequestConfig>());
 }
+
+// Tests that GetJavaScriptDialogPresenter() returns an overlay-based JavaScript
+// dialog presenter.
+TEST_F(WebStateDelegateTabHelperTest, GetJavaScriptDialogPresenter) {
+  // Verify that the delegate returns a non-null presenter.
+  web::JavaScriptDialogPresenter* presenter =
+      delegate()->GetJavaScriptDialogPresenter(web_state());
+  EXPECT_TRUE(presenter);
+
+  // Present a JavaScript alert.
+  GURL kOriginUrl("http://chromium.test");
+  web::DialogClosedCallback callback =
+      base::BindOnce(^(bool success, NSString* user_input){
+      });
+  presenter->RunJavaScriptDialog(web_state(), kOriginUrl,
+                                 web::JAVASCRIPT_DIALOG_TYPE_ALERT, @"", @"",
+                                 std::move(callback));
+
+  // Verify that JavaScript alert OverlayRequest has been added to the
+  // WebState's queue.
+  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+      web_state(), OverlayModality::kWebContentArea);
+  ASSERT_TRUE(queue);
+  OverlayRequest* request = queue->front_request();
+  EXPECT_TRUE(request);
+  EXPECT_TRUE(request->GetConfig<JavaScriptAlertOverlayRequestConfig>());
+}
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index 0ec2ce4..0e0bcca4 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -23,10 +23,6 @@
 // https://crbug.com/841105.
 extern const base::Feature kCrashOnUnexpectedURLChange;
 
-// Used to disconnect the scroll proxy during slimnav restore. This is a
-// speculative change to mitigate the crashes in https://crbug.com/959499.
-extern const base::Feature kDisconnectScrollProxyDuringRestore;
-
 // Used to enable the workaround for WKWebView history clobber bug
 // (crbug.com/887497).
 extern const base::Feature kHistoryClobberWorkaround;
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index 754563d..fdc2dda 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -23,9 +23,6 @@
 const base::Feature kCrashOnUnexpectedURLChange{
     "CrashOnUnexpectedURLChange", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kDisconnectScrollProxyDuringRestore{
-    "DisconnectScrollProxyDuringRestore", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kHistoryClobberWorkaround{
     "WKWebViewHistoryClobberWorkaround", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index d15ba18..e0886c3f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -974,8 +974,7 @@
       web::WKNavigationState::FINISHED)
     return;
 
-  // Restore allowsBackForwardNavigationGestures and the scroll proxy once
-  // restoration is complete.
+  // Restore allowsBackForwardNavigationGestures once restoration is complete.
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
       !self.navigationManagerImpl->IsRestoreSessionInProgress()) {
     if (_webView.allowsBackForwardNavigationGestures !=
@@ -983,11 +982,6 @@
       _webView.allowsBackForwardNavigationGestures =
           _allowsBackForwardNavigationGestures;
     }
-
-    if (base::FeatureList::IsEnabled(
-            web::features::kDisconnectScrollProxyDuringRestore)) {
-      [_containerView reconnectScrollProxy];
-    }
   }
 
   BOOL success = !context || !context->GetError();
@@ -1660,13 +1654,6 @@
       [[CRWWebViewContentView alloc] initWithWebView:self.webView
                                           scrollView:self.webScrollView];
   [_containerView displayWebViewContentView:webViewContentView];
-
-  if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-      self.navigationManagerImpl->IsRestoreSessionInProgress() &&
-      base::FeatureList::IsEnabled(
-          web::features::kDisconnectScrollProxyDuringRestore)) {
-    [_containerView disconnectScrollProxy];
-  }
 }
 
 - (void)removeWebView {
@@ -2149,17 +2136,12 @@
   [self loadCompleteWithSuccess:loadSuccess forContext:context];
 }
 
-- (void)webRequestControllerDisconnectScrollViewProxy:
+- (void)webRequestControllerDisableNavigationGesturesUntilFinishNavigation:
     (CRWWebRequestController*)requestController {
   // Disable |allowsBackForwardNavigationGestures| during restore. Otherwise,
   // WebKit will trigger a snapshot for each (blank) page, and quickly
-  // overload system memory.  Also disables the scroll proxy during session
-  // restoration.
+  // overload system memory.
   self.webView.allowsBackForwardNavigationGestures = NO;
-  if (base::FeatureList::IsEnabled(
-          web::features::kDisconnectScrollProxyDuringRestore)) {
-    [_containerView disconnectScrollProxy];
-  }
 }
 
 - (web::UserInteractionState*)webRequestControllerUserInteractionState:
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.h b/ios/web/web_state/ui/crw_web_controller_container_view.h
index e32a523..a2579996 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.h
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.h
@@ -76,11 +76,6 @@
 // Removes all subviews and resets state to default.
 - (void)resetContent;
 
-// Disconnects and reconnects the scroll proxy to prevent extra calls to
-// WKebView.
-- (void)disconnectScrollProxy;
-- (void)reconnectScrollProxy;
-
 // Replaces the currently displayed content with |webViewContentView|.
 - (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView;
 
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm
index f6ead8f..61cb492 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -219,14 +219,6 @@
   self.contentViewProxy.contentView = self.webViewContentView;
 }
 
-- (void)disconnectScrollProxy {
-  [self.contentViewProxy disconnectScrollProxy];
-}
-
-- (void)reconnectScrollProxy {
-  [self.contentViewProxy reconnectScrollProxy];
-}
-
 #pragma mark UIView (printing)
 
 // Only print the web view by returning the web view printformatter.
diff --git a/ios/web/web_state/ui/crw_web_request_controller.h b/ios/web/web_state/ui/crw_web_request_controller.h
index 8d09224..aa1a10b 100644
--- a/ios/web/web_state/ui/crw_web_request_controller.h
+++ b/ios/web/web_state/ui/crw_web_request_controller.h
@@ -42,8 +42,11 @@
     didCompleteLoadWithSuccess:(BOOL)loadSuccess
                     forContext:(web::NavigationContextImpl*)context;
 
-// Asks proxy to disconnect scroll proxy if needed.
-- (void)webRequestControllerDisconnectScrollViewProxy:
+// Asks proxy to disable back forward navigation gestures until the current (in
+// this case restore) navigation is complete.  This is necessary as restore
+// can trigger a large number of navigations, and when back/forward is enabled
+// this can lead to an unbounded memory spike.
+- (void)webRequestControllerDisableNavigationGesturesUntilFinishNavigation:
     (CRWWebRequestController*)requestController;
 
 // Asks the delegate for the associated |UserInteractionState|.
diff --git a/ios/web/web_state/ui/crw_web_request_controller.mm b/ios/web/web_state/ui/crw_web_request_controller.mm
index 682fbde7..d2ca2b8 100644
--- a/ios/web/web_state/ui/crw_web_request_controller.mm
+++ b/ios/web/web_state/ui/crw_web_request_controller.mm
@@ -657,7 +657,9 @@
 
     if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
         self.navigationManagerImpl->IsRestoreSessionInProgress()) {
-      [_delegate webRequestControllerDisconnectScrollViewProxy:self];
+      [_delegate
+          webRequestControllerDisableNavigationGesturesUntilFinishNavigation:
+              self];
     }
 
     WKNavigation* navigation = nil;
diff --git a/ios/web/web_state/ui/crw_web_view_proxy_impl.h b/ios/web/web_state/ui/crw_web_view_proxy_impl.h
index 4dae7ee..fb4323a0 100644
--- a/ios/web/web_state/ui/crw_web_view_proxy_impl.h
+++ b/ios/web/web_state/ui/crw_web_view_proxy_impl.h
@@ -23,11 +23,6 @@
 // Init with a weak reference of web controller, used for passing through calls.
 - (instancetype)initWithWebController:(CRWWebController*)webController;
 
-// Disconnects and reconnects the scroll proxy to prevent extra calls to
-// WKebView.
-- (void)disconnectScrollProxy;
-- (void)reconnectScrollProxy;
-
 @end
 
 #endif  // IOS_WEB_WEB_STATE_UI_CRW_WEB_VIEW_PROXY_IMPL_H_
diff --git a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
index 0d472985..5e55401 100644
--- a/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
+++ b/ios/web/web_state/ui/crw_web_view_proxy_impl.mm
@@ -170,14 +170,6 @@
   [_contentViewScrollViewProxy setScrollView:contentView.scrollView];
 }
 
-- (void)disconnectScrollProxy {
-  [_contentViewScrollViewProxy setScrollView:nil];
-}
-
-- (void)reconnectScrollProxy {
-  [_contentViewScrollViewProxy setScrollView:self.contentView.scrollView];
-}
-
 - (void)addSubview:(UIView*)view {
   return [_contentView addSubview:view];
 }
diff --git a/media/gpu/android/shared_image_video.cc b/media/gpu/android/shared_image_video.cc
index 2ac3775..4908964 100644
--- a/media/gpu/android/shared_image_video.cc
+++ b/media/gpu/android/shared_image_video.cc
@@ -229,16 +229,7 @@
               static_cast<GLenum>(GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM));
 
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
-    DCHECK(video_backing);
-    auto* codec_image = video_backing->codec_image_.get();
-    auto* texture_owner = codec_image->texture_owner().get();
-
-    // Render the codec image.
-    codec_image->RenderToFrontBuffer();
-
-    // Bind the tex image if it's not already bound.
-    if (!texture_owner->binds_texture_on_update())
-      texture_owner->EnsureTexImageBound();
+    video_backing->BeginGLReadAccess();
     return true;
   }
 
@@ -250,6 +241,43 @@
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureVideo);
 };
 
+// Representation of SharedImageVideo as a GL Texture.
+class SharedImageRepresentationGLTexturePassthroughVideo
+    : public gpu::SharedImageRepresentationGLTexturePassthrough {
+ public:
+  SharedImageRepresentationGLTexturePassthroughVideo(
+      gpu::SharedImageManager* manager,
+      SharedImageVideo* backing,
+      gpu::MemoryTypeTracker* tracker,
+      scoped_refptr<gpu::gles2::TexturePassthrough> texture)
+      : gpu::SharedImageRepresentationGLTexturePassthrough(manager,
+                                                           backing,
+                                                           tracker),
+        texture_(std::move(texture)) {}
+
+  const scoped_refptr<gpu::gles2::TexturePassthrough>& GetTexturePassthrough()
+      override {
+    return texture_;
+  }
+
+  bool BeginAccess(GLenum mode) override {
+    // This representation should only be called for read.
+    DCHECK_EQ(mode,
+              static_cast<GLenum>(GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM));
+
+    auto* video_backing = static_cast<SharedImageVideo*>(backing());
+    video_backing->BeginGLReadAccess();
+    return true;
+  }
+
+  void EndAccess() override {}
+
+ private:
+  scoped_refptr<gpu::gles2::TexturePassthrough> texture_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTexturePassthroughVideo);
+};
+
 // Vulkan backed Skia representation of SharedImageVideo.
 class SharedImageRepresentationVideoSkiaVk
     : public gpu::SharedImageRepresentationSkia {
@@ -427,6 +455,29 @@
       manager, this, tracker, texture);
 }
 
+// TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
+// image. Add support for overlays in GLRenderer as well as overlay
+// representations of shared image.
+std::unique_ptr<gpu::SharedImageRepresentationGLTexturePassthrough>
+SharedImageVideo::ProduceGLTexturePassthrough(gpu::SharedImageManager* manager,
+                                              gpu::MemoryTypeTracker* tracker) {
+  // For (old) overlays, we don't have a texture owner, but overlay promotion
+  // might not happen for some reasons. In that case, it will try to draw
+  // which should result in no image.
+  if (!codec_image_->texture_owner())
+    return nullptr;
+  // TODO(vikassoni): We would want to give the TextureOwner's underlying
+  // Texture, but it was not set with the correct size. The AbstractTexture,
+  // that we use for legacy mailbox, is correctly set.
+  scoped_refptr<gpu::gles2::TexturePassthrough> texture =
+      gpu::gles2::TexturePassthrough::CheckedCast(
+          abstract_texture_->GetTextureBase());
+  DCHECK(texture);
+
+  return std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
+      manager, this, tracker, std::move(texture));
+}
+
 // Currently SkiaRenderer doesn't support overlays.
 std::unique_ptr<gpu::SharedImageRepresentationSkia>
 SharedImageVideo::ProduceSkia(
@@ -460,4 +511,14 @@
       std::move(gl_representation), nullptr, manager, this, tracker);
 }
 
+void SharedImageVideo::BeginGLReadAccess() {
+  // Render the codec image.
+  codec_image_->RenderToFrontBuffer();
+
+  // Bind the tex image if it's not already bound.
+  auto* texture_owner = codec_image_->texture_owner().get();
+  if (!texture_owner->binds_texture_on_update())
+    texture_owner->EnsureTexImageBound();
+}
+
 }  // namespace media
diff --git a/media/gpu/android/shared_image_video.h b/media/gpu/android/shared_image_video.h
index 42407ed..cffd661 100644
--- a/media/gpu/android/shared_image_video.h
+++ b/media/gpu/android/shared_image_video.h
@@ -64,6 +64,10 @@
       gpu::SharedImageManager* manager,
       gpu::MemoryTypeTracker* tracker) override;
 
+  std::unique_ptr<gpu::SharedImageRepresentationGLTexturePassthrough>
+  ProduceGLTexturePassthrough(gpu::SharedImageManager* manager,
+                              gpu::MemoryTypeTracker* tracker) override;
+
   std::unique_ptr<gpu::SharedImageRepresentationSkia> ProduceSkia(
       gpu::SharedImageManager* manager,
       gpu::MemoryTypeTracker* tracker,
@@ -74,9 +78,12 @@
 
  private:
   friend class SharedImageRepresentationGLTextureVideo;
+  friend class SharedImageRepresentationGLTexturePassthroughVideo;
   friend class SharedImageRepresentationVideoSkiaGL;
   friend class SharedImageRepresentationVideoSkiaVk;
 
+  void BeginGLReadAccess();
+
   scoped_refptr<CodecImage> codec_image_;
 
   // |abstract_texture_| is only used for legacy mailbox.
diff --git a/media/gpu/linux/dmabuf_video_frame_pool.h b/media/gpu/linux/dmabuf_video_frame_pool.h
index cf920376..3ff360e8 100644
--- a/media/gpu/linux/dmabuf_video_frame_pool.h
+++ b/media/gpu/linux/dmabuf_video_frame_pool.h
@@ -6,6 +6,7 @@
 #define MEDIA_GPU_LINUX_DMABUF_VIDEO_FRAME_POOL_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
 #include "base/sequenced_task_runner.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
@@ -35,11 +36,13 @@
   // Used to prevent client from draining all memory.
   virtual void SetMaxNumFrames(size_t max_num_frames) = 0;
 
-  // Sets the parameters of allocating frames.
+  // Sets the parameters of allocating frames and returns a valid
+  // VideoFrameLayout that VideoFrame will be created by GetFrame() has.
   // This method must be called before GetFrame() is called.
-  virtual void SetFrameFormat(VideoFrameLayout layout,
-                              gfx::Rect visible_rect,
-                              gfx::Size natural_size) = 0;
+  virtual base::Optional<VideoFrameLayout> NegotiateFrameFormat(
+      const VideoFrameLayout& layout,
+      const gfx::Rect& visible_rect,
+      const gfx::Size& natural_size) = 0;
 
   // Returns a frame from the pool with the parameters assigned by
   // SetFrameFormat(). Returns nullptr if the pool is exhausted.
diff --git a/media/gpu/linux/platform_video_frame_pool.cc b/media/gpu/linux/platform_video_frame_pool.cc
index cc8b8a1..ddaacfb 100644
--- a/media/gpu/linux/platform_video_frame_pool.cc
+++ b/media/gpu/linux/platform_video_frame_pool.cc
@@ -65,7 +65,6 @@
     const base::TickClock* tick_clock)
     : create_frame_cb_(std::move(cb)),
       tick_clock_(tick_clock),
-      format_(PIXEL_FORMAT_UNKNOWN),
       max_num_frames_(kDefaultMaxNumFrames),
       weak_this_factory_(this) {
   DVLOGF(4);
@@ -88,11 +87,13 @@
   DVLOGF(4);
   base::AutoLock auto_lock(lock_);
 
-  if (coded_size_.IsEmpty()) {
-    VLOGF(1) << "Please call SetFrameFormat() first.";
+  if (!frame_layout_) {
+    VLOGF(1) << "Please call NegotiateFrameFormat() first.";
     return nullptr;
   }
 
+  VideoPixelFormat format = frame_layout_->format();
+  const gfx::Size& coded_size = frame_layout_->coded_size();
   if (free_frames_.empty()) {
     if (GetTotalNumFrames_Locked() >= max_num_frames_)
       return nullptr;
@@ -101,8 +102,8 @@
     // is sub rect of the original visible_rect. Therefore we set visible_rect
     // as large as coded_size to guarantee this condition.
     scoped_refptr<VideoFrame> new_frame =
-        create_frame_cb_.Run(format_, coded_size_, gfx::Rect(coded_size_),
-                             coded_size_, base::TimeDelta());
+        create_frame_cb_.Run(format, coded_size, gfx::Rect(coded_size),
+                             coded_size, base::TimeDelta());
     if (!new_frame)
       return nullptr;
 
@@ -112,11 +113,11 @@
   DCHECK(!free_frames_.empty());
   scoped_refptr<VideoFrame> origin_frame = std::move(free_frames_.back().frame);
   free_frames_.pop_back();
-  DCHECK_EQ(origin_frame->format(), format_);
-  DCHECK_EQ(origin_frame->coded_size(), coded_size_);
+  DCHECK_EQ(origin_frame->format(), format);
+  DCHECK_EQ(origin_frame->coded_size(), coded_size);
 
   scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
-      *origin_frame, format_, visible_rect_, natural_size_);
+      *origin_frame, format, visible_rect_, natural_size_);
   DCHECK(wrapped_frame);
   frames_in_use_.emplace(GetDmabufId(*wrapped_frame), origin_frame.get());
   wrapped_frame->AddDestructionObserver(
@@ -167,9 +168,10 @@
     std::move(frame_available_cb_).Run();
 }
 
-void PlatformVideoFramePool::SetFrameFormat(VideoFrameLayout layout,
-                                            gfx::Rect visible_rect,
-                                            gfx::Size natural_size) {
+base::Optional<VideoFrameLayout> PlatformVideoFramePool::NegotiateFrameFormat(
+    const VideoFrameLayout& layout,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size) {
   DVLOGF(4);
   base::AutoLock auto_lock(lock_);
 
@@ -177,17 +179,27 @@
   // the pool here. If only the visible or natural size changed we don't need to
   // allocate new frames, but will just update the properties of wrapped frames
   // returned by GetFrame().
-  if (!IsSameLayout_Locked(layout.format(), layout.coded_size())) {
+  // NOTE: It is assumed layout is determined by |format| and |coded_size|.
+  if (!IsSameLayout_Locked(layout)) {
     DVLOGF(4) << "The video frame format is changed. Clearing the pool.";
     free_frames_.clear();
   }
 
-  format_ = layout.format();
-  coded_size_ = layout.coded_size();
   visible_rect_ = visible_rect;
   natural_size_ = natural_size;
-  DCHECK_LE(visible_rect_.right(), coded_size_.width());
-  DCHECK_LE(visible_rect_.bottom(), coded_size_.height());
+
+  // Create a temporary frame in order to know VideoFrameLayout that VideoFrame
+  // that will be allocated in GetFrame() has.
+  auto frame =
+      create_frame_cb_.Run(layout.format(), layout.coded_size(), visible_rect,
+                           natural_size_, base::TimeDelta());
+  if (!frame) {
+    VLOGF(1) << "Failed to create video frame";
+    return base::nullopt;
+  }
+  frame_layout_ = base::make_optional<VideoFrameLayout>(frame->layout());
+
+  return frame_layout_;
 }
 
 bool PlatformVideoFramePool::IsExhausted() {
@@ -249,7 +261,7 @@
   DCHECK(it != frames_in_use_.end());
   frames_in_use_.erase(it);
 
-  if (IsSameLayout_Locked(origin_frame->format(), origin_frame->coded_size())) {
+  if (IsSameLayout_Locked(origin_frame->layout())) {
     InsertFreeFrame_Locked(std::move(origin_frame));
   }
 
@@ -274,12 +286,13 @@
   return free_frames_.size() + frames_in_use_.size();
 }
 
-bool PlatformVideoFramePool::IsSameLayout_Locked(VideoPixelFormat format,
-                                                 gfx::Size coded_size) const {
+bool PlatformVideoFramePool::IsSameLayout_Locked(
+    const VideoFrameLayout& layout) const {
   DVLOGF(4);
   lock_.AssertAcquired();
 
-  return format_ == format && coded_size_ == coded_size;
+  return frame_layout_ && frame_layout_->format() == layout.format() &&
+         frame_layout_->coded_size() == layout.coded_size();
 }
 
 size_t PlatformVideoFramePool::GetPoolSizeForTesting() {
diff --git a/media/gpu/linux/platform_video_frame_pool.h b/media/gpu/linux/platform_video_frame_pool.h
index be8e03a..47df14dc 100644
--- a/media/gpu/linux/platform_video_frame_pool.h
+++ b/media/gpu/linux/platform_video_frame_pool.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_frame_layout.h"
 #include "media/gpu/linux/dmabuf_video_frame_pool.h"
 #include "media/gpu/media_gpu_export.h"
 
@@ -32,10 +33,10 @@
 // the memory is returned to the pool for use by a subsequent GetFrame()
 // call. The memory in the pool is retained for the life of the
 // PlatformVideoFramePool object. Before calling GetFrame(), the client should
-// call SetFrameFormat(). If the parameters passed to SetFrameFormat() are
-// changed, then the memory used by frames with the old parameter values will be
-// purged from the pool. Frames which are not used for a certain period
-// will be purged.
+// call NegotiateFrameFormat(). If the parameters passed to
+// NegotiateFrameFormat() are changed, then the memory used by frames with the
+// old parameter values will be purged from the pool. Frames which are not used
+// for a certain period will be purged.
 class MEDIA_GPU_EXPORT PlatformVideoFramePool : public DmabufVideoFramePool {
  public:
   using DmabufId = const std::vector<base::ScopedFD>*;
@@ -47,9 +48,10 @@
   void set_parent_task_runner(
       scoped_refptr<base::SequencedTaskRunner> parent_task_runner) override;
   void SetMaxNumFrames(size_t max_num_frames) override;
-  void SetFrameFormat(VideoFrameLayout layout,
-                      gfx::Rect visible_rect,
-                      gfx::Size natural_size) override;
+  base::Optional<VideoFrameLayout> NegotiateFrameFormat(
+      const VideoFrameLayout& layout,
+      const gfx::Rect& visible_rect,
+      const gfx::Size& natural_size) override;
   scoped_refptr<VideoFrame> GetFrame() override;
   bool IsExhausted() override;
   VideoFrame* UnwrapFrame(const VideoFrame& wrapped_frame) override;
@@ -92,7 +94,7 @@
   void InsertFreeFrame_Locked(scoped_refptr<VideoFrame> frame)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
   size_t GetTotalNumFrames_Locked() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  bool IsSameLayout_Locked(VideoPixelFormat format, gfx::Size coded_size) const
+  bool IsSameLayout_Locked(const VideoFrameLayout& layout) const
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
   bool IsExhausted_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
@@ -110,11 +112,10 @@
   // Every public method and OnFrameReleased() should acquire this lock.
   base::Lock lock_;
 
-  // The arguments of current frame. We allocate new frames only with |format_|
-  // and |coded_size_|. When calling GetFrame(), we update |visible_rect_| and
-  // |natural_size_| of wrapped frames.
-  VideoPixelFormat format_ GUARDED_BY(lock_);
-  gfx::Size coded_size_ GUARDED_BY(lock_);
+  // The arguments of current frame. We allocate new frames only if a pixel
+  // format or coded size in |frame_layout_| is changed. When GetFrame() is
+  // called, we update |visible_rect_| and |natural_size_| of wrapped frames.
+  base::Optional<VideoFrameLayout> frame_layout_ GUARDED_BY(lock_);
   gfx::Rect visible_rect_ GUARDED_BY(lock_);
   gfx::Size natural_size_ GUARDED_BY(lock_);
 
diff --git a/media/gpu/linux/platform_video_frame_pool_unittest.cc b/media/gpu/linux/platform_video_frame_pool_unittest.cc
index aa223ff..f01b489 100644
--- a/media/gpu/linux/platform_video_frame_pool_unittest.cc
+++ b/media/gpu/linux/platform_video_frame_pool_unittest.cc
@@ -68,7 +68,7 @@
     layout_ = VideoFrameLayout::Create(format, coded_size);
     DCHECK(layout_);
 
-    pool_->SetFrameFormat(*layout_, visible_rect_, natural_size_);
+    pool_->NegotiateFrameFormat(*layout_, visible_rect_, natural_size_);
   }
 
   scoped_refptr<VideoFrame> GetFrame(int timestamp_ms) {
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index aada606c..57048f19 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -339,7 +339,9 @@
                                   base::BindOnce(std::move(init_cb), false));
     return;
   }
+
   needs_bitstream_conversion_ = (config.codec() == kCodecH264);
+  pixel_aspect_ratio_ = config.GetPixelAspectRatio();
 
   // Setup input format.
   if (!SetupInputFormat(input_format_fourcc)) {
@@ -350,29 +352,13 @@
   }
 
   // Setup output format.
-  uint32_t output_format_fourcc = NegotiateOutputFormat();
-  num_output_planes_ =
-      V4L2Device::GetNumPlanesOfV4L2PixFmt(output_format_fourcc);
-  if (!SetupOutputFormat(output_format_fourcc)) {
+  if (!SetupOutputFormat(config.coded_size(), config.visible_rect())) {
     VLOGF(1) << "Failed to setup output format.";
     client_task_runner_->PostTask(FROM_HERE,
                                   base::BindOnce(std::move(init_cb), false));
     return;
   }
 
-  // Setup frame pool.
-  VideoPixelFormat output_format =
-      V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc);
-  frame_layout_ = VideoFrameLayout::Create(output_format, config.coded_size());
-  if (!frame_layout_) {
-    VLOGF(1) << "Failed to create video frame layout.";
-    client_task_runner_->PostTask(FROM_HERE,
-                                  base::BindOnce(std::move(init_cb), false));
-    return;
-  }
-  pixel_aspect_ratio_ = config.GetPixelAspectRatio();
-  UpdateVideoFramePoolFormat(config.visible_rect());
-
   // Create Input/Output V4L2Queue
   input_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
   output_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
@@ -433,48 +419,81 @@
   return true;
 }
 
-uint32_t V4L2SliceVideoDecoder::NegotiateOutputFormat() {
+base::Optional<struct v4l2_format>
+V4L2SliceVideoDecoder::SetFormatOnOutputQueue(uint32_t format_fourcc,
+                                              const gfx::Size& size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
+
+  struct v4l2_format format = {};
+  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+  format.fmt.pix_mp.pixelformat = format_fourcc;
+  format.fmt.pix_mp.width = size.width();
+  format.fmt.pix_mp.height = size.height();
+  format.fmt.pix_mp.num_planes =
+      V4L2Device::GetNumPlanesOfV4L2PixFmt(format_fourcc);
+  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0 ||
+      format.fmt.pix_mp.pixelformat != format_fourcc) {
+    VPLOGF(2) << "Failed to set output format. format_fourcc=" << format_fourcc;
+    return base::nullopt;
+  }
+  return format;
+}
+
+base::Optional<VideoFrameLayout> V4L2SliceVideoDecoder::SetupOutputFormat(
+    const gfx::Size& size,
+    const gfx::Rect& visible_rect) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
 
   const std::vector<uint32_t> formats = device_->EnumerateSupportedPixelformats(
       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
   DCHECK(!formats.empty());
-  for (const auto format : formats) {
-    if (device_->CanCreateEGLImageFrom(format)) {
-      return format;
+  for (const auto format_fourcc : formats) {
+    if (!device_->CanCreateEGLImageFrom(format_fourcc))
+      continue;
+
+    // Make sure VFPool can allocate video frames with width and height.
+    auto frame_layout =
+        UpdateVideoFramePoolFormat(format_fourcc, size, visible_rect);
+    if (!frame_layout) {
+      continue;
+    }
+
+    // Next S_FMT with the size adjusted by VFPool.
+    gfx::Size adjusted_size(frame_layout->planes()[0].stride,
+                            frame_layout->coded_size().height());
+    base::Optional<struct v4l2_format> format =
+        SetFormatOnOutputQueue(format_fourcc, adjusted_size);
+    if (!format) {
+      num_output_planes_ = format->fmt.pix_mp.num_planes;
+      return frame_layout;
     }
   }
 
   // TODO(akahuang): Use ImageProcessor in this case.
   VLOGF(2) << "WARNING: Cannot find format that can create EGL image. "
            << "We need ImageProcessor to convert pixel format.";
-  return formats[0];
+  NOTIMPLEMENTED();
+  return base::nullopt;
 }
 
-bool V4L2SliceVideoDecoder::SetupOutputFormat(uint32_t output_format_fourcc) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
-  DVLOGF(3) << "output_format_fourcc = " << output_format_fourcc;
-
-  // Only set fourcc for output; resolution, etc., will come from the
-  // driver once surface_it extracts surface_it from the stream.
-  struct v4l2_format format;
-  memset(&format, 0, sizeof(format));
-  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  format.fmt.pix_mp.pixelformat = output_format_fourcc;
-  format.fmt.pix_mp.num_planes = num_output_planes_;
-  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
-    VPLOGF(1) << "Failed to call IOCTL to set output format.";
-    return false;
-  }
-  DCHECK_EQ(format.fmt.pix_mp.pixelformat, output_format_fourcc);
-
-  return true;
-}
-
-void V4L2SliceVideoDecoder::UpdateVideoFramePoolFormat(
+base::Optional<VideoFrameLayout>
+V4L2SliceVideoDecoder::UpdateVideoFramePoolFormat(
+    uint32_t output_format_fourcc,
+    const gfx::Size& size,
     const gfx::Rect& visible_rect) {
+  VideoPixelFormat output_format =
+      V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc);
+  if (output_format == PIXEL_FORMAT_UNKNOWN) {
+    return base::nullopt;
+  }
+  auto layout = VideoFrameLayout::Create(output_format, size);
+  if (!layout) {
+    VLOGF(1) << "Failed to create video frame layout.";
+    return base::nullopt;
+  }
+
   gfx::Size natural_size = GetNaturalSize(visible_rect, pixel_aspect_ratio_);
-  frame_pool_->SetFrameFormat(*frame_layout_, visible_rect, natural_size);
+  return frame_pool_->NegotiateFrameFormat(*layout, visible_rect, natural_size);
 }
 
 void V4L2SliceVideoDecoder::Reset(base::OnceClosure closure) {
@@ -705,38 +724,24 @@
   if (!StopStreamV4L2Queue())
     return false;
 
-  // Set the new resolution.
+  // Set output format with the new resolution.
   gfx::Size pic_size = avd_->GetPicSize();
   DCHECK(!pic_size.IsEmpty());
   DVLOGF(3) << "Change resolution to " << pic_size.width() << "x"
             << pic_size.height();
-  struct v4l2_format format;
-  memset(&format, 0, sizeof(format));
-  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
-    VLOGF(1) << "Failed getting output format.";
-    return false;
-  }
-  format.fmt.pix_mp.width = pic_size.width();
-  format.fmt.pix_mp.height = pic_size.height();
-  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
-    VLOGF(1) << "Failed setting resolution.";
+  auto frame_layout = SetupOutputFormat(pic_size, avd_->GetVisibleRect());
+  if (!frame_layout) {
+    VLOGF(1) << "No format is available with thew new resolution";
     return false;
   }
 
-  // Update frame layout.
-  gfx::Size coded_size(base::checked_cast<int>(format.fmt.pix_mp.width),
-                       base::checked_cast<int>(format.fmt.pix_mp.height));
+  auto coded_size = frame_layout->coded_size();
   DCHECK_EQ(coded_size.width() % 16, 0);
   DCHECK_EQ(coded_size.height() % 16, 0);
   if (!gfx::Rect(coded_size).Contains(gfx::Rect(pic_size))) {
     VLOGF(1) << "Got invalid adjusted coded size: " << coded_size.ToString();
     return false;
   }
-  frame_layout_ = VideoFrameLayout::Create(frame_layout_->format(), coded_size);
-  DCHECK(frame_layout_);
-
-  UpdateVideoFramePoolFormat(avd_->GetVisibleRect());
 
   // Allocate new output buffers.
   if (!output_queue_->DeallocateBuffers())
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.h b/media/gpu/v4l2/v4l2_slice_video_decoder.h
index 571d0c6..a552e4b1 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -5,6 +5,8 @@
 #ifndef MEDIA_GPU_V4L2_V4L2_SLICE_VIDEO_DECODER_H_
 #define MEDIA_GPU_V4L2_V4L2_SLICE_VIDEO_DECODER_H_
 
+#include <linux/videodev2.h>
+
 #include <map>
 #include <memory>
 #include <string>
@@ -135,15 +137,30 @@
   void InitializeTask(const VideoDecoderConfig& config,
                       InitCB init_cb,
                       const OutputCB& output_cb);
-  // Setup format for V4L2 input buffers.
+  // Setup format for input queue.
   bool SetupInputFormat(uint32_t input_format_fourcc);
-  // Negotiate the pixel output format with V4L2 device. Return fourcc of the
-  // pixel format that is supported by V4L2 device.
-  uint32_t NegotiateOutputFormat();
-  // Setup format for V4L2 output buffers.
-  bool SetupOutputFormat(uint32_t output_format_fourcc);
-  // Update the format of frames in |frame_pool_|.
-  void UpdateVideoFramePoolFormat(const gfx::Rect& visible_rect);
+
+  // Call VIDIOC_S_FMT with |format_fourcc| and |size|. Returns v4l2_format
+  // returned by VIDIOC_S_FMT on success, otherwise returns base::nullopt.
+  // This should be called only from SetupOutputFormat().
+  base::Optional<struct v4l2_format> SetFormatOnOutputQueue(
+      uint32_t format_fourcc,
+      const gfx::Size& size);
+  // Setup format for output queue. This function sets output format on output
+  // queue that is supported by a v4l2 driver, can be allocatable by
+  // VideoFramePool and can be composited by chrome. This also updates format
+  // in VideoFramePool. The returned VideoFrameLayout is one of VideoFrame that
+  // VideoFramePool will allocate. Returns base::nullopt on failure of if there
+  // is no format that satisfies the above conditions.
+  base::Optional<VideoFrameLayout> SetupOutputFormat(
+      const gfx::Size& size,
+      const gfx::Rect& visible_rect);
+  // Update the format of frames in |frame_pool_| with |output_format_fourcc|,
+  // |size| and |visible_rect|.
+  base::Optional<VideoFrameLayout> UpdateVideoFramePoolFormat(
+      uint32_t output_format_fourcc,
+      const gfx::Size& size,
+      const gfx::Rect& visible_rect);
 
   // Destroy on decoder thread.
   void DestroyTask();
diff --git a/media/gpu/v4l2/v4l2_vp8_accelerator.cc b/media/gpu/v4l2/v4l2_vp8_accelerator.cc
index 90a4a38d..4cf4b3e 100644
--- a/media/gpu/v4l2/v4l2_vp8_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_vp8_accelerator.cc
@@ -217,7 +217,7 @@
   memset(&ctrl, 0, sizeof(ctrl));
   ctrl.id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR;
   ctrl.size = sizeof(v4l2_frame_hdr);
-  ctrl.p_vp8_frame_hdr = &v4l2_frame_hdr;
+  ctrl.ptr = &v4l2_frame_hdr;
 
   struct v4l2_ext_controls ext_ctrls;
   memset(&ext_ctrls, 0, sizeof(ext_ctrls));
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 5364cd3..55ec03e7 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -468,7 +468,8 @@
   if (visible_rect_ != visible_rect) {
     visible_rect_ = visible_rect;
     gfx::Size natural_size = GetNaturalSize(visible_rect_, pixel_aspect_ratio_);
-    frame_pool_->SetFrameFormat(*frame_layout_, visible_rect_, natural_size);
+    frame_pool_->NegotiateFrameFormat(*frame_layout_, visible_rect_,
+                                      natural_size);
   }
 
   auto it = buffer_id_to_timestamp_.find(buffer_id);
@@ -527,7 +528,8 @@
       GfxBufferFormatToVideoPixelFormat(GetBufferFormat());
   frame_layout_ = VideoFrameLayout::Create(format, pic_size);
   DCHECK(frame_layout_);
-  frame_pool_->SetFrameFormat(*frame_layout_, visible_rect_, natural_size);
+  frame_pool_->NegotiateFrameFormat(*frame_layout_, visible_rect_,
+                                    natural_size);
   frame_pool_->SetMaxNumFrames(decoder_->GetRequiredNumOfPictures());
 
   // All pending decode operations will be completed before triggering a
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index 67ba27c..4d6c534c 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -112,10 +112,6 @@
 const double kDefaultSubsequentFramerateRatio = 0.1;
 // Tolerance factor for how encoded bitrate can differ from requested bitrate.
 const double kBitrateTolerance = 0.1;
-// Minimum required FPS throughput for the basic performance test.
-const uint32_t kMinPerfFPS = 30;
-// The frame size for 2160p (UHD 4K) video in pixels.
-const int k2160PSizeInPixels = 3840 * 2160;
 // Minimum (arbitrary) number of frames required to enforce bitrate requirements
 // over. Streams shorter than this may be too short to realistically require
 // an encoder to be able to converge to the requested bitrate over.
@@ -1523,7 +1519,6 @@
             bool save_to_file,
             unsigned int keyframe_period,
             bool force_bitrate,
-            bool test_perf,
             bool mid_stream_bitrate_switch,
             bool mid_stream_framerate_switch,
             bool verify_output,
@@ -1577,9 +1572,6 @@
   // short period.
   void FlushTimeout();
 
-  // Verify the minimum FPS requirement.
-  void VerifyMinFPS();
-
   // Verify that stream bitrate has been close to current_requested_bitrate_,
   // assuming current_framerate_ since the last time VerifyStreamProperties()
   // was called. Fail the test if |force_bitrate_| is true and the bitrate
@@ -1683,9 +1675,6 @@
   // time we checked bitrate.
   size_t encoded_stream_size_since_last_check_;
 
-  // If true, verify performance at the end of the test.
-  bool test_perf_;
-
   // Check the output frame quality of the encoder.
   bool verify_output_;
 
@@ -1734,7 +1723,6 @@
                      bool save_to_file,
                      unsigned int keyframe_period,
                      bool force_bitrate,
-                     bool test_perf,
                      bool mid_stream_bitrate_switch,
                      bool mid_stream_framerate_switch,
                      bool verify_output,
@@ -1757,7 +1745,6 @@
       current_requested_bitrate_(0),
       current_framerate_(0),
       encoded_stream_size_since_last_check_(0),
-      test_perf_(test_perf),
       verify_output_(verify_output),
       verify_output_timestamp_(verify_output_timestamp),
       requested_bitrate_(0),
@@ -2291,7 +2278,6 @@
     }
   } else if (num_encoded_frames_ == num_frames_to_encode_) {
     LogPerf();
-    VerifyMinFPS();
     VerifyStreamProperties();
     // We might receive the last frame before calling Flush(). In this case we
     // set the state to CS_FLUSHING first to bypass the state transition check.
@@ -2382,23 +2368,6 @@
   SetState(CS_ERROR);
 }
 
-void VEAClient::VerifyMinFPS() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (test_perf_) {
-    if (input_coded_size_.GetArea() >= k2160PSizeInPixels) {
-      // When |input_coded_size_| is 2160p or more, it is expected that the
-      // calculated FPS might be lower than kMinPerfFPS. Log as warning instead
-      // of failing the test in this case.
-      if (frames_per_second() < kMinPerfFPS) {
-        LOG(WARNING) << "Measured FPS: " << frames_per_second()
-                     << " is below min required: " << kMinPerfFPS << " FPS.";
-      }
-    } else {
-      EXPECT_GE(frames_per_second(), kMinPerfFPS);
-    }
-  }
-}
-
 void VEAClient::VerifyStreamProperties() {
   DCHECK(thread_checker_.CalledOnValidThread());
   LOG_ASSERT(num_frames_since_last_check_ > 0UL);
@@ -2707,7 +2676,6 @@
 // - Force a keyframe every n frames.
 // - Force bitrate; the actual required value is provided as a property
 //   of the input stream, because it depends on stream type/resolution/etc.
-// - If true, measure performance.
 // - If true, switch bitrate mid-stream.
 // - If true, switch framerate mid-stream.
 // - If true, verify the output frames of encoder.
@@ -2716,22 +2684,19 @@
 //   available for H264 encoder for now.
 class VideoEncodeAcceleratorTest
     : public ::testing::TestWithParam<
-          std::
-              tuple<int, bool, int, bool, bool, bool, bool, bool, bool, bool>> {
-};
+          std::tuple<int, bool, int, bool, bool, bool, bool, bool, bool>> {};
 
 TEST_P(VideoEncodeAcceleratorTest, TestSimpleEncode) {
   size_t num_concurrent_encoders = std::get<0>(GetParam());
   const bool save_to_file = std::get<1>(GetParam());
   const unsigned int keyframe_period = std::get<2>(GetParam());
   const bool force_bitrate = std::get<3>(GetParam());
-  const bool test_perf = std::get<4>(GetParam());
-  const bool mid_stream_bitrate_switch = std::get<5>(GetParam());
-  const bool mid_stream_framerate_switch = std::get<6>(GetParam());
+  const bool mid_stream_bitrate_switch = std::get<4>(GetParam());
+  const bool mid_stream_framerate_switch = std::get<5>(GetParam());
   const bool verify_output =
-      std::get<7>(GetParam()) || g_env->verify_all_output();
-  const bool verify_output_timestamp = std::get<8>(GetParam());
-  const bool force_level = std::get<9>(GetParam());
+      std::get<6>(GetParam()) || g_env->verify_all_output();
+  const bool verify_output_timestamp = std::get<7>(GetParam());
+  const bool force_level = std::get<8>(GetParam());
 
 #if defined(OS_CHROMEOS)
   if (ShouldSkipTest())
@@ -2783,7 +2748,7 @@
         std::make_unique<media::test::ClientStateNotification<ClientState>>());
     clients.push_back(std::make_unique<VEAClient>(
         g_env->test_streams_[test_stream_index].get(), notes.back().get(),
-        encoder_save_to_file, keyframe_period, force_bitrate, test_perf,
+        encoder_save_to_file, keyframe_period, force_bitrate,
         mid_stream_bitrate_switch, mid_stream_framerate_switch, verify_output,
         verify_output_timestamp, force_level));
 
@@ -2888,7 +2853,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false)));
 
 INSTANTIATE_TEST_SUITE_P(EncoderPerf,
@@ -2897,7 +2861,6 @@
                                                            false,
                                                            0,
                                                            false,
-                                                           true,
                                                            false,
                                                            false,
                                                            false,
@@ -2914,7 +2877,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false)));
 
 INSTANTIATE_TEST_SUITE_P(ForceBitrate,
@@ -2927,7 +2889,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false)));
 
 INSTANTIATE_TEST_SUITE_P(MidStreamParamSwitchBitrate,
@@ -2936,7 +2897,6 @@
                                                            false,
                                                            0,
                                                            true,
-                                                           false,
                                                            true,
                                                            false,
                                                            false,
@@ -2951,7 +2911,6 @@
                                                            0,
                                                            true,
                                                            false,
-                                                           false,
                                                            true,
                                                            false,
                                                            false,
@@ -2967,13 +2926,11 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false),
                                            std::make_tuple(3,
                                                            false,
                                                            0,
                                                            true,
-                                                           false,
                                                            true,
                                                            false,
                                                            false,
@@ -2989,7 +2946,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            true,
                                                            false)));
 
@@ -3003,7 +2959,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            true)));
 
 INSTANTIATE_TEST_SUITE_P(NoInputTest,
@@ -3025,7 +2980,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false),
                                            std::make_tuple(1,
                                                            true,
@@ -3033,7 +2987,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            true,
                                                            false,
                                                            false)));
@@ -3044,7 +2997,6 @@
                                                            false,
                                                            0,
                                                            false,
-                                                           true,
                                                            false,
                                                            false,
                                                            false,
@@ -3061,7 +3013,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false)));
 
 INSTANTIATE_TEST_SUITE_P(VerifyTimestamp,
@@ -3073,7 +3024,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            true,
                                                            false)));
 
@@ -3088,7 +3038,6 @@
                                                            false,
                                                            false,
                                                            false,
-                                                           false,
                                                            false)));
 #endif  // defined(OS_WIN)
 
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 4d39c9ae..2d470bae 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -58,6 +58,7 @@
 #include "third_party/angle/include/EGL/eglext.h"
 #include "ui/display/display_switches.h"
 #include "ui/gfx/color_space_win.h"
+#include "ui/gl/direct_composition_surface_win.h"
 #include "ui/gl/gl_angle_util_win.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
@@ -1558,9 +1559,8 @@
     RETURN_ON_HR_FAILURE(hr, "Failed to get stream attributes", false);
     out_attributes->SetUINT32(MF_SA_D3D11_BINDFLAGS,
                               D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DECODER);
-    // TODO(sunnyps): Find if we can always set resource sharing to disabled.
-    if (base::FeatureList::IsEnabled(
-            features::kDirectCompositionUseNV12DecodeSwapChain)) {
+    // TODO(sunnyps): Find if we can always set resource sharing to disabled
+    if (gl::DirectCompositionSurfaceWin::IsDecodeSwapChainSupported()) {
       // Decode swap chains do not support shared resources.
       out_attributes->SetUINT32(MF_SA_D3D11_SHARED, FALSE);
     } else {
diff --git a/media/test/data/eme_player.html b/media/test/data/eme_player.html
index fb7fe54c..929e71a 100644
--- a/media/test/data/eme_player.html
+++ b/media/test/data/eme_player.html
@@ -59,13 +59,39 @@
     var player;
     var heartbeatCount = 0;
 
+    function getTimeRanges(range) {
+      const result = [];
+      for (let i = 0; i < range.length; i++) {
+        result.push(`[${range.start(i)},${range.end(i)}]`);
+      }
+      return '[' + result.join(', ') + ']';
+    }
+
+    function getVideoStatus(video) {
+      if (video == null) {
+        return 'null';
+      }
+      const result = [];
+      result.push(`paused: ${video.paused}`);
+      result.push(`ended: ${video.ended}`);
+      result.push(`currentTime: ${video.currentTime}`);
+      result.push(`duration: ${video.duration}`);
+      result.push(`buffered: ${getTimeRanges(video.buffered)}`);
+      result.push(`played: ${getTimeRanges(video.played)}`);
+      if (video.error) {
+        result.push(`error: {${video.error.code},${video.error.message}}`);
+      }
+      return result.join(', ');
+    }
+
     function initApp() {
       testConfig = new TestConfig();
       testConfig.loadQueryParams();
       // Update document with test configuration values.
       emeApp = new EMEApp(testConfig);
       setInterval(function () {
-                      Utils.timeLog('heartbeat #' + ++heartbeatCount);
+                      Utils.timeLog('heartbeat #' + ++heartbeatCount +
+                                    ' video: ' + getVideoStatus(player.video));
                   }, 1000);
     }
 
@@ -117,13 +143,12 @@
       if (playTwice) {
         // Wait for the first play to complete.
         video.addEventListener('ended', onFirstPlayEnded);
-        video.play();
-        return;
+        return video.play();
       }
       // Ended should not fire before onTimeUpdate.
       video.addEventListener('ended', Utils.failTest);
       video.addEventListener('timeupdate', onTimeUpdate);
-      video.play();
+      return video.play();
     }
 
     function toggleDisplay(id) {
@@ -143,7 +168,7 @@
       emeApp.createPlayer()
           .then(function(p) {
               player = p;
-              play(player.video, testConfig.playTwice);
+              return play(player.video, testConfig.playTwice);
           }).catch(function(error) {
               Utils.timeLog(error);
               Utils.failTest('Unable to play video.');
diff --git a/media/test/data/eme_player_js/player_utils.js b/media/test/data/eme_player_js/player_utils.js
index bec9b91..5e2afa2 100644
--- a/media/test/data/eme_player_js/player_utils.js
+++ b/media/test/data/eme_player_js/player_utils.js
@@ -62,6 +62,15 @@
         }
         player.onMessage(message);
       });
+      mediaKeySession.addEventListener('keystatuseschange', function(e) {
+        const result = [];
+        for (let item of mediaKeySession.keyStatuses) {
+          result.push(`{kid:${
+              Utils.base64urlEncode(
+                  Utils.convertToUint8Array(item[0]))},status:${item[1]}}`);
+        }
+        Utils.timeLog('KeyStatusesChange: ' + result.join(','));
+      });
     }
 
     // Calls getStatusForPolicy() and returns a resolved promise if the result
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index c1b8d1a..ee2d5721 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -422,14 +422,6 @@
   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
@@ -438,7 +430,6 @@
                             DNS_CONFIG_WATCH_MAX);
   return watcher_->Watch();
 }
-#endif  // defined(OS_ANDROID) || defined(OS_CHROMEOS)
 
 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
   InvalidateConfig();
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 0f71a13..c3ab224 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -305,6 +305,7 @@
 
   deps = [
     "//base:i18n",
+    "//build:branding_buildflags",
     "//components/policy/core/common",
     "//crypto",
     "//google_apis",
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index 565e088..428da182 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -586,6 +586,17 @@
 void ClientSession::OnDesktopDisplayChanged(
     std::unique_ptr<protocol::VideoLayout> displays) {
   LOG(INFO) << "ClientSession::OnDesktopDisplayChanged";
+
+#if defined(OS_MACOSX)
+  // Don't send pixel based TrackLayout info to the client.  There are several
+  // bugs related to cursor position and desktop selection (for multi-mon) which
+  // prevent the user from interacting with their machine.  For now, we return
+  // early which will provide the same experience as the Chrome app used to and
+  // the iOS and Android apps currently do.
+  // TODO(crbug.com/987513): Investigate to figure out why the cursor offset and
+  // display selection issues are occurring and then re-enable this for MacOS.
+  return;
+#else
   // Scan display list to calculate the full desktop size.
   int min_x = 0;
   int max_x = 0;
@@ -676,6 +687,7 @@
   }
 
   connection_->client_stub()->SetVideoLayout(layout);
+#endif  // defined(OS_MACOSX)
 }
 
 void ClientSession::CreateFileTransferMessageHandler(
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index c813625..2cfa4c16 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -48,6 +48,15 @@
 #define MAYBE_RestoreEventState DISABLED_RestoreEventState
 #define MAYBE_MultiMonMouseMove DISABLED_MultiMonMouseMove
 #define MAYBE_DisconnectOnLocalInputTest DISABLED_DisconnectOnLocalInputTest
+#elif OS_MACOSX
+// TODO(crbug.com/987513): Reenable tests for MacOS.
+#define MAYBE_MultiMonMouseMove_SameSize DISABLED_MultiMonMouseMove_SameSize
+#define MAYBE_MultiMonMouseMove DISABLED_MultiMonMouseMove
+#define MAYBE_ClampMouseEvents DISABLED_ClampMouseEvents
+#define MAYBE_DisableInputs DISABLED_DisableInputs
+#define MAYBE_DisconnectOnLocalInputTest DISABLED_DisconnectOnLocalInputTest
+#define MAYBE_LocalInputTest DISABLED_LocalInputTest
+#define MAYBE_RestoreEventState DISABLED_RestoreEventState
 #else
 #define MAYBE_LocalInputTest LocalInputTest
 #define MAYBE_ClampMouseEvents ClampMouseEvents
diff --git a/remoting/host/host_attributes.cc b/remoting/host/host_attributes.cc
index 985c423..4922af35 100644
--- a/remoting/host/host_attributes.cc
+++ b/remoting/host/host_attributes.cc
@@ -13,6 +13,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
+#include "build/branding_buildflags.h"
 #include "build/build_config.h"
 
 #if defined(OS_WIN)
@@ -45,7 +46,7 @@
 inline constexpr bool IsChromeBranded() {
 #if defined(GOOGLE_CHROME_BUILD)
   return true;
-#elif defined(CHROMIUM_BUILD)
+#elif BUILDFLAG(CHROMIUM_BRANDING)
   return false;
 #else
   #error Only Chrome and Chromium brands are supported.
diff --git a/services/media_session/media_controller.cc b/services/media_session/media_controller.cc
index 6f8b54a6..80160c51 100644
--- a/services/media_session/media_controller.cc
+++ b/services/media_session/media_controller.cc
@@ -147,6 +147,7 @@
   media_controller_observer->MediaSessionInfoChanged(session_info_.Clone());
   media_controller_observer->MediaSessionMetadataChanged(session_metadata_);
   media_controller_observer->MediaSessionActionsChanged(session_actions_);
+  media_controller_observer->MediaSessionPositionChanged(session_position_);
 
   observers_.Add(std::move(media_controller_observer));
 }
@@ -180,6 +181,16 @@
   session_actions_ = actions;
 }
 
+void MediaController::MediaSessionPositionChanged(
+    const base::Optional<media_session::MediaPosition>& position) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (auto& observer : observers_)
+    observer->MediaSessionPositionChanged(position);
+
+  session_position_ = position;
+}
+
 void MediaController::MediaSessionImagesChanged(
     const base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>&
         images) {
@@ -295,6 +306,7 @@
     observer->MediaSessionMetadataChanged(base::nullopt);
     observer->MediaSessionActionsChanged(
         std::vector<mojom::MediaSessionAction>());
+    observer->MediaSessionPositionChanged(base::nullopt);
   }
 
   for (auto& holder : image_observers_)
@@ -323,6 +335,7 @@
   session_metadata_.reset();
   session_actions_.clear();
   session_images_.clear();
+  session_position_.reset();
 }
 
 }  // namespace media_session
diff --git a/services/media_session/media_controller.h b/services/media_session/media_controller.h
index 1f57285..62d68f35 100644
--- a/services/media_session/media_controller.h
+++ b/services/media_session/media_controller.h
@@ -62,6 +62,8 @@
   void MediaSessionImagesChanged(
       const base::flat_map<mojom::MediaSessionImageType,
                            std::vector<MediaImage>>& images) override;
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override;
 
   void SetMediaSession(AudioFocusRequest* session);
   void ClearMediaSession();
@@ -91,6 +93,9 @@
   // The current actions for |session_|.
   std::vector<mojom::MediaSessionAction> session_actions_;
 
+  // The current position for |session_|.
+  base::Optional<MediaPosition> session_position_;
+
   // The current images for |session_|.
   base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>
       session_images_;
diff --git a/services/media_session/media_controller_unittest.cc b/services/media_session/media_controller_unittest.cc
index 3411d89..d937247 100644
--- a/services/media_session/media_controller_unittest.cc
+++ b/services/media_session/media_controller_unittest.cc
@@ -892,6 +892,117 @@
   }
 }
 
+TEST_F(MediaControllerTest, ActiveController_Position_Observer_Empty) {
+  test::MockMediaSession media_session;
+  media_session.SetIsControllable(true);
+
+  base::Optional<MediaPosition> test_position;
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
+    RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::TestMediaControllerObserver observer(controller());
+    media_session.SimulatePositionChanged(test_position);
+    observer.WaitForEmptyPosition();
+  }
+}
+
+TEST_F(MediaControllerTest, ActiveController_Position_Observer_WithInfo) {
+  MediaPosition position(1 /* playback_rate */,
+                         base::TimeDelta::FromSeconds(600) /* duration */,
+                         base::TimeDelta::FromSeconds(300) /* position */);
+
+  test::MockMediaSession media_session;
+  media_session.SetIsControllable(true);
+
+  base::Optional<MediaPosition> test_position(position);
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
+    RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::TestMediaControllerObserver observer(controller());
+    media_session.SimulatePositionChanged(test_position);
+    observer.WaitForNonEmptyPosition();
+  }
+}
+
+TEST_F(MediaControllerTest, ActiveController_Position_AddObserver_Empty) {
+  test::MockMediaSession media_session;
+  media_session.SetIsControllable(true);
+
+  base::Optional<MediaPosition> test_position;
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
+    RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  media_session.SimulatePositionChanged(test_position);
+
+  {
+    test::TestMediaControllerObserver observer(controller());
+    observer.WaitForEmptyPosition();
+  }
+}
+
+TEST_F(MediaControllerTest, ActiveController_Position_AddObserver_WithInfo) {
+  MediaPosition position(1 /* playback_rate */,
+                         base::TimeDelta::FromSeconds(600) /* duration */,
+                         base::TimeDelta::FromSeconds(300) /* position */);
+
+  test::MockMediaSession media_session;
+  media_session.SetIsControllable(true);
+
+  base::Optional<MediaPosition> test_position(position);
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
+    RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  media_session.SimulatePositionChanged(test_position);
+
+  {
+    test::TestMediaControllerObserver observer(controller());
+    observer.WaitForNonEmptyPosition();
+  }
+}
+
+TEST_F(MediaControllerTest, ActiveController_Position_Observer_Abandoned) {
+  MediaPosition position(1 /* playback_rate */,
+                         base::TimeDelta::FromSeconds(600) /* duration */,
+                         base::TimeDelta::FromSeconds(300) /* position */);
+
+  test::MockMediaSession media_session;
+  media_session.SetIsControllable(true);
+
+  base::Optional<MediaPosition> test_position(position);
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
+    RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  media_session.SimulatePositionChanged(test_position);
+  media_session.AbandonAudioFocusFromClient();
+
+  {
+    test::TestMediaControllerObserver observer(controller());
+    observer.WaitForEmptyPosition();
+  }
+}
+
 TEST_F(MediaControllerTest, ActiveController_Observer_Abandoned) {
   test::MockMediaSession media_session;
   media_session.SetIsControllable(true);
@@ -906,11 +1017,12 @@
     test::TestMediaControllerObserver observer(controller());
     media_session.AbandonAudioFocusFromClient();
 
-    // We should see empty info, metadata and actions flushed since the active
-    // controller is no longer bound to a media session.
+    // We should see empty info, metadata, actions, and position flushed since
+    // the active controller is no longer bound to a media session.
     observer.WaitForEmptyInfo();
     observer.WaitForEmptyMetadata();
     observer.WaitForEmptyActions();
+    observer.WaitForEmptyPosition();
   }
 }
 
@@ -929,11 +1041,12 @@
   {
     test::TestMediaControllerObserver observer(controller());
 
-    // We should see empty info, metadata and actions since the active
-    // controller is no longer bound to a media session.
+    // We should see empty info, metadata, actions, and position since the
+    // active controller is no longer bound to a media session.
     observer.WaitForEmptyInfo();
     observer.WaitForEmptyMetadata();
     observer.WaitForEmptyActions();
+    observer.WaitForEmptyPosition();
   }
 }
 
diff --git a/services/media_session/public/cpp/media_position.h b/services/media_session/public/cpp/media_position.h
index 6bbabc6..f59467f6 100644
--- a/services/media_session/public/cpp/media_position.h
+++ b/services/media_session/public/cpp/media_position.h
@@ -9,6 +9,16 @@
 #include "base/gtest_prod_util.h"
 #include "base/time/time.h"
 
+namespace IPC {
+template <class P>
+struct ParamTraits;
+}
+
+namespace ipc_fuzzer {
+template <class T>
+struct FuzzTraits;
+}  // namespace ipc_fuzzer
+
 namespace mojo {
 template <typename DataViewType, typename T>
 struct StructTraits;
@@ -50,6 +60,8 @@
   std::string ToString() const;
 
  private:
+  friend struct IPC::ParamTraits<media_session::MediaPosition>;
+  friend struct ipc_fuzzer::FuzzTraits<media_session::MediaPosition>;
   friend struct mojo::StructTraits<mojom::MediaPositionDataView, MediaPosition>;
   friend class MediaPositionTest;
   FRIEND_TEST_ALL_PREFIXES(MediaPositionTest, TestPositionUpdated);
diff --git a/services/media_session/public/cpp/test/mock_media_session.cc b/services/media_session/public/cpp/test/mock_media_session.cc
index c960a9ee..d8c3e21a 100644
--- a/services/media_session/public/cpp/test/mock_media_session.cc
+++ b/services/media_session/public/cpp/test/mock_media_session.cc
@@ -75,6 +75,19 @@
   }
 }
 
+void MockMediaSessionMojoObserver::MediaSessionPositionChanged(
+    const base::Optional<media_session::MediaPosition>& position) {
+  session_position_ = position;
+
+  if (waiting_for_empty_position_ && !position.has_value()) {
+    run_loop_->Quit();
+    waiting_for_empty_position_ = false;
+  } else if (waiting_for_non_empty_position_ && position.has_value()) {
+    run_loop_->Quit();
+    waiting_for_non_empty_position_ = false;
+  }
+}
+
 void MockMediaSessionMojoObserver::WaitForState(
     mojom::MediaSessionInfo::SessionState wanted_state) {
   if (session_info_ && session_info_->state == wanted_state)
@@ -144,6 +157,24 @@
   StartWaiting();
 }
 
+void MockMediaSessionMojoObserver::WaitForEmptyPosition() {
+  // |session_position_| is doubly wrapped in base::Optional so we must check
+  // both values.
+  if (session_position_.has_value() && !session_position_->has_value())
+    return;
+
+  waiting_for_empty_position_ = true;
+  StartWaiting();
+}
+
+void MockMediaSessionMojoObserver::WaitForNonEmptyPosition() {
+  if (session_position_.has_value() && session_position_->has_value())
+    return;
+
+  waiting_for_non_empty_position_ = true;
+  StartWaiting();
+}
+
 void MockMediaSessionMojoObserver::StartWaiting() {
   DCHECK(!run_loop_);
 
@@ -340,6 +371,13 @@
   }
 }
 
+void MockMediaSession::SimulatePositionChanged(
+    const base::Optional<MediaPosition>& position) {
+  for (auto& observer : observers_) {
+    observer->MediaSessionPositionChanged(position);
+  }
+}
+
 void MockMediaSession::ClearAllImages() {
   images_.clear();
 
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h
index ea3675b..995a65a 100644
--- a/services/media_session/public/cpp/test/mock_media_session.h
+++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -46,6 +46,8 @@
   void MediaSessionImagesChanged(
       const base::flat_map<mojom::MediaSessionImageType,
                            std::vector<MediaImage>>& images) override;
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override;
 
   void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state);
   void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state);
@@ -61,6 +63,9 @@
   void WaitForExpectedImagesOfType(mojom::MediaSessionImageType type,
                                    const std::vector<MediaImage>& images);
 
+  void WaitForEmptyPosition();
+  void WaitForNonEmptyPosition();
+
   const mojom::MediaSessionInfoPtr& session_info() const {
     return session_info_;
   }
@@ -74,6 +79,10 @@
     return *session_actions_;
   }
 
+  const base::Optional<base::Optional<MediaPosition>>& session_position() {
+    return session_position_;
+  }
+
  private:
   void StartWaiting();
 
@@ -83,6 +92,9 @@
   base::Optional<
       base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>>
       session_images_;
+  base::Optional<base::Optional<MediaPosition>> session_position_;
+  bool waiting_for_empty_position_ = false;
+  bool waiting_for_non_empty_position_ = false;
 
   base::Optional<MediaMetadata> expected_metadata_;
   base::Optional<std::set<mojom::MediaSessionAction>> expected_actions_;
@@ -152,6 +164,7 @@
   void FlushForTesting();
 
   void SimulateMetadataChanged(const base::Optional<MediaMetadata>& metadata);
+  void SimulatePositionChanged(const base::Optional<MediaPosition>& position);
 
   void ClearAllImages();
   void SetImagesOfType(mojom::MediaSessionImageType type,
diff --git a/services/media_session/public/cpp/test/test_media_controller.cc b/services/media_session/public/cpp/test/test_media_controller.cc
index 11b3462..7d3fb8c 100644
--- a/services/media_session/public/cpp/test/test_media_controller.cc
+++ b/services/media_session/public/cpp/test/test_media_controller.cc
@@ -106,6 +106,19 @@
   }
 }
 
+void TestMediaControllerObserver::MediaSessionPositionChanged(
+    const base::Optional<media_session::MediaPosition>& position) {
+  session_position_ = position;
+
+  if (waiting_for_empty_position_ && !position.has_value()) {
+    run_loop_->Quit();
+    waiting_for_empty_position_ = false;
+  } else if (waiting_for_non_empty_position_ && position.has_value()) {
+    run_loop_->Quit();
+    waiting_for_non_empty_position_ = false;
+  }
+}
+
 void TestMediaControllerObserver::WaitForState(
     mojom::MediaSessionInfo::SessionState wanted_state) {
   if (session_info_ && session_info()->state == wanted_state)
@@ -162,6 +175,24 @@
   StartWaiting();
 }
 
+void TestMediaControllerObserver::WaitForEmptyPosition() {
+  // |session_position_| is doubly wrapped in base::Optional so we must check
+  // both values.
+  if (session_position_.has_value() && !session_position_->has_value())
+    return;
+
+  waiting_for_empty_position_ = true;
+  StartWaiting();
+}
+
+void TestMediaControllerObserver::WaitForNonEmptyPosition() {
+  if (session_position_.has_value() && session_position_->has_value())
+    return;
+
+  waiting_for_non_empty_position_ = true;
+  StartWaiting();
+}
+
 void TestMediaControllerObserver::WaitForSession(
     const base::Optional<base::UnguessableToken>& request_id) {
   if (session_request_id_.has_value() && session_request_id_ == request_id)
@@ -197,6 +228,10 @@
   ++resume_count_;
 }
 
+void TestMediaController::Stop() {
+  ++stop_count_;
+}
+
 void TestMediaController::ToggleSuspendResume() {
   ++toggle_suspend_resume_count_;
 }
diff --git a/services/media_session/public/cpp/test/test_media_controller.h b/services/media_session/public/cpp/test/test_media_controller.h
index 66fe8e9..b4680b5 100644
--- a/services/media_session/public/cpp/test/test_media_controller.h
+++ b/services/media_session/public/cpp/test/test_media_controller.h
@@ -66,6 +66,8 @@
       const std::vector<mojom::MediaSessionAction>& actions) override;
   void MediaSessionChanged(
       const base::Optional<base::UnguessableToken>& request_id) override;
+  void MediaSessionPositionChanged(
+      const base::Optional<media_session::MediaPosition>& position) override;
 
   void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state);
   void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state);
@@ -78,6 +80,9 @@
   void WaitForExpectedActions(
       const std::set<mojom::MediaSessionAction>& actions);
 
+  void WaitForEmptyPosition();
+  void WaitForNonEmptyPosition();
+
   void WaitForSession(const base::Optional<base::UnguessableToken>& request_id);
 
   const mojom::MediaSessionInfoPtr& session_info() const {
@@ -93,6 +98,10 @@
     return *session_actions_;
   }
 
+  const base::Optional<base::Optional<MediaPosition>>& session_position() {
+    return session_position_;
+  }
+
  private:
   void StartWaiting();
 
@@ -100,6 +109,9 @@
   base::Optional<base::Optional<MediaMetadata>> session_metadata_;
   base::Optional<std::set<mojom::MediaSessionAction>> session_actions_;
   base::Optional<base::Optional<base::UnguessableToken>> session_request_id_;
+  base::Optional<base::Optional<MediaPosition>> session_position_;
+  bool waiting_for_empty_position_ = false;
+  bool waiting_for_non_empty_position_ = false;
 
   base::Optional<MediaMetadata> expected_metadata_;
   base::Optional<std::set<mojom::MediaSessionAction>> expected_actions_;
@@ -128,7 +140,7 @@
   // mojom::MediaController:
   void Suspend() override;
   void Resume() override;
-  void Stop() override {}
+  void Stop() override;
   void ToggleSuspendResume() override;
   void AddObserver(
       mojo::PendingRemote<mojom::MediaControllerObserver> observer) override;
@@ -149,6 +161,7 @@
 
   int suspend_count() const { return suspend_count_; }
   int resume_count() const { return resume_count_; }
+  int stop_count() const { return stop_count_; }
   int add_observer_count() const { return add_observer_count_; }
   int previous_track_count() const { return previous_track_count_; }
   int next_track_count() const { return next_track_count_; }
@@ -164,6 +177,7 @@
   int toggle_suspend_resume_count_ = 0;
   int suspend_count_ = 0;
   int resume_count_ = 0;
+  int stop_count_ = 0;
   int add_observer_count_ = 0;
   int previous_track_count_ = 0;
   int next_track_count_ = 0;
diff --git a/services/media_session/public/mojom/media_controller.mojom b/services/media_session/public/mojom/media_controller.mojom
index 1c810d10..6c6c413 100644
--- a/services/media_session/public/mojom/media_controller.mojom
+++ b/services/media_session/public/mojom/media_controller.mojom
@@ -100,6 +100,13 @@
   // Called when the bound media session changes. This tells the observer the
   // |request_id| of the new session of null if it is not bound to a session.
   MediaSessionChanged(mojo_base.mojom.UnguessableToken? request_id);
+
+  // Called when the position of the bound media session has changed. If
+  // |position| is empty then the media session does not have a position or
+  // the controller is no longer bound to a media session. Position is updated
+  // anytime the position state (playback rate, duration) is changed or the
+  // media is seeked.
+  MediaSessionPositionChanged(MediaPosition? position);
 };
 
 // The observer for observing when images associated with a media controller
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index ae3e243..ab3bbfb 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -127,7 +127,7 @@
 };
 
 // The observer for observing media session events.
-// Next Method ID: 4
+// Next Method ID: 5
 interface MediaSessionObserver {
   // Call when the info associated with the session changed.
   MediaSessionInfoChanged@0(MediaSessionInfo info);
@@ -144,6 +144,9 @@
   // Called when the images associated with a media session change.
   MediaSessionImagesChanged@3(
       map<MediaSessionImageType, array<MediaImage>> images);
+
+  // Called when the position associated with the session has changed.
+  MediaSessionPositionChanged@4(MediaPosition? position);
 };
 
 // A MediaSession manages the media session and audio focus for a given
diff --git a/services/metrics/public/cpp/ukm_recorder.h b/services/metrics/public/cpp/ukm_recorder.h
index b4cb17e..239643d4 100644
--- a/services/metrics/public/cpp/ukm_recorder.h
+++ b/services/metrics/public/cpp/ukm_recorder.h
@@ -18,6 +18,7 @@
 #include "url/gurl.h"
 
 class PermissionUmaUtil;
+class WebApkUkmRecorder;
 
 namespace blink {
 class Document;
@@ -75,6 +76,13 @@
   friend metrics::UkmRecorderInterface;
   friend PermissionUmaUtil;
 
+  // WebApkUkmRecorder records metrics about installed Webapps. Instead of using
+  // the current main frame URL, we want to record the URL of the Webapp
+  // manifest which identifies the current app. Therefore, WebApkUkmRecorder
+  // needs to be a friend so that it can access the private UpdateSourceURL()
+  // method.
+  friend WebApkUkmRecorder;
+
   // Associates the SourceId with a URL. Most UKM recording code should prefer
   // to use a shared SourceId that is already associated with a URL, rather
   // than using this API directly. New uses of this API must be audited to
diff --git a/services/shape_detection/OWNERS b/services/shape_detection/OWNERS
index 4009628..aee8252f 100644
--- a/services/shape_detection/OWNERS
+++ b/services/shape_detection/OWNERS
@@ -1,7 +1,4 @@
-reillyg@chromium.org
-
-# Original (legacy) owner.
-mcasas@chromium.org
+file://third_party/blink/renderer/modules/shapedetection/OWNERS
 
 # COMPONENT: Blink>ShapeDetection
 # TEAM: device-dev@chromium.org
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index 0dfdb64d..35d81a8 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -115,21 +115,23 @@
     "coordinator_test_util.cc",
     "coordinator_test_util.h",
     "coordinator_unittest.cc",
+    "perfetto/consumer_host_unittest.cc",
+    "perfetto/json_trace_exporter_unittest.cc",
+    "perfetto/perfetto_integration_unittest.cc",
+    "perfetto/perfetto_tracing_coordinator_unittest.cc",
+    "perfetto/test_utils.cc",
+    "perfetto/test_utils.h",
+    "perfetto/track_event_json_exporter_unittest.cc",
+    "public/cpp/perfetto/task_runner_unittest.cc",
+    "public/cpp/perfetto/trace_event_data_source_unittest.cc",
+    "public/cpp/perfetto/traced_value_proto_writer_unittest.cc",
+    "public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc",
     "public/cpp/trace_event_agent_unittest.cc",
     "recorder_unittest.cc",
     "test_util.cc",
     "test_util.h",
   ]
 
-  if (is_mac || is_linux || is_android || is_fuchsia) {
-    sources += [
-      "public/cpp/perfetto/task_runner_unittest.cc",
-      "public/cpp/perfetto/trace_event_data_source_unittest.cc",
-      "public/cpp/perfetto/traced_value_proto_writer_unittest.cc",
-      "public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc",
-    ]
-  }
-
   if (!is_android) {
     sources += [ "tracing_service_unittest.cc" ]
   }
@@ -145,29 +147,14 @@
     "//services/tracing:lib",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/perfetto/include/perfetto/protozero:protozero",
+    "//third_party/perfetto/protos/perfetto/common:lite",
+    "//third_party/perfetto/protos/perfetto/trace:lite",
+    "//third_party/perfetto/protos/perfetto/trace/chrome:lite",
+    "//third_party/perfetto/protos/perfetto/trace/interned_data:lite",
+    "//third_party/perfetto/protos/perfetto/trace/track_event:lite",
   ]
 
-  if (is_mac || is_linux || is_android || is_fuchsia) {
-    sources += [
-      "perfetto/consumer_host_unittest.cc",
-      "perfetto/json_trace_exporter_unittest.cc",
-      "perfetto/perfetto_integration_unittest.cc",
-      "perfetto/perfetto_tracing_coordinator_unittest.cc",
-      "perfetto/test_utils.cc",
-      "perfetto/test_utils.h",
-      "perfetto/track_event_json_exporter_unittest.cc",
-    ]
-
-    deps += [
-      "//third_party/perfetto/include/perfetto/protozero:protozero",
-      "//third_party/perfetto/protos/perfetto/common:lite",
-      "//third_party/perfetto/protos/perfetto/trace:lite",
-      "//third_party/perfetto/protos/perfetto/trace/chrome:lite",
-      "//third_party/perfetto/protos/perfetto/trace/interned_data:lite",
-      "//third_party/perfetto/protos/perfetto/trace/track_event:lite",
-    ]
-  }
-
   if (is_android) {
     sources += [
       "perfetto/system_perfetto_unittest.cc",
diff --git a/services/tracing/perfetto/system_perfetto_unittest.cc b/services/tracing/perfetto/system_perfetto_unittest.cc
index 34e6051..524ed797 100644
--- a/services/tracing/perfetto/system_perfetto_unittest.cc
+++ b/services/tracing/perfetto/system_perfetto_unittest.cc
@@ -220,9 +220,11 @@
 
   // Set up the producer to talk to the system.
   base::RunLoop system_data_source_enabled_runloop;
+  base::RunLoop system_data_source_disabled_runloop;
   auto system_producer = CreateMockAndroidSystemProducer(
       system_service.get(),
-      /* num_data_sources = */ 1, &system_data_source_enabled_runloop);
+      /* num_data_sources = */ 1, &system_data_source_enabled_runloop,
+      &system_data_source_disabled_runloop);
 
   // Start a system trace, and wait on the Data Source being started.
   base::RunLoop system_no_more_packets_runloop;
@@ -236,9 +238,15 @@
   system_data_source_enabled_runloop.Run();
 
   // Post a task to ensure we stop the trace after the data is written.
+  base::RunLoop stop_tracing;
   PerfettoTracedProcess::GetTaskRunner()->PostTask(
-      [&]() { system_consumer.StopTracing(); });
+      [&system_consumer, &stop_tracing]() {
+        system_consumer.StopTracing();
+        stop_tracing.Quit();
+      });
+  stop_tracing.Run();
 
+  system_data_source_disabled_runloop.Run();
   system_no_more_packets_runloop.Run();
 
   EXPECT_EQ(1u, system_consumer.received_packets());
@@ -447,9 +455,9 @@
   system_data_source_disabled_runloop.Run();
   system_no_more_packets_runloop.Run();
 
-  // Once we StopTracing() on the local trace the system tracing system will
-  // come back. So set new enabled and disabled RunLoops for the system
-  // producer.
+  // Once we StopTracing() on the system trace the we want to make sure a new
+  // local trace can start smoothly. So set new enabled and disabled RunLoops
+  // for the system producer.
   base::RunLoop local_data_source_reenabled_runloop;
   base::RunLoop local_data_source_redisabled_runloop;
   local_producer_client->SetAgentEnabledCallback(
@@ -678,10 +686,11 @@
         });
 
     base::RunLoop system_data_source_enabled_runloop;
+    base::RunLoop system_data_source_disabled_runloop;
     auto system_producer = CreateMockAndroidSystemProducer(
         system_ptr,
         /* num_data_sources = */ 1, &system_data_source_enabled_runloop,
-        /* system_data_source_disabled_runloop = */ nullptr, check_sdk_level);
+        &system_data_source_disabled_runloop, check_sdk_level);
 
     if (!check_sdk_level) {
       system_data_source_enabled_runloop.Run();
@@ -689,9 +698,19 @@
 
     // Post the task to ensure that the data will have been written and
     // committed if any tracing is being done.
+    base::RunLoop stop_tracing;
     PerfettoTracedProcess::GetTaskRunner()->PostTask(
-        [&system_consumer]() { system_consumer.StopTracing(); });
+        [&system_consumer, &stop_tracing]() {
+          system_consumer.StopTracing();
+          stop_tracing.Quit();
+        });
+    stop_tracing.Run();
+
+    if (!check_sdk_level) {
+      system_data_source_disabled_runloop.Run();
+    }
     system_no_more_packets_runloop.Run();
+
     PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
     return system_consumer.received_packets();
   };
diff --git a/services/tracing/perfetto/system_test_utils.cc b/services/tracing/perfetto/system_test_utils.cc
index 830adfb..df1689f 100644
--- a/services/tracing/perfetto/system_test_utils.cc
+++ b/services/tracing/perfetto/system_test_utils.cc
@@ -21,11 +21,11 @@
       task_runner_(std::make_unique<PerfettoTaskRunner>(
           base::SequencedTaskRunnerHandle::Get())) {
   service_ = perfetto::ServiceIPCHost::CreateInstance(task_runner_.get());
-  DCHECK(service_);
+  CHECK(service_);
   unlink(producer_socket.c_str());
   unlink(consumer_socket.c_str());
   bool succeeded = service_->Start(producer_.c_str(), consumer_.c_str());
-  DCHECK(succeeded);
+  CHECK(succeeded);
 }
 
 MockSystemService::~MockSystemService() {
@@ -73,7 +73,7 @@
   // See comment in the constructor.
   auto client = PerfettoTracedProcess::Get()->SetSystemProducerForTesting(
       std::move(old_producer_));
-  DCHECK(client.get() == this);
+  CHECK(client.get() == this);
   client.release();
 }
 
@@ -81,7 +81,7 @@
     perfetto::DataSourceInstanceID id,
     const perfetto::DataSourceConfig& data_source_config) {
   AndroidSystemProducer::StartDataSource(id, data_source_config);
-  DCHECK_LT(num_data_sources_active_, num_data_sources_expected_);
+  CHECK_LT(num_data_sources_active_, num_data_sources_expected_);
   if (data_source_enabled_callback_ &&
       ++num_data_sources_active_ == num_data_sources_expected_) {
     std::move(data_source_enabled_callback_).Run();
@@ -91,7 +91,7 @@
 void MockAndroidSystemProducer::StopDataSource(
     perfetto::DataSourceInstanceID id) {
   AndroidSystemProducer::StopDataSource(id);
-  DCHECK_GT(num_data_sources_active_, 0u);
+  CHECK_GT(num_data_sources_active_, 0u);
   if (data_source_disabled_callback_ && --num_data_sources_active_ == 0) {
     std::move(data_source_disabled_callback_).Run();
   }
diff --git a/services/tracing/perfetto/test_utils.cc b/services/tracing/perfetto/test_utils.cc
index 8f54932..ffa0e51 100644
--- a/services/tracing/perfetto/test_utils.cc
+++ b/services/tracing/perfetto/test_utils.cc
@@ -69,7 +69,7 @@
 }
 
 void TestDataSource::StopTracing(base::OnceClosure stop_complete_callback) {
-  DCHECK(producer_);
+  CHECK(producer_);
   producer_ = nullptr;
   std::move(stop_complete_callback).Run();
 }
@@ -118,7 +118,7 @@
   ProducerClient::StartDataSource(id, std::move(data_source_config),
                                   std::move(callback));
 
-  DCHECK_LT(num_data_sources_active_, num_data_sources_expected_);
+  CHECK_LT(num_data_sources_active_, num_data_sources_expected_);
   if (client_enabled_callback_ &&
       ++num_data_sources_active_ == num_data_sources_expected_) {
     std::move(client_enabled_callback_).Run();
@@ -129,7 +129,7 @@
                                         StopDataSourceCallback callback) {
   ProducerClient::StopDataSource(id, std::move(callback));
 
-  DCHECK_GT(num_data_sources_active_, 0u);
+  CHECK_GT(num_data_sources_active_, 0u);
   if (client_disabled_callback_ && --num_data_sources_active_ == 0) {
     std::move(client_disabled_callback_).Run();
   }
@@ -165,18 +165,21 @@
                            PacketReceivedCallback packet_received_callback)
     : packet_received_callback_(packet_received_callback),
       data_source_names_(data_source_names) {
-  DCHECK(!data_source_names_.empty());
+  CHECK(!data_source_names_.empty());
   consumer_endpoint_ = service->ConnectConsumer(this, /*uid=*/0);
+  CHECK(consumer_endpoint_);
 }
 
 MockConsumer::~MockConsumer() = default;
 
 void MockConsumer::ReadBuffers() {
+  CHECK(consumer_endpoint_);
   consumer_endpoint_->ReadBuffers();
 }
 
 void MockConsumer::StopTracing() {
   ReadBuffers();
+  CHECK(consumer_endpoint_);
   consumer_endpoint_->DisableTracing();
 }
 
@@ -189,10 +192,12 @@
     ds_config->set_target_buffer(0);
   }
 
+  CHECK(consumer_endpoint_);
   consumer_endpoint_->EnableTracing(trace_config);
 }
 
 void MockConsumer::FreeBuffers() {
+  CHECK(consumer_endpoint_);
   consumer_endpoint_->FreeBuffers();
 }
 
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
index 1738cef..68fc96a 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
@@ -29,7 +29,7 @@
 //   in PerfettoProducer::StartDataSource.
 class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final {
  public:
-  class DataSourceBase {
+  class COMPONENT_EXPORT(TRACING_CPP) DataSourceBase {
    public:
     explicit DataSourceBase(const std::string& name);
     virtual ~DataSourceBase();
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
index bf334e0..d6045e05 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
@@ -14,6 +14,7 @@
 #include "base/process/process.h"
 #include "base/process/process_handle.h"
 #include "base/profiler/stack_sampling_profiler.h"
+#include "base/strings/strcat.h"
 #include "base/task/thread_pool/thread_pool.h"
 #include "base/threading/thread_local.h"
 #include "build/build_config.h"
@@ -60,25 +61,41 @@
 
   void RegisterProfiler(TracingSamplerProfiler* profiler) {
     base::AutoLock lock(lock_);
-    if (!profilers_.insert(profiler).second || !is_started_) {
+    if (!profilers_.insert(profiler).second) {
       return;
     }
 
-    profiler->StartTracing(
-        perfetto_producer_->CreateTraceWriter(
-            data_source_config_.target_buffer()),
-        data_source_config_.chrome_config().privacy_filtering_enabled());
+    if (is_started_) {
+      profiler->StartTracing(
+          perfetto_producer_->CreateTraceWriter(
+              data_source_config_.target_buffer()),
+          data_source_config_.chrome_config().privacy_filtering_enabled());
+    } else if (is_startup_tracing_) {
+      profiler->StartTracing(nullptr, /*should_enable_filtering=*/true);
+    }
   }
 
   void UnregisterProfiler(TracingSamplerProfiler* profiler) {
     base::AutoLock lock(lock_);
-    if (!profilers_.erase(profiler) || !is_started_) {
+    if (!profilers_.erase(profiler) || !(is_started_ || is_startup_tracing_)) {
       return;
     }
 
     profiler->StopTracing();
   }
 
+  void SetupStartupTracing() {
+    base::AutoLock lock(lock_);
+    if (is_started_) {
+      return;
+    }
+    is_startup_tracing_ = true;
+    for (auto* profiler : profilers_) {
+      // Enable filtering for startup tracing always to be safe.
+      profiler->StartTracing(nullptr, /*should_enable_filtering=*/true);
+    }
+  }
+
   // PerfettoTracedProcess::DataSourceBase implementation, called by
   // ProducerClient.
   void StartTracing(
@@ -87,6 +104,7 @@
     base::AutoLock lock(lock_);
     DCHECK(!is_started_);
     is_started_ = true;
+    is_startup_tracing_ = false;
     perfetto_producer_ = producer;
     data_source_config_ = data_source_config;
 
@@ -104,6 +122,7 @@
     base::AutoLock lock(lock_);
     DCHECK(is_started_);
     is_started_ = false;
+    is_startup_tracing_ = false;
     perfetto_producer_ = nullptr;
 
     for (auto* profiler : profilers_) {
@@ -128,6 +147,7 @@
  private:
   base::Lock lock_;  // Protects subsequent members.
   std::set<TracingSamplerProfiler*> profilers_;
+  bool is_startup_tracing_ = false;
   bool is_started_ = false;
   PerfettoProducer* perfetto_producer_ = nullptr;
   perfetto::DataSourceConfig data_source_config_;
@@ -150,6 +170,18 @@
 
 }  // namespace
 
+TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
+    base::TimeTicks ts,
+    std::vector<base::Frame>&& s)
+    : timestamp(ts), sample(std::move(s)) {}
+
+TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::
+    ~BufferedSample() = default;
+
+TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
+    TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&& other)
+    : BufferedSample(other.timestamp, std::move(other.sample)) {}
+
 TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder(
     base::PlatformThreadId sampled_thread_id,
     std::unique_ptr<perfetto::TraceWriter> trace_writer,
@@ -181,6 +213,27 @@
 
 void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
     std::vector<base::Frame> frames) {
+  base::AutoLock l(trace_writer_lock_);
+  if (!trace_writer_) {
+    if (buffered_samples_.size() < kMaxBufferedSamples) {
+      buffered_samples_.emplace_back(
+          BufferedSample(TRACE_TIME_TICKS_NOW(), std::move(frames)));
+    }
+    return;
+  }
+  if (!buffered_samples_.empty()) {
+    for (const auto& sample : buffered_samples_) {
+      WriteSampleToTrace(sample);
+    }
+    buffered_samples_.clear();
+  }
+  WriteSampleToTrace(BufferedSample(TRACE_TIME_TICKS_NOW(), std::move(frames)));
+}
+
+void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
+    const TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&
+        sample) {
+  const auto& frames = sample.sample;
   auto reset_id =
       TracingSamplerProfilerDataSource::GetIncrementalStateResetID();
   if (reset_id != last_incremental_state_reset_id_) {
@@ -205,13 +258,12 @@
     auto* thread_descriptor = trace_packet->set_thread_descriptor();
     thread_descriptor->set_pid(base::GetCurrentProcId());
     thread_descriptor->set_tid(sampled_thread_id_);
-    last_timestamp_ = TRACE_TIME_TICKS_NOW();
+    last_timestamp_ = sample.timestamp;
     thread_descriptor->set_reference_timestamp_us(
         last_timestamp_.since_origin().InMicroseconds());
     reset_incremental_state_ = false;
   }
 
-  auto time_now = TRACE_TIME_TICKS_NOW();
   int32_t current_process_priority = base::Process::Current().GetPriority();
   if (current_process_priority != last_emitted_process_priority_) {
     last_emitted_process_priority_ = current_process_priority;
@@ -226,8 +278,14 @@
   auto* streaming_profile_packet = trace_packet->set_streaming_profile_packet();
   streaming_profile_packet->add_callstack_iid(callstack_id);
   streaming_profile_packet->add_timestamp_delta_us(
-      (time_now - last_timestamp_).InMicroseconds());
-  last_timestamp_ = time_now;
+      (sample.timestamp - last_timestamp_).InMicroseconds());
+  last_timestamp_ = sample.timestamp;
+}
+
+void TracingSamplerProfiler::TracingProfileBuilder::SetTraceWriter(
+    std::unique_ptr<perfetto::TraceWriter> writer) {
+  base::AutoLock l(trace_writer_lock_);
+  trace_writer_ = std::move(writer);
 }
 
 InterningID
@@ -301,6 +359,22 @@
     }
 #endif
 
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    // Linux ELF module IDs are 160bit integers, which we need to mangle
+    // down to 128bit integers to match the id that Breakpad outputs.
+    // Example on version '66.0.3359.170' x64:
+    //   Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
+    //   Debug-ID  "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
+    if (module_id.size() >= 32) {
+      module_id =
+          base::StrCat({module_id.substr(6, 2), module_id.substr(4, 2),
+                        module_id.substr(2, 2), module_id.substr(0, 2),
+                        module_id.substr(10, 2), module_id.substr(8, 2),
+                        module_id.substr(14, 2), module_id.substr(12, 2),
+                        module_id.substr(16, 16), "0"});
+    }
+#endif
+
     // We never emit frame names in privacy filtered mode.
     bool should_emit_frame_names =
         !frame_name.empty() && !should_enable_filtering_;
@@ -419,6 +493,11 @@
 }
 
 // static
+void TracingSamplerProfiler::SetupStartupTracing() {
+  TracingSamplerProfilerDataSource::Get()->SetupStartupTracing();
+}
+
+// static
 void TracingSamplerProfiler::StopTracingForTesting() {
   TracingSamplerProfilerDataSource::Get()->StopTracing(base::DoNothing());
 }
@@ -438,7 +517,12 @@
     std::unique_ptr<perfetto::TraceWriter> trace_writer,
     bool should_enable_filtering) {
   base::AutoLock lock(lock_);
-  DCHECK(!profiler_.get());
+  if (profiler_) {
+    if (trace_writer) {
+      profile_builder_->SetTraceWriter(std::move(trace_writer));
+    }
+    return;
+  }
 
 #if defined(OS_ANDROID)
   // The sampler profiler would conflict with the reached code profiler if they
@@ -455,11 +539,12 @@
   // metrics when looking at traces.
   params.keep_consistent_sampling_interval = false;
 
+  auto profile_builder = std::make_unique<TracingProfileBuilder>(
+      sampled_thread_id_, std::move(trace_writer), should_enable_filtering);
+  profile_builder_ = profile_builder.get();
   // Create and start the stack sampling profiler.
 #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
     defined(OFFICIAL_BUILD)
-  auto profile_builder = std::make_unique<TracingProfileBuilder>(
-      sampled_thread_id_, std::move(trace_writer), should_enable_filtering);
   auto* module_cache = profile_builder->GetModuleCache();
   profiler_ = std::make_unique<base::StackSamplingProfiler>(
       sampled_thread_id_, params, std::move(profile_builder),
@@ -467,10 +552,7 @@
 
 #else
   profiler_ = std::make_unique<base::StackSamplingProfiler>(
-      sampled_thread_id_, params,
-      std::make_unique<TracingProfileBuilder>(sampled_thread_id_,
-                                              std::move(trace_writer),
-                                              should_enable_filtering));
+      sampled_thread_id_, params, std::move(profile_builder));
 #endif
 
   profiler_->Start();
@@ -478,10 +560,13 @@
 
 void TracingSamplerProfiler::StopTracing() {
   base::AutoLock lock(lock_);
-  DCHECK(profiler_.get());
+  if (!profiler_) {
+    return;
+  }
 
   // Stop and release the stack sampling profiler.
   profiler_->Stop();
+  profile_builder_ = nullptr;
   profiler_.reset();
 }
 
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
index fe003fc..85714ac 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
@@ -50,13 +50,33 @@
     void OnProfileCompleted(base::TimeDelta profile_duration,
                             base::TimeDelta sampling_period) override {}
 
+    void SetTraceWriter(std::unique_ptr<perfetto::TraceWriter> trace_writer);
+
    private:
+    struct BufferedSample {
+      BufferedSample(base::TimeTicks, std::vector<base::Frame>&&);
+      BufferedSample(BufferedSample&& other);
+      ~BufferedSample();
+
+      base::TimeTicks timestamp;
+      std::vector<base::Frame> sample;
+
+      DISALLOW_COPY_AND_ASSIGN(BufferedSample);
+    };
+
     InterningID GetCallstackIDAndMaybeEmit(
         const std::vector<base::Frame>& frames,
         perfetto::TraceWriter::TracePacketHandle* trace_packet);
+    void WriteSampleToTrace(const BufferedSample& sample);
+
+    // We usually sample at 50ms, and expect that tracing should have started in
+    // 10s.
+    constexpr static size_t kMaxBufferedSamples = 200;
+    std::vector<BufferedSample> buffered_samples_;
 
     base::ModuleCache module_cache_;
     const base::PlatformThreadId sampled_thread_id_;
+    base::Lock trace_writer_lock_;
     std::unique_ptr<perfetto::TraceWriter> trace_writer_;
     InterningIndex<size_t> interned_callstacks_{1000};
     InterningIndex<std::pair<std::string, std::string>,
@@ -79,6 +99,8 @@
   static void CreateForCurrentThread();
   static void RegisterDataSource();
 
+  static void SetupStartupTracing();
+
   // For tests.
   static void DeleteForCurrentThreadForTesting();
   static void StartTracingForTesting(tracing::PerfettoProducer* producer);
@@ -97,6 +119,7 @@
 
   base::Lock lock_;
   std::unique_ptr<base::StackSamplingProfiler> profiler_;  // under |lock_|
+  TracingProfileBuilder* profile_builder_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TracingSamplerProfiler);
 };
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
index 9f0012bd..9c0ad98 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
@@ -3,14 +3,17 @@
 // found in the LICENSE file.
 
 #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
+#include <limits>
 
 #include "base/at_exit.h"
 #include "base/bind.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_buffer.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -172,20 +175,6 @@
 #endif
   }
 
-  static void TraceDataCallback(
-      const base::RepeatingCallback<void()>& callback,
-      std::string* output,
-      const scoped_refptr<base::RefCountedString>& json_events_str,
-      bool has_more_events) {
-    if (output->size() > 1 && !json_events_str->data().empty()) {
-      output->append(",");
-    }
-    output->append(json_events_str->data());
-    if (!has_more_events) {
-      callback.Run();
-    }
-  }
-
   void EndTracing() {
     TracingSamplerProfiler::StopTracingForTesting();
     base::RunLoop().RunUntilIdle();
@@ -206,13 +195,27 @@
     }
   }
 
+  uint32_t FindProfilerSequenceId() {
+    uint32_t profile_sequence_id = std::numeric_limits<uint32_t>::max();
+    auto& packets = producer_->finalized_packets();
+    for (auto& packet : packets) {
+      if (packet->has_streaming_profile_packet()) {
+        profile_sequence_id = packet->trusted_packet_sequence_id();
+        break;
+      }
+    }
+    EXPECT_NE(profile_sequence_id, std::numeric_limits<uint32_t>::max());
+    return profile_sequence_id;
+  }
+
+  const MockPerfettoProducer* producer() const { return producer_.get(); }
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   // We want our singleton torn down after each test.
   base::ShadowingAtExitManager at_exit_manager_;
   base::trace_event::TraceResultBuffer trace_buffer_;
-  base::trace_event::TraceResultBuffer::SimpleOutput json_output_;
 
   std::unique_ptr<MockPerfettoProducer> producer_;
 
@@ -230,11 +233,15 @@
   TestModule(const TestModule&) = delete;
   TestModule& operator=(const TestModule&) = delete;
 
+  void set_id(const std::string& id) { id_ = id; }
   uintptr_t GetBaseAddress() const override { return 0; }
-  std::string GetId() const override { return ""; }
+  std::string GetId() const override { return id_; }
   base::FilePath GetDebugBasename() const override { return base::FilePath(); }
   size_t GetSize() const override { return 0; }
   bool IsNative() const override { return true; }
+
+ private:
+  std::string id_;
 };
 
 }  // namespace
@@ -261,6 +268,76 @@
   TracingSamplerProfiler::DeleteForCurrentThreadForTesting();
 }
 
+TEST_F(TracingSampleProfilerTest, TestStartupTracing) {
+  TracingSamplerProfiler::CreateForCurrentThread();
+  TracingSamplerProfiler::SetupStartupTracing();
+  base::RunLoop().RunUntilIdle();
+  WaitForEvents();
+  auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
+  BeginTrace();
+  base::RunLoop().RunUntilIdle();
+  WaitForEvents();
+  EndTracing();
+  base::RunLoop().RunUntilIdle();
+  if (IsStackUnwindingSupported()) {
+    uint32_t seq_id = FindProfilerSequenceId();
+    auto& packets = producer()->finalized_packets();
+    int64_t reference_ts = 0;
+    int64_t first_profile_ts = 0;
+    for (auto& packet : packets) {
+      if (packet->trusted_packet_sequence_id() == seq_id) {
+        if (packet->has_thread_descriptor()) {
+          reference_ts = packet->thread_descriptor().reference_timestamp_us();
+        } else if (packet->has_streaming_profile_packet()) {
+          first_profile_ts =
+              reference_ts +
+              packet->streaming_profile_packet().timestamp_delta_us(0);
+          break;
+        }
+      }
+    }
+    // Expect first sample before tracing started.
+    EXPECT_LT(first_profile_ts,
+              start_tracing_ts.since_origin().InMicroseconds());
+  }
+  TracingSamplerProfiler::DeleteForCurrentThreadForTesting();
+}
+
+TEST_F(TracingSampleProfilerTest, JoinStartupTracing) {
+  TracingSamplerProfiler::SetupStartupTracing();
+  base::RunLoop().RunUntilIdle();
+  TracingSamplerProfiler::CreateForCurrentThread();
+  WaitForEvents();
+  auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
+  BeginTrace();
+  base::RunLoop().RunUntilIdle();
+  WaitForEvents();
+  EndTracing();
+  base::RunLoop().RunUntilIdle();
+  if (IsStackUnwindingSupported()) {
+    uint32_t seq_id = FindProfilerSequenceId();
+    auto& packets = producer()->finalized_packets();
+    int64_t reference_ts = 0;
+    int64_t first_profile_ts = 0;
+    for (auto& packet : packets) {
+      if (packet->trusted_packet_sequence_id() == seq_id) {
+        if (packet->has_thread_descriptor()) {
+          reference_ts = packet->thread_descriptor().reference_timestamp_us();
+        } else if (packet->has_streaming_profile_packet()) {
+          first_profile_ts =
+              reference_ts +
+              packet->streaming_profile_packet().timestamp_delta_us(0);
+          break;
+        }
+      }
+    }
+    // Expect first sample before tracing started.
+    EXPECT_LT(first_profile_ts,
+              start_tracing_ts.since_origin().InMicroseconds());
+  }
+  TracingSamplerProfiler::DeleteForCurrentThreadForTesting();
+}
+
 TEST_F(TracingSampleProfilerTest, SamplingChildThread) {
   base::Thread sampled_thread("sampling_profiler_test");
   sampled_thread.Start();
@@ -294,4 +371,34 @@
   profile_builder.OnSampleCompleted({base::Frame(0x1010, nullptr)});
 }
 
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+TEST(TracingProfileBuilderTest, MangleELFModuleID) {
+  TestModule module;
+  // See explanation for the module_id mangling in
+  // TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit.
+  module.set_id("7F0715C286F8B16C10E4AD349CDA3B9B56C7A773");
+
+  bool found_build_id = false;
+  auto on_packet_callback = base::BindLambdaForTesting(
+      [&found_build_id](std::unique_ptr<perfetto::protos::TracePacket> packet) {
+        if (!packet->has_interned_data() ||
+            packet->interned_data().build_ids_size() == 0) {
+          return;
+        }
+
+        found_build_id = true;
+        EXPECT_EQ(packet->interned_data().build_ids(0).str(),
+                  "C215077FF8866CB110E4AD349CDA3B9B0");
+      });
+
+  auto trace_writer = std::make_unique<MockTraceWriter>(on_packet_callback);
+  auto* raw_trace_writer = trace_writer.get();
+  TracingSamplerProfiler::TracingProfileBuilder profile_builder(
+      base::PlatformThreadId(), std::move(trace_writer), false);
+  profile_builder.OnSampleCompleted({base::Frame(0x1010, &module)});
+  raw_trace_writer->FlushPacketIfPossible();
+  EXPECT_TRUE(found_build_id);
+}
+#endif
+
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index b2e5f9c..5b02671 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -11,6 +11,7 @@
 #include "components/tracing/common/trace_to_console.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
+#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
 #include "services/tracing/public/cpp/tracing_features.h"
 
 namespace tracing {
@@ -37,6 +38,7 @@
 
   if (startup_config->IsEnabled()) {
     if (TracingUsesPerfettoBackend()) {
+      TracingSamplerProfiler::SetupStartupTracing();
       TraceEventDataSource::GetInstance()->SetupStartupTracing(
           startup_config->GetSessionOwner() ==
           TraceStartupConfig::SessionOwner::kBackgroundTracing);
diff --git a/services/viz/public/cpp/compositing/render_pass_struct_traits.h b/services/viz/public/cpp/compositing/render_pass_struct_traits.h
index a33c429b..72b8788 100644
--- a/services/viz/public/cpp/compositing/render_pass_struct_traits.h
+++ b/services/viz/public/cpp/compositing/render_pass_struct_traits.h
@@ -51,9 +51,9 @@
     return input->backdrop_filters;
   }
 
-  static gfx::RRectF backdrop_filter_bounds(
+  static base::Optional<gfx::RRectF> backdrop_filter_bounds(
       const std::unique_ptr<viz::RenderPass>& input) {
-    return input->backdrop_filter_bounds.value_or(gfx::RRectF());
+    return input->backdrop_filter_bounds;
   }
 
   static const gfx::ColorSpace& color_space(
diff --git a/services/viz/public/cpp/compositing/struct_traits_unittest.cc b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
index c382fa53..7f74ad6 100644
--- a/services/viz/public/cpp/compositing/struct_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
@@ -885,9 +885,10 @@
 TEST_F(StructTraitsTest, RenderPassWithEmptySharedQuadStateList) {
   const RenderPassId render_pass_id = 3u;
   const gfx::Rect output_rect(45, 22, 120, 13);
+  const gfx::Rect damage_rect(56, 123, 19, 43);
   const gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
-  const gfx::Rect damage_rect(56, 123, 19, 43);
+  const base::Optional<gfx::RRectF> backdrop_filter_bounds;
   skcms_Matrix3x3 to_XYZD50 = SkNamedGamut::kXYZ;
   skcms_TransferFunction fn = {1, 0, 1, 0, 0, 0, 1};
   gfx::ColorSpace color_space = gfx::ColorSpace::CreateCustom(to_XYZD50, fn);
@@ -898,9 +899,9 @@
   std::unique_ptr<RenderPass> input = RenderPass::Create();
   input->SetAll(render_pass_id, output_rect, damage_rect, transform_to_root,
                 cc::FilterOperations(), cc::FilterOperations(),
-                base::Optional<gfx::RRectF>(), color_space,
-                has_transparent_background, cache_render_pass,
-                has_damage_from_contributing_content, generate_mipmap);
+                backdrop_filter_bounds, color_space, has_transparent_background,
+                cache_render_pass, has_damage_from_contributing_content,
+                generate_mipmap);
 
   // Unlike the previous test, don't add any quads to the list; we need to
   // verify that the serialization code can deal with that.
@@ -914,6 +915,7 @@
   EXPECT_EQ(output_rect, output->output_rect);
   EXPECT_EQ(damage_rect, output->damage_rect);
   EXPECT_EQ(transform_to_root, output->transform_to_root_target);
+  EXPECT_EQ(backdrop_filter_bounds, output->backdrop_filter_bounds);
   EXPECT_EQ(has_transparent_background, output->has_transparent_background);
   EXPECT_EQ(color_space, output->color_space);
 }
diff --git a/storage/browser/fileapi/plugin_private_file_system_backend.cc b/storage/browser/fileapi/plugin_private_file_system_backend.cc
index 01b440062..d13dc733 100644
--- a/storage/browser/fileapi/plugin_private_file_system_backend.cc
+++ b/storage/browser/fileapi/plugin_private_file_system_backend.cc
@@ -26,7 +26,9 @@
 #include "storage/browser/fileapi/file_system_operation_context.h"
 #include "storage/browser/fileapi/isolated_context.h"
 #include "storage/browser/fileapi/obfuscated_file_util.h"
+#include "storage/browser/fileapi/obfuscated_file_util_memory_delegate.h"
 #include "storage/browser/fileapi/quota/quota_reservation.h"
+#include "storage/browser/fileapi/sandbox_file_stream_writer.h"
 #include "storage/common/fileapi/file_system_util.h"
 
 namespace storage {
@@ -187,12 +189,15 @@
 }
 
 bool PluginPrivateFileSystemBackend::SupportsStreaming(
-    const storage::FileSystemURL& url) const {
-  return false;
+    const FileSystemURL& url) const {
+  // Streaming is required for incognito file systems in order to access
+  // memory-backed files.
+  DCHECK(CanHandleType(url.type()));
+  return file_system_options_.is_incognito();
 }
 
 bool PluginPrivateFileSystemBackend::HasInplaceCopyImplementation(
-    storage::FileSystemType type) const {
+    FileSystemType type) const {
   return false;
 }
 
@@ -203,6 +208,7 @@
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
     FileSystemContext* context) const {
+  DCHECK(CanHandleType(url.type()));
   return FileStreamReader::CreateForFileSystemFile(context, url, offset,
                                                    expected_modification_time);
 }
@@ -212,7 +218,11 @@
     const FileSystemURL& url,
     int64_t offset,
     FileSystemContext* context) const {
-  return std::unique_ptr<FileStreamWriter>();
+  DCHECK(CanHandleType(url.type()));
+
+  // Observers not supported by PluginPrivateFileSystemBackend.
+  return std::make_unique<SandboxFileStreamWriter>(context, url, offset,
+                                                   UpdateObserverList());
 }
 
 FileSystemQuotaUtil* PluginPrivateFileSystemBackend::GetQuotaUtil() {
@@ -374,4 +384,11 @@
       static_cast<AsyncFileUtilAdapter*>(file_util_.get())->sync_file_util());
 }
 
+ObfuscatedFileUtilMemoryDelegate*
+PluginPrivateFileSystemBackend::obfuscated_file_util_memory_delegate() {
+  auto* file_util = obfuscated_file_util();
+  DCHECK(file_util->is_incognito());
+  return static_cast<ObfuscatedFileUtilMemoryDelegate*>(file_util->delegate());
+}
+
 }  // namespace storage
diff --git a/storage/browser/fileapi/plugin_private_file_system_backend.h b/storage/browser/fileapi/plugin_private_file_system_backend.h
index 6fab28d..c85bfb9 100644
--- a/storage/browser/fileapi/plugin_private_file_system_backend.h
+++ b/storage/browser/fileapi/plugin_private_file_system_backend.h
@@ -35,6 +35,7 @@
 namespace storage {
 
 class ObfuscatedFileUtil;
+class ObfuscatedFileUtilMemoryDelegate;
 class SpecialStoragePolicy;
 class WatcherManager;
 
@@ -134,8 +135,10 @@
 
  private:
   friend class content::PluginPrivateFileSystemBackendTest;
+  friend class SandboxFileStreamWriter;
 
   ObfuscatedFileUtil* obfuscated_file_util();
+  ObfuscatedFileUtilMemoryDelegate* obfuscated_file_util_memory_delegate();
   const base::FilePath& base_path() const { return base_path_; }
 
   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
diff --git a/storage/browser/fileapi/sandbox_file_stream_writer.cc b/storage/browser/fileapi/sandbox_file_stream_writer.cc
index ecf0040b..cf734fc1 100644
--- a/storage/browser/fileapi/sandbox_file_stream_writer.cc
+++ b/storage/browser/fileapi/sandbox_file_stream_writer.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
@@ -19,6 +20,8 @@
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_features.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
+#include "storage/browser/fileapi/obfuscated_file_util_memory_delegate.h"
+#include "storage/browser/fileapi/plugin_private_file_system_backend.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/common/fileapi/file_system_util.h"
 
@@ -143,9 +146,21 @@
 
   if (file_system_context_->is_incognito() &&
       base::FeatureList::IsEnabled(features::kEnableFilesystemInIncognito)) {
+    base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_delegate;
+    if (url_.type() == kFileSystemTypePluginPrivate) {
+      auto* backend = static_cast<PluginPrivateFileSystemBackend*>(
+          file_system_context_->GetFileSystemBackend(
+              kFileSystemTypePluginPrivate));
+      memory_file_util_delegate =
+          backend->obfuscated_file_util_memory_delegate()->GetWeakPtr();
+    } else {
+      memory_file_util_delegate =
+          file_system_context_->sandbox_delegate()->memory_file_util_delegate();
+    }
     file_writer_ = FileStreamWriter::CreateForMemoryFile(
-        file_system_context_->sandbox_delegate()->memory_file_util_delegate(),
-        platform_path, initial_offset_, FileStreamWriter::OPEN_EXISTING_FILE);
+        memory_file_util_delegate, platform_path, initial_offset_,
+        FileStreamWriter::OPEN_EXISTING_FILE);
+
   } else {
     file_writer_ = FileStreamWriter::CreateForLocalFile(
         file_system_context_->default_file_task_runner(), platform_path,
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 419480b6..3d30f2c 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -4460,52 +4460,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=android-webview-instrumentation",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--dont-restore-color-profile-after-test",
-          "--os-type",
-          "android",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "android_webview_pixel_skia_gold_test",
-        "precommit_args": [
-          "--review-patch-issue",
-          "${patch_issue}",
-          "--review-patch-set",
-          "${patch_set}",
-          "--buildbucket-build-id",
-          "${buildbucket_build_id}"
-        ],
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      },
-      {
-        "args": [
           "--gtest-benchmark-name=angle_perftests",
           "-v",
           "--one-frame-only",
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index 30a7203..14b8accda 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -146,7 +146,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -167,7 +167,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -186,7 +186,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -208,7 +208,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -227,7 +227,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -243,7 +243,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -262,7 +262,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
diff --git a/testing/buildbot/chromium.webrtc.json b/testing/buildbot/chromium.webrtc.json
index a27c8db..dd27644 100644
--- a/testing/buildbot/chromium.webrtc.json
+++ b/testing/buildbot/chromium.webrtc.json
@@ -107,7 +107,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -127,7 +127,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -161,7 +161,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -182,7 +182,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -203,7 +203,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -221,7 +221,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -236,7 +236,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -254,7 +254,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 9d86511..f6fc2cd6 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1151,6 +1151,18 @@
         }
       },
       {
+        "experiment_percentage": 100,
+        "isolate_name": "polymer_tools_python_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "polymer_tools_python_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "isolate_name": "telemetry_gpu_unittests",
         "merge": {
           "args": [],
@@ -2968,6 +2980,24 @@
         }
       },
       {
+        "experiment_percentage": 100,
+        "isolate_name": "polymer_tools_python_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "polymer_tools_python_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        }
+      },
+      {
         "isolate_name": "telemetry_gpu_unittests",
         "merge": {
           "args": [],
@@ -6044,6 +6074,18 @@
         }
       },
       {
+        "experiment_percentage": 100,
+        "isolate_name": "polymer_tools_python_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "polymer_tools_python_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
         "isolate_name": "telemetry_gpu_unittests",
         "merge": {
           "args": [],
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 2db217c7..0f33a31b 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2209,6 +2209,14 @@
     "label": "//chrome/test:pixel_browser_tests",
     "type": "console_test_launcher",
   },
+  "polymer_tools_python_unittests": {
+    "args": [
+      "../../tools/polymer/run_polymer_tools_tests.py",
+    ],
+    "label": "//tools/polymer:polymer_tools_python_unittests",
+    "script": "//testing/scripts/run_isolated_script_test.py",
+    "type": "script",
+  },
   "postmortem-metadata": {
     "label": "//v8:postmortem-metadata",
     "type": "additional_compile_target",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 169db18..ff7c9dca 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -154,35 +154,37 @@
       },
     },
 
-    'android_webview_gpu_telemetry_tests': {
-      'pixel_skia': {
-        'name': 'android_webview_pixel_skia_gold_test',
-        'args': [
-          '--dont-restore-color-profile-after-test',
-          '--os-type',
-          '${os_type}',
-          '--build-revision',
-          '${got_revision}',
-          '--test-machine-name',
-          '${buildername}',
-        ],
-        'precommit_args': [
-          # Gerrit issue ID
-          '--review-patch-issue',
-          '${patch_issue}',
-          # Patch set number
-          '--review-patch-set',
-          '${patch_set}',
-          # Buildbucket ID
-          '--buildbucket-build-id',
-          '${buildbucket_build_id}',
-        ],
-        'swarming': {
-          'service_account': 'chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com'
-        },
-        'telemetry_test_name': 'pixel',
-      },
-    },
+    # Temporarily disabled due to timeouts causing invalid test results.
+    # TODO(jmadill): Re-enable once timeout is resolved. http://crbug.com/987623
+    # 'android_webview_gpu_telemetry_tests': {
+    #   'pixel_skia': {
+    #     'name': 'android_webview_pixel_skia_gold_test',
+    #     'args': [
+    #       '--dont-restore-color-profile-after-test',
+    #       '--os-type',
+    #       '${os_type}',
+    #       '--build-revision',
+    #       '${got_revision}',
+    #       '--test-machine-name',
+    #       '${buildername}',
+    #     ],
+    #     'precommit_args': [
+    #       # Gerrit issue ID
+    #       '--review-patch-issue',
+    #       '${patch_issue}',
+    #       # Patch set number
+    #       '--review-patch-set',
+    #       '${patch_set}',
+    #       # Buildbucket ID
+    #       '--buildbucket-build-id',
+    #       '${buildbucket_build_id}',
+    #     ],
+    #     'swarming': {
+    #       'service_account': 'chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com'
+    #     },
+    #     'telemetry_test_name': 'pixel',
+    #   },
+    # },
 
     'aura_gtests': {
       'aura_unittests': {},
@@ -4845,6 +4847,9 @@
           ],
         },
       },
+      'polymer_tools_python_unittests': {
+        'experiment_percentage': 100,
+      },
     },
 
     'win_specific_xr_perf_tests': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 10f414ef..1ea6c721 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2420,7 +2420,9 @@
           'gtest_tests': 'gpu_fyi_android_and_mac_gtests',
           'isolated_scripts': 'gpu_angle_and_mobile_representative_perf_fyi_isolated_scripts',
           'gpu_telemetry_tests': 'gpu_nexus5x_telemetry_tests',
-          'android_webview_gpu_telemetry_tests': 'android_webview_gpu_telemetry_tests',
+          # Temporarily disabled due to timeouts causing invalid test results.
+          # TODO(jmadill): Re-enable once timeout is resolved. http://crbug.com/987623
+          # 'android_webview_gpu_telemetry_tests': 'android_webview_gpu_telemetry_tests',
         },
       },
       'Android FYI Release (Nexus 6)': {
@@ -3898,7 +3900,7 @@
       },
       'WebRTC Chromium Linux Tester': {
         'mixins': [
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'webrtc_chromium_tests_with_baremetal_tests',
@@ -3997,7 +3999,7 @@
       'WebRTC Chromium FYI Linux Tester': {
         'mixins': [
           'x86-64',
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'webrtc_chromium_gtests',
diff --git a/testing/scripts/run_isolated_script_test.py b/testing/scripts/run_isolated_script_test.py
index f0d3e5e..faa0cad 100755
--- a/testing/scripts/run_isolated_script_test.py
+++ b/testing/scripts/run_isolated_script_test.py
@@ -58,6 +58,7 @@
     'run_blinkpy_tests.py',
     'metrics_python_tests.py',
     'run_mac_signing_tests.py',
+    'run_polymer_tools_tests.py',
 }
 
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d637d37..a0a69a3 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -610,26 +610,6 @@
             ]
         }
     ],
-    "AutofillDoNotUploadSaveUnsupportedCards": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillDoNotUploadSaveUnsupportedCards"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillDynamicForms": [
         {
             "platforms": [
@@ -5254,6 +5234,7 @@
                 "android",
                 "chromeos",
                 "ios",
+                "linux",
                 "mac",
                 "windows"
             ],
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index ec924c7..aee815d 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -326,7 +326,7 @@
     "BlinkHeapConcurrentMarking", base::FEATURE_DISABLED_BY_DEFAULT};
 // Enables concurrently sweeping Blink's heap.
 const base::Feature kBlinkHeapConcurrentSweeping{
-    "BlinkHeapConcurrentSweeping", base::FEATURE_ENABLED_BY_DEFAULT};
+    "BlinkHeapConcurrentSweeping", base::FEATURE_DISABLED_BY_DEFAULT};
 // Enables incrementally marking Blink's heap.
 const base::Feature kBlinkHeapIncrementalMarking{
     "BlinkHeapIncrementalMarking", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -352,5 +352,16 @@
     "VerifyHTMLFetchedFromAppCacheBeforeDelay",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether we use ThreadPriority::DISPLAY for renderer
+// compositor & IO threads.
+const base::Feature kBlinkCompositorUseDisplayThreadPriority {
+  "BlinkCompositorUseDisplayThreadPriority",
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/common/messaging/transferable_message_struct_traits.cc b/third_party/blink/common/messaging/transferable_message_struct_traits.cc
index e629d98..e82adb89 100644
--- a/third_party/blink/common/messaging/transferable_message_struct_traits.cc
+++ b/third_party/blink/common/messaging/transferable_message_struct_traits.cc
@@ -28,6 +28,7 @@
       blink::MessagePortChannel::CreateFromHandles(std::move(stream_channels));
   out->has_user_gesture = data.has_user_gesture();
   out->transfer_user_activation = data.transfer_user_activation();
+  out->allow_autoplay = data.allow_autoplay();
   return true;
 }
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 1a1cd29f..7ea1cd7 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -112,6 +112,9 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kVerifyHTMLFetchedFromAppCacheBeforeDelay;
 
+BLINK_COMMON_EXPORT extern const base::Feature
+    kBlinkCompositorUseDisplayThreadPriority;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/common/messaging/transferable_message.h b/third_party/blink/public/common/messaging/transferable_message.h
index e532eadb..4ac7bd1 100644
--- a/third_party/blink/public/common/messaging/transferable_message.h
+++ b/third_party/blink/public/common/messaging/transferable_message.h
@@ -47,6 +47,9 @@
   // destination frame.
   bool transfer_user_activation = false;
 
+  // Whether the destination frame is allowed to autoplay.
+  bool allow_autoplay = false;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TransferableMessage);
 };
diff --git a/third_party/blink/public/common/messaging/transferable_message_struct_traits.h b/third_party/blink/public/common/messaging/transferable_message_struct_traits.h
index 0fd5213..21712cad 100644
--- a/third_party/blink/public/common/messaging/transferable_message_struct_traits.h
+++ b/third_party/blink/public/common/messaging/transferable_message_struct_traits.h
@@ -54,6 +54,10 @@
     return input.transfer_user_activation;
   }
 
+  static bool allow_autoplay(blink::TransferableMessage& input) {
+    return input.allow_autoplay;
+  }
+
   static bool Read(blink::mojom::TransferableMessage::DataView data,
                    blink::TransferableMessage* out);
 };
diff --git a/third_party/blink/public/mojom/messaging/transferable_message.mojom b/third_party/blink/public/mojom/messaging/transferable_message.mojom
index 5cd699fb..585d6bd 100644
--- a/third_party/blink/public/mojom/messaging/transferable_message.mojom
+++ b/third_party/blink/public/mojom/messaging/transferable_message.mojom
@@ -38,4 +38,6 @@
   // Whether the state of user activation should be transferred to the
   // destination frame.
   bool transfer_user_activation;
+  // Whether the destination frame is allowed to autoplay.
+  bool allow_autoplay;
 };
diff --git a/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom b/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
index fa5b930..cfc0a85 100644
--- a/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
@@ -45,7 +45,7 @@
   // TODO(kinuko): Add DispatchExtendableMessageEvent() as well.
 
   // Connects a new pipe to this controller instance.
-  Clone(ControllerServiceWorker& controller);
+  Clone(pending_receiver<ControllerServiceWorker> receiver);
 };
 
 // A convenient struct that packs necessary information for a service worker
@@ -58,7 +58,7 @@
       blink.mojom.ControllerServiceWorkerMode.kNoController;
 
   // Non-null iff there is a controller and it has a fetch event handler.
-  ControllerServiceWorker? endpoint;
+  pending_remote<ControllerServiceWorker>? remote_controller;
 
   // The client being controlled, used for FetchEvent#clientId. The ID is
   // issued by the browser process for this receiving client, and would
@@ -89,5 +89,5 @@
   // Resets the controller connection with the given |controller|, this
   // can be called when a new controller is given, e.g. due to claim().
   // |controller| can be null if it gets no controller.
-  UpdateController(ControllerServiceWorker? controller);
+  UpdateController(pending_remote<ControllerServiceWorker>? controller);
 };
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
index 6044db6..612b97e7 100644
--- a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -76,7 +76,7 @@
 
   // Cloned and passed to each controllee to directly dispatch events from the
   // controllees.
-  ControllerServiceWorker& controller_request;
+  pending_receiver<ControllerServiceWorker> controller_receiver;
 
   // Information to transfer installed scripts from the browser to the renderer.
   ServiceWorkerInstalledScriptsInfo? installed_scripts_info;
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
index 3e73835a..5c07350 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
@@ -82,8 +82,9 @@
   // reported to the controllees, but the browser process is responsible for
   // properly handling the failure and recording the reasons.
   // |purpose| is used for UMA.
-  EnsureControllerServiceWorker(ControllerServiceWorker& controller,
-                                ControllerServiceWorkerPurpose purpose);
+  EnsureControllerServiceWorker(
+      pending_receiver<ControllerServiceWorker> receiver,
+      ControllerServiceWorkerPurpose purpose);
 
   // Makes a new endpoint to this ServiceWorkerContainerHost.
   CloneContainerHost(ServiceWorkerContainerHost& container_host);
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 55c2727..cb490ad 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2049,7 +2049,6 @@
   kOpenerNavigationDownloadCrossOrigin = 2649,
   kV8RegExpMatchIsTrueishOnNonJSRegExp = 2650,
   kV8RegExpMatchIsFalseishOnJSRegExp = 2651,
-  kDownloadInAdFrameWithUserGesture = 2652,
   kDownloadInAdFrameWithoutUserGesture = 2653,
   kNavigatorAppVersion = 2654,
   kNavigatorDoNotTrack = 2655,
@@ -2330,9 +2329,7 @@
   kWebkitMarginAfterCollapseDiscard = 2945,
   kWebkitMarginAfterCollapseSeparate = 2946,
   kWebkitMarginAfterCollapseSeparateMaybeDoesSomething = 2947,
-  kCredentialManagerCreateWithUVM = 2948,
   kCredentialManagerGetWithUVM = 2949,
-  kCredentialManagerCreateSuccessWithUVM = 2950,
   kCredentialManagerGetSuccessWithUVM = 2951,
   kDiscardInputEventToMovingIframe = 2952,
   kSignedExchangeSubresourcePrefetch = 2953,
@@ -2356,6 +2353,10 @@
   kV8PointerEvent_GetPredictedEvents_Method = 2971,
   kScrollSnapOnViewportBreaks = 2972,
   kScrollPaddingOnViewportBreaks = 2973,
+  kDownloadInAdFrame = 2974,
+  kDownloadInSandbox = 2975,
+  kDownloadWithoutUserGesture = 2976,
+  kAutoplayDynamicDelegation = 2977,
 
   // 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/webauthn/authenticator.mojom b/third_party/blink/public/mojom/webauthn/authenticator.mojom
index e18dded..d5dd46b5 100644
--- a/third_party/blink/public/mojom/webauthn/authenticator.mojom
+++ b/third_party/blink/public/mojom/webauthn/authenticator.mojom
@@ -59,8 +59,8 @@
   array<uint8> client_data_json;
 };
 
-// Content of user verification method extension returned by both
-// Authenticator::MakeCredential and Authenticator::GetAssertion.
+// Content of user verification method extension returned by
+// Authenticator::GetAssertion.
 // See https://w3c.github.io/webauthn/#sctn-uvm-extension
 // Registry of the available values, see
 // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#user-verification-methods
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 115a2953..f3481bf 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
@@ -120,6 +120,7 @@
                                scoped_refptr<WebRTCICECandidate>) {
     return false;
   }
+  virtual void RestartIce() = 0;
   virtual void GetStats(const WebRTCStatsRequest&) = 0;
   // Gets stats using the new stats collection API, see
   // third_party/webrtc/api/stats/.  These will replace the old stats collection
diff --git a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h
index 2000984..4566274 100644
--- a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h
+++ b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h
@@ -52,7 +52,7 @@
       // A handle for mojom::ServiceWorkerRequest.
       mojo::ScopedMessagePipeHandle request) = 0;
   virtual void BindControllerServiceWorker(
-      // A handle for mojom::ControllerServiceWorkerRequest.
+      // A handle for mojo::PendingReceiver<mojom::ControllerServiceWorker>.
       mojo::ScopedMessagePipeHandle request) = 0;
 
   virtual void OnNavigationPreloadResponse(
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index c3c23c3..ea92c27 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -42,7 +42,7 @@
 class WebContentSettingsClient;
 class WebDocument;
 class WebDoubleSize;
-class WebDOMEvent;
+class WebDOMMessageEvent;
 class WebLocalFrameClient;
 class WebFrameWidget;
 class WebInputMethodController;
@@ -609,7 +609,7 @@
   // Dispatches a message event on the current DOMWindow in this WebFrame.
   virtual void DispatchMessageEventWithOriginCheck(
       const WebSecurityOrigin& intended_target_origin,
-      const WebDOMEvent&,
+      const WebDOMMessageEvent&,
       bool has_user_gesture) = 0;
 
   // TEMP: Usage count for chrome.loadtimes deprecation.
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS
index 0f1550d..cbce2d3 100644
--- a/third_party/blink/renderer/DEPS
+++ b/third_party/blink/renderer/DEPS
@@ -17,9 +17,9 @@
     "+base/macros.h",
     "+base/memory/ptr_util.h",
     "+base/memory/weak_ptr.h",
+    "+base/metrics/field_trial_params.h",
     "+base/metrics/histogram_functions.h",
     "+base/metrics/histogram_macros.h",
-    "+base/metrics/field_trial_params.h",
     "+base/metrics/single_sample_metrics.h",
     "+base/numerics/checked_math.h",
     "+base/numerics/clamped_math.h",
@@ -42,10 +42,11 @@
     "+base/time/default_tick_clock.h",
     "+base/time/time.h",
     "+base/timer/elapsed_timer.h",
+    "+base/timer/timer.h",
     "+base/trace_event/memory_dump_manager.h",
     "+base/trace_event/memory_dump_provider.h",
     "+base/trace_event/trace_event.h",
-    "+base/timer/timer.h",
+    "+base/util/type_safety/pass_key.h",
     "+build",
     "+components/crash/core/common/crash_key.h",
     "+services/network/public/mojom",
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index f8b129f..8e6a54e2 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -1474,10 +1474,10 @@
 // when the checker was constructed, according to WTF::currentTime.
 class TimeIntervalChecker {
  public:
-  TimeIntervalChecker() : start_time_(WTF::CurrentTime()) {}
+  TimeIntervalChecker() : start_time_(base::Time::Now().ToDoubleT()) {}
   bool WasAliveAt(double time_in_milliseconds) {
     double time = time_in_milliseconds / kMsPerSecond;
-    return start_time_ <= time && time <= WTF::CurrentTime();
+    return start_time_ <= time && time <= base::Time::Now().ToDoubleT();
   }
 
  private:
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc b/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
index 300688e..5e25d58 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
@@ -69,7 +69,7 @@
     bool is_main_frame,
     WindowProxy::FrameReuseStatus frame_reuse_status) {
   did_dispose_context_for_main_frame_ = is_main_frame;
-  last_context_disposal_time_ = WTF::CurrentTime();
+  last_context_disposal_time_ = base::Time::Now().ToDoubleT();
 #if defined(OS_ANDROID)
   // When a low end device is in a low memory situation we should prioritize
   // memory use and trigger a V8+Blink GC. However, on Android, if the frame
@@ -101,7 +101,7 @@
   double max_time_since_last_context_disposal = .2;
   if (!did_dispose_context_for_main_frame_ && !pseudo_idle_timer_.IsActive() &&
       last_context_disposal_time_ + max_time_since_last_context_disposal >=
-          WTF::CurrentTime()) {
+          base::Time::Now().ToDoubleT()) {
     pseudo_idle_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
   }
 }
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index 75fd7fa7..f4f489e 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -13,10 +13,12 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
+#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
 #include "third_party/blink/renderer/modules/shapedetection/detected_barcode.h"
 #include "third_party/blink/renderer/modules/shapedetection/detected_face.h"
 #include "third_party/blink/renderer/modules/shapedetection/detected_text.h"
+#include "third_party/blink/renderer/modules/shapedetection/landmark.h"
 
 namespace blink {
 
@@ -81,8 +83,8 @@
           return nullptr;
         corner_points.push_back(point);
       }
-      return DetectedBarcode::Create(raw_value, bounding_box, format,
-                                     corner_points);
+      return MakeGarbageCollected<DetectedBarcode>(raw_value, bounding_box,
+                                                   format, corner_points);
     }
     case kDetectedFaceTag: {
       DOMRectReadOnly* bounding_box = ReadDOMRectReadOnly();
@@ -98,7 +100,7 @@
           return nullptr;
         landmarks.push_back(landmark);
       }
-      return DetectedFace::Create(bounding_box, landmarks);
+      return MakeGarbageCollected<DetectedFace>(bounding_box, landmarks);
     }
     case kDetectedTextTag: {
       String raw_value;
@@ -117,7 +119,8 @@
           return nullptr;
         corner_points.push_back(point);
       }
-      return DetectedText::Create(raw_value, bounding_box, corner_points);
+      return MakeGarbageCollected<DetectedText>(raw_value, bounding_box,
+                                                corner_points);
     }
     default:
       break;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index 58f2d68..baf987bc 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -16,6 +16,8 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_detected_text.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h"
+#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
+#include "third_party/blink/renderer/modules/shapedetection/landmark.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 5548998..6b54dd83 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -28,7 +28,9 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_result_impl.h"
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
+#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
+#include "third_party/blink/renderer/modules/shapedetection/landmark.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
diff --git a/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py
index bac570c..8fc3a77 100644
--- a/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py
+++ b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py
@@ -30,7 +30,14 @@
 def main():
     options, filepaths = parse_options()
 
-    database = web_idl.build_database(filepaths)
+    # Incomplete IDL compiler produces a lot of errors, which break trybots.
+    # So, we ignore all the errors for the time being.
+    # TODO(bindings-team): Replace |report_error| with sys.exit once IDL
+    # compiler completes.
+    report_error = lambda message: None
+
+    database = web_idl.build_database(filepaths=filepaths,
+                                      report_error=report_error)
 
     utilities.write_pickle_file(options.output, database)
 
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/argument.py b/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
index d614068..4cd1651 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
@@ -33,6 +33,15 @@
             self.idl_type = idl_type
             self.default_value = default_value
 
+        def make_copy(self):
+            return Argument.IR(
+                identifier=self.identifier,
+                index=self.index,
+                idl_type=self.idl_type,
+                default_value=self.default_value,
+                extended_attributes=self.extended_attributes.make_copy(),
+                code_generator_info=self.code_generator_info.make_copy())
+
     @property
     def idl_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/attribute.py b/third_party/blink/renderer/bindings/scripts/web_idl/attribute.py
index 43d641e7..4ab3e8a 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/attribute.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/attribute.py
@@ -28,6 +28,7 @@
                      exposures=None,
                      code_generator_info=None,
                      component=None,
+                     components=None,
                      debug_info=None):
             assert isinstance(idl_type, IdlType)
             assert isinstance(is_static, bool)
@@ -38,7 +39,8 @@
             WithExtendedAttributes.__init__(self, extended_attributes)
             WithExposure.__init__(self, exposures)
             WithCodeGeneratorInfo.__init__(self, code_generator_info)
-            WithComponent.__init__(self, component)
+            WithComponent.__init__(
+                self, component=component, components=components)
             WithDebugInfo.__init__(self, debug_info)
 
             self.idl_type = idl_type
@@ -46,6 +48,18 @@
             self.is_readonly = is_readonly
             self.does_inherit_getter = does_inherit_getter
 
+        def make_copy(self):
+            return Attribute.IR(
+                identifier=self.identifier,
+                idl_type=self.idl_type,
+                is_static=self.is_static,
+                is_readonly=self.is_readonly,
+                does_inherit_getter=self.does_inherit_getter,
+                extended_attributes=self.extended_attributes.make_copy(),
+                code_generator_info=self.code_generator_info.make_copy(),
+                components=self.components,
+                debug_info=self.debug_info.make_copy())
+
     @property
     def idl_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/common.py b/third_party/blink/renderer/bindings/scripts/web_idl/common.py
index 0cb07cd..c721703c 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/common.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/common.py
@@ -138,27 +138,25 @@
 
 
 class Location(object):
-    def __init__(self, filepath=None, line_number=None, column_number=None):
+    def __init__(self, filepath=None, line_number=None, position=None):
         assert filepath is None or isinstance(filepath, str)
         assert line_number is None or isinstance(line_number, int)
-        assert column_number is None or isinstance(column_number, int)
+        assert position is None or isinstance(position, int)
         self._filepath = filepath
         self._line_number = line_number
-        self._column_number = column_number
+        self._position = position  # Position number in a file
 
     def __str__(self):
         text = '{}'.format(self._filepath or '<<unknown path>>')
         if self._line_number:
             text += ':{}'.format(self._line_number)
-            if self._column_number:
-                text += ':{}'.format(self._column_number)
         return text
 
     def make_copy(self):
         return Location(
             filepath=self._filepath,
             line_number=self._line_number,
-            column_number=self._column_number)
+            position=self._position)
 
     @property
     def filepath(self):
@@ -169,8 +167,8 @@
         return self._line_number
 
     @property
-    def column_number(self):
-        return self._column_number
+    def position_in_file(self):
+        return self._position
 
 
 class DebugInfo(object):
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/constant.py b/third_party/blink/renderer/bindings/scripts/web_idl/constant.py
index d7e14252..7dfa6796 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/constant.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/constant.py
@@ -26,6 +26,7 @@
                      exposures=None,
                      code_generator_info=None,
                      component=None,
+                     components=None,
                      debug_info=None):
             assert isinstance(value, ConstantValue)
             assert isinstance(idl_type, IdlType)
@@ -34,12 +35,23 @@
             WithExtendedAttributes.__init__(self, extended_attributes)
             WithExposure.__init__(self, exposures)
             WithCodeGeneratorInfo.__init__(self, code_generator_info)
-            WithComponent.__init__(self, component)
+            WithComponent.__init__(
+                self, component=component, components=components)
             WithDebugInfo.__init__(self, debug_info)
 
             self.value = value
             self.idl_type = idl_type
 
+        def make_copy(self):
+            return Constant.IR(
+                identifier=self.identifier,
+                value=self.value,
+                idl_type=self.idl_type,
+                extended_attributes=self.extended_attributes.make_copy(),
+                code_generator_info=self.code_generator_info.make_copy(),
+                components=self.components,
+                debug_info=self.debug_info.make_copy())
+
     @property
     def idl_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/database_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/database_builder.py
index 8abc27d..4581612 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/database_builder.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/database_builder.py
@@ -9,16 +9,32 @@
 from .reference import RefByIdFactory
 
 
-def build_database(filepaths):
-    """Compiles IDL definitions in |filepaths| and builds a database."""
+def build_database(filepaths, report_error):
+    """
+    Compiles IDL definitions in |filepaths| and builds a database.
+
+    Args:
+        filepaths: Paths to files of AstGroup.
+        report_error: A callback that will be invoked when an error occurs due
+            to inconsistent/invalid IDL definitions.  This callback takes an
+            error message of type str and return value is not used.  It's okay
+            to terminate the program in this callback.
+    """
 
     ir_map = IdentifierIRMap()
     ref_to_idl_type_factory = RefByIdFactory()
     ref_to_idl_def_factory = RefByIdFactory()
     idl_type_factory = IdlTypeFactory()
     load_and_register_idl_definitions(
-        filepaths, ir_map.register, ref_to_idl_type_factory.create,
-        ref_to_idl_def_factory.create, idl_type_factory)
-    compiler = IdlCompiler(ir_map, ref_to_idl_type_factory,
-                           ref_to_idl_def_factory, idl_type_factory)
+        filepaths=filepaths,
+        register_ir=ir_map.register,
+        create_ref_to_idl_def=ref_to_idl_def_factory.create,
+        create_ref_to_idl_type=ref_to_idl_type_factory.create,
+        idl_type_factory=idl_type_factory)
+    compiler = IdlCompiler(
+        ir_map=ir_map,
+        ref_to_idl_def_factory=ref_to_idl_def_factory,
+        ref_to_idl_type_factory=ref_to_idl_type_factory,
+        idl_type_factory=idl_type_factory,
+        report_error=report_error)
     return compiler.build_database()
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 5211afb2..5cd456d6 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -9,6 +9,7 @@
 from .idl_type import IdlTypeFactory
 from .reference import RefByIdFactory
 from .typedef import Typedef
+from .user_defined_type import UserDefinedType
 
 
 class IdlCompiler(object):
@@ -32,27 +33,33 @@
     the details.
     """
 
-    def __init__(self, ir_map, ref_to_idl_type_factory, ref_to_idl_def_factory,
-                 idl_type_factory):
+    def __init__(self, ir_map, ref_to_idl_def_factory, ref_to_idl_type_factory,
+                 idl_type_factory, report_error):
         """
         Args:
             ir_map: IdentifierIRMap filled with the initial IRs of IDL
                 definitions.
-            ref_to_idl_type_factory: RefByIdFactory that created all references
-                to IdlType.
             ref_to_idl_def_factory: RefByIdFactory that created all references
                 to UserDefinedType.
+            ref_to_idl_type_factory: RefByIdFactory that created all references
+                to IdlType.
             idl_type_factory: IdlTypeFactory that created all instances of
                 IdlType.
+            report_error: A callback that will be invoked when an error occurs
+                due to inconsistent/invalid IDL definitions.  This callback
+                takes an error message of type str and return value is not used.
+                It's okay to terminate the program in this callback.
         """
         assert isinstance(ir_map, IdentifierIRMap)
-        assert isinstance(ref_to_idl_type_factory, RefByIdFactory)
         assert isinstance(ref_to_idl_def_factory, RefByIdFactory)
+        assert isinstance(ref_to_idl_type_factory, RefByIdFactory)
         assert isinstance(idl_type_factory, IdlTypeFactory)
+        assert callable(report_error)
         self._ir_map = ir_map
-        self._ref_to_idl_type_factory = ref_to_idl_type_factory
         self._ref_to_idl_def_factory = ref_to_idl_def_factory
+        self._ref_to_idl_type_factory = ref_to_idl_type_factory
         self._idl_type_factory = idl_type_factory
+        self._report_error = report_error
         self._db = DatabaseBody()
         self._did_run = False  # Run only once.
 
@@ -68,13 +75,45 @@
         self._create_public_objects()
 
         # Resolve references.
-        self._resolve_references_to_idl_type()
         self._resolve_references_to_idl_def()
+        self._resolve_references_to_idl_type()
 
         return Database(self._db)
 
     def _merge_partial_interfaces(self):
-        pass
+        def merge_partials(old_interfaces, partial_interfaces):
+            for identifier, old_interface in old_interfaces.iteritems():
+                new_interface = old_interface.make_copy()
+                for partial in partial_interfaces.get(identifier, []):
+                    new_interface.add_components(partial.components)
+                    new_interface.debug_info.add_locations(
+                        partial.debug_info.all_locations)
+                    new_interface.attributes.extend([
+                        attribute.make_copy()
+                        for attribute in partial.attributes
+                    ])
+                    new_interface.constants.extend([
+                        constant.make_copy() for constant in partial.constants
+                    ])
+                    new_interface.operations.extend([
+                        operation.make_copy()
+                        for operation in partial.operations
+                    ])
+
+            self._ir_map.add(new_interface)
+
+        old_interfaces = self._ir_map.find_by_kind(
+            IdentifierIRMap.IR.Kind.INTERFACE)
+        partial_interfaces = self._ir_map.find_by_kind(
+            IdentifierIRMap.IR.Kind.PARTIAL_INTERFACE)
+        old_mixins = self._ir_map.find_by_kind(
+            IdentifierIRMap.IR.Kind.INTERFACE_MIXIN)
+        partial_mixins = self._ir_map.find_by_kind(
+            IdentifierIRMap.IR.Kind.PARTIAL_INTERFACE_MIXIN)
+
+        self._ir_map.move_to_new_phase()
+        merge_partials(old_interfaces, partial_interfaces)
+        merge_partials(old_mixins, partial_mixins)
 
     def _merge_partial_dictionaries(self):
         old_dictionaries = self._ir_map.find_by_kind(
@@ -109,20 +148,33 @@
         for ir in typedef_irs.itervalues():
             self._db.register(DatabaseBody.Kind.TYPEDEF, Typedef(ir))
 
-    def _resolve_references_to_idl_type(self):
-        def resolve(ref):
-            # Resolve to stubs for the time being.
-            ref.set_target_object(False)
-
-        self._ref_to_idl_type_factory.for_each(resolve)
-
     def _resolve_references_to_idl_def(self):
         def resolve(ref):
             try:
-                ref.set_target_object(
-                    self._db.find_by_identifier(ref.identifier))
+                idl_def = self._db.find_by_identifier(ref.identifier)
             except KeyError:
-                # Resolve to stubs for the time being.
-                ref.set_target_object(False)
+                self._report_error("{}: Unresolved reference to {}".format(
+                    ref.ref_own_debug_info.location, ref.identifier))
+                idl_def = UserDefinedType(ref.identifier)  # dummy stub
+            ref.set_target_object(idl_def)
 
         self._ref_to_idl_def_factory.for_each(resolve)
+
+    def _resolve_references_to_idl_type(self):
+        def resolve(ref):
+            try:
+                idl_def = self._db.find_by_identifier(ref.identifier)
+            except KeyError:
+                self._report_error("{}: Unresolved reference to {}".format(
+                    ref.ref_own_debug_info.location, ref.identifier))
+                idl_def = UserDefinedType(ref.identifier)  # dummy stub
+            if isinstance(idl_def, UserDefinedType):
+                idl_type = self._idl_type_factory.definition_type(
+                    user_defined_type=idl_def)
+            elif isinstance(idl_def, Typedef):
+                idl_type = self._idl_type_factory.typedef_type(typedef=idl_def)
+            else:
+                assert False
+            ref.set_target_object(idl_type)
+
+        self._ref_to_idl_type_factory.for_each(resolve)
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
index 360ed34..b5e1d713 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_type.py
@@ -498,20 +498,18 @@
     """
 
     def __init__(self,
-                 user_def_type,
+                 user_defined_type,
                  code_generator_info=None,
                  debug_info=None,
                  pass_key=None):
-        assert isinstance(user_def_type, UserDefinedType)
+        assert isinstance(user_defined_type, UserDefinedType)
         IdlType.__init__(
             self,
             code_generator_info=code_generator_info,
             debug_info=debug_info,
             pass_key=pass_key)
-        WithIdentifier.__init__(self, user_def_type.identifier)
-        self._definition = user_def_type
-
-    # TODO(peria): Consider exposing access of |_definition|
+        WithIdentifier.__init__(self, user_defined_type.identifier)
+        self._definition = user_defined_type
 
     # IdlType overrides
     @property
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 45a71d4..df58e6b 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
@@ -40,12 +40,13 @@
                      constants=None,
                      operations=None,
                      iterable=None,
-                     setlike=None,
                      maplike=None,
+                     setlike=None,
                      extended_attributes=None,
                      exposures=None,
                      code_generator_info=None,
                      component=None,
+                     components=None,
                      debug_info=None):
             assert isinstance(is_partial, bool)
             assert isinstance(is_mixin, bool)
@@ -54,8 +55,8 @@
             assert constants is None or isinstance(constants, (list, tuple))
             assert operations is None or isinstance(operations, (list, tuple))
             assert iterable is None or isinstance(iterable, Iterable)
-            assert setlike is None or isinstance(setlike, Setlike)
             assert maplike is None or isinstance(maplike, Maplike)
+            assert setlike is None or isinstance(setlike, Setlike)
 
             attributes = attributes or []
             constants = constants or []
@@ -84,19 +85,38 @@
             WithExtendedAttributes.__init__(self, extended_attributes)
             WithExposure.__init__(self, exposures)
             WithCodeGeneratorInfo.__init__(self, code_generator_info)
-            WithComponent.__init__(self, component)
+            WithComponent.__init__(
+                self, component=component, components=components)
             WithDebugInfo.__init__(self, debug_info)
 
             self.is_partial = is_partial
             self.is_mixin = is_mixin
             self.inherited = inherited
-            self.attrbiutes = list(attributes)
+            self.attributes = list(attributes)
             self.constants = list(constants)
             self.operations = list(operations)
             self.iterable = iterable
             self.maplike = maplike
             self.setlike = setlike
 
+        def make_copy(self):
+            return Interface.IR(
+                identifier=self.identifier,
+                is_partial=self.is_partial,
+                is_mixin=self.is_mixin,
+                inherited=self.inherited,
+                attributes=map(Attribute.IR.make_copy, self.attributes),
+                constants=map(Constant.IR.make_copy, self.constants),
+                operations=map(Operation.IR.make_copy, self.operations),
+                iterable=self.iterable,
+                maplike=self.maplike,
+                setlike=self.setlike,
+                extended_attributes=self.extended_attributes.make_copy(),
+                code_generator_info=self.code_generator_info.make_copy(),
+                components=self.components,
+                debug_info=self.debug_info.make_copy())
+
+
     @property
     def inherited_interface(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
index b619dd32..ca9b094 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -27,9 +27,9 @@
 from .values import DefaultValue
 
 
-def load_and_register_idl_definitions(filepaths, register_ir,
-                                      create_ref_to_idl_type,
-                                      create_ref_to_idl_def, idl_type_factory):
+def load_and_register_idl_definitions(
+        filepaths, register_ir, create_ref_to_idl_def, create_ref_to_idl_type,
+        idl_type_factory):
     """
     Reads ASTs from |filepaths| and builds IRs from ASTs.
 
@@ -37,10 +37,10 @@
         filepaths: Paths to pickle files that store AST nodes.
         register_ir: A callback function that registers the argument as an
             IR.
-        create_ref_to_idl_type: A callback function that creates a reference
-            to an IdlType from the given identifier.
         create_ref_to_idl_def: A callback function that creates a reference
             to an IDL definition from the given identifier.
+        create_ref_to_idl_type: A callback function that creates a reference
+            to an IdlType from the given identifier.
         idl_type_factory: All IdlType instances will be created through this
             factory.
     """
@@ -49,8 +49,11 @@
     for filepath in filepaths:
         asts_per_component = AstGroup.read_from_file(filepath)
         component = asts_per_component.component
-        builder = _IRBuilder(component, create_ref_to_idl_type,
-                             create_ref_to_idl_def, idl_type_factory)
+        builder = _IRBuilder(
+            component=component,
+            create_ref_to_idl_def=create_ref_to_idl_def,
+            create_ref_to_idl_type=create_ref_to_idl_type,
+            idl_type_factory=idl_type_factory)
 
         for file_node in asts_per_component:
             assert file_node.GetClass() == 'File'
@@ -59,25 +62,25 @@
 
 
 class _IRBuilder(object):
-    def __init__(self, component, create_ref_to_idl_type,
-                 create_ref_to_idl_def, idl_type_factory):
+    def __init__(self, component, create_ref_to_idl_def,
+                 create_ref_to_idl_type, idl_type_factory):
         """
         Args:
             component: A Component to which the built IRs are associated.
-            create_ref_to_idl_type: A callback function that creates a reference
-                to an IdlType from the given identifier.
             create_ref_to_idl_def: A callback function that creates a reference
                 to an IDL definition from the given identifier.
+            create_ref_to_idl_type: A callback function that creates a reference
+                to an IdlType from the given identifier.
             idl_type_factory: All IdlType instances will be created through this
                 factory.
         """
-        assert callable(create_ref_to_idl_type)
         assert callable(create_ref_to_idl_def)
+        assert callable(create_ref_to_idl_type)
         assert isinstance(idl_type_factory, IdlTypeFactory)
 
         self._component = component
-        self._create_ref_to_idl_type = create_ref_to_idl_type
         self._create_ref_to_idl_def = create_ref_to_idl_def
+        self._create_ref_to_idl_type = create_ref_to_idl_type
         self._idl_type_factory = idl_type_factory
 
     def build_top_level_def(self, node):
@@ -111,16 +114,12 @@
         attributes = []
         constants = []
         operations = []
-        property_handlers = []
         for member in members:
             if isinstance(member, Attribute.IR):
                 attributes.append(member)
             elif isinstance(member, Operation.IR):
-                assert member.identifier or member.is_property_handler
                 if member.identifier:
                     operations.append(member)
-                if member.is_property_handler:
-                    property_handlers.append(member)
             elif isinstance(member, Constant.IR):
                 constants.append(member)
             else:
@@ -195,9 +194,6 @@
                 arguments=arguments,
                 return_type=return_type,
                 is_static=bool(node.GetProperty('STATIC')),
-                is_getter=bool(node.GetProperty('GETTER')),
-                is_setter=bool(node.GetProperty('SETTER')),
-                is_deleter=bool(node.GetProperty('DELETER')),
                 extended_attributes=extended_attributes,
                 component=self._component,
                 debug_info=self._build_debug_info(node))
@@ -321,7 +317,7 @@
             location=Location(
                 filepath=node.GetProperty('FILENAME'),
                 line_number=node.GetProperty('LINENO'),
-                column_number=node.GetProperty('POSITION')))
+                position=node.GetProperty('POSITION')))
 
     def _build_default_value(self, node):
         assert node.GetClass() == 'Default'
@@ -333,7 +329,8 @@
 
     def _build_inheritance(self, node):
         assert node.GetClass() == 'Inherit'
-        return self._create_ref_to_idl_def(node.GetName())
+        return self._create_ref_to_idl_def(node.GetName(),
+                                           self._build_debug_info(node))
 
     def _build_is_variadic_argument(self, node):
         # idl_parser produces the following tree to indicate an argument is
@@ -439,7 +436,8 @@
         def build_reference_type(node, extended_attributes):
             identifier = node.GetName()
             return self._idl_type_factory.reference_type(
-                ref_to_idl_type=self._create_ref_to_idl_type(identifier),
+                ref_to_idl_type=self._create_ref_to_idl_type(
+                    identifier, self._build_debug_info(node)),
                 is_optional=is_optional,
                 extended_attributes=extended_attributes,
                 debug_info=self._build_debug_info(node))
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/operation.py b/third_party/blink/renderer/bindings/scripts/web_idl/operation.py
index 5916139..45d84a4 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/operation.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/operation.py
@@ -26,37 +26,39 @@
                      arguments,
                      return_type,
                      is_static=False,
-                     is_getter=False,
-                     is_setter=False,
-                     is_deleter=False,
                      extended_attributes=None,
                      exposures=None,
                      code_generator_info=None,
                      component=None,
+                     components=None,
                      debug_info=None):
             assert isinstance(arguments, (list, tuple)) and all(
                 isinstance(arg, Argument.IR) for arg in arguments)
             assert isinstance(return_type, IdlType)
             assert isinstance(is_static, bool)
-            assert isinstance(is_getter, bool)
-            assert isinstance(is_setter, bool)
-            assert isinstance(is_deleter, bool)
-            assert int(is_getter) + int(is_setter) + int(is_deleter) <= 1
 
             WithIdentifier.__init__(self, identifier)
             WithExtendedAttributes.__init__(self, extended_attributes)
             WithExposure.__init__(self, exposures)
             WithCodeGeneratorInfo.__init__(self, code_generator_info)
-            WithComponent.__init__(self, component)
+            WithComponent.__init__(
+                self, component=component, components=components)
             WithDebugInfo.__init__(self, debug_info)
 
             self.arguments = list(arguments)
             self.return_type = return_type
             self.is_static = is_static
-            self.is_getter = is_getter
-            self.is_setter = is_setter
-            self.is_deleter = is_deleter
-            self.is_property_handler = is_getter or is_setter or is_deleter
+
+        def make_copy(self):
+            return Operation.IR(
+                identifier=self.identifier,
+                arguments=map(Argument.IR.make_copy, self.arguments),
+                return_type=self.return_type,
+                is_static=self.is_static,
+                extended_attributes=self.extended_attributes.make_copy(),
+                code_generator_info=self.code_generator_info.make_copy(),
+                components=self.components,
+                debug_info=self.debug_info.make_copy())
 
     @property
     def is_static(self):
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/reference.py b/third_party/blink/renderer/bindings/scripts/web_idl/reference.py
index 4f54e08e..e86bf5d 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/reference.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/reference.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from .common import DebugInfo
 from .common import WithIdentifier
 
 
@@ -98,9 +99,11 @@
 
     def __init__(self,
                  identifier,
+                 debug_info=None,
                  target_attrs=None,
                  target_attrs_with_priority=None,
                  pass_key=None):
+        assert debug_info is None or isinstance(debug_info, DebugInfo)
         assert pass_key is _REF_BY_ID_PASS_KEY
 
         Proxy.__init__(
@@ -108,6 +111,12 @@
             target_attrs=target_attrs,
             target_attrs_with_priority=target_attrs_with_priority)
         WithIdentifier.__init__(self, identifier)
+        self._ref_own_debug_info = debug_info
+
+    @property
+    def ref_own_debug_info(self):
+        """This reference's own DebugInfo."""
+        return self._ref_own_debug_info
 
 
 class RefByIdFactory(object):
@@ -128,10 +137,17 @@
         self._target_attrs = target_attrs
         self._target_attrs_with_priority = target_attrs_with_priority
 
-    def create(self, identifier):
+    def create(self, identifier, debug_info=None):
+        """
+            Args:
+                identifier: An identifier to be resolved later.
+                debug_info: Where the reference is created, which is useful
+                    especially when the reference is unresolvable.
+        """
         assert not self._is_frozen
         ref = RefById(
             identifier,
+            debug_info=debug_info,
             target_attrs=self._target_attrs,
             target_attrs_with_priority=self._target_attrs_with_priority,
             pass_key=_REF_BY_ID_PASS_KEY)
diff --git a/third_party/blink/renderer/core/animation/compositor_animations.cc b/third_party/blink/renderer/core/animation/compositor_animations.cc
index ad9f756c..be4f5e1 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations.cc
@@ -229,7 +229,7 @@
     }
 
     const PropertySpecificKeyframeVector& keyframes =
-        keyframe_effect.GetPropertySpecificKeyframes(property);
+        *keyframe_effect.GetPropertySpecificKeyframes(property);
     DCHECK_GE(keyframes.size(), 2U);
     for (const auto& keyframe : keyframes) {
       if (keyframe->Composite() != EffectModel::kCompositeReplace &&
@@ -666,7 +666,7 @@
     if (!std::isfinite(scale))
       scale = 1.0;
     const PropertySpecificKeyframeVector& values =
-        effect.GetPropertySpecificKeyframes(property);
+        *effect.GetPropertySpecificKeyframes(property);
 
     compositor_target_property::Type target_property;
     std::unique_ptr<CompositorAnimationCurve> curve;
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index 65cd329..358dfd0 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -873,10 +873,10 @@
   effect1->SnapshotAllCompositorKeyframesIfNecessary(*element_.Get(), *style,
                                                      nullptr);
 
-  EXPECT_EQ(2u,
-            effect1->GetPropertySpecificKeyframes(target_property1h).size());
-  EXPECT_FALSE(effect1->GetPropertySpecificKeyframes(target_property1h)[0]
-                   ->GetCompositorKeyframeValue());
+  const auto& keyframes1 =
+      *effect1->GetPropertySpecificKeyframes(target_property1h);
+  EXPECT_EQ(2u, keyframes1.size());
+  EXPECT_FALSE(keyframes1[0]->GetCompositorKeyframeValue());
   EXPECT_EQ(1u, effect1->Properties().size());
   EXPECT_TRUE(CheckCanStartEffectOnCompositor(timing_, *element_.Get(),
                                               animation1, *effect1) &
@@ -896,10 +896,10 @@
   effect2->SnapshotAllCompositorKeyframesIfNecessary(*inline_.Get(), *style,
                                                      nullptr);
 
-  EXPECT_EQ(2u,
-            effect2->GetPropertySpecificKeyframes(target_property2h).size());
-  EXPECT_TRUE(effect2->GetPropertySpecificKeyframes(target_property2h)[0]
-                  ->GetCompositorKeyframeValue());
+  const auto& keyframes2 =
+      *effect2->GetPropertySpecificKeyframes(target_property2h);
+  EXPECT_EQ(2u, keyframes2.size());
+  EXPECT_TRUE(keyframes2[0]->GetCompositorKeyframeValue());
   EXPECT_EQ(1u, effect2->Properties().size());
   EXPECT_TRUE(CheckCanStartEffectOnCompositor(timing_, *inline_.Get(),
                                               animation2, *effect2) &
@@ -922,10 +922,10 @@
   effect3->SnapshotAllCompositorKeyframesIfNecessary(*element_.Get(), *style,
                                                      nullptr);
 
-  EXPECT_EQ(2u,
-            effect3->GetPropertySpecificKeyframes(target_property3h).size());
-  EXPECT_TRUE(effect3->GetPropertySpecificKeyframes(target_property3h)[0]
-                  ->GetCompositorKeyframeValue());
+  const auto& keyframes3 =
+      *effect3->GetPropertySpecificKeyframes(target_property3h);
+  EXPECT_EQ(2u, keyframes3.size());
+  EXPECT_TRUE(keyframes3[0]->GetCompositorKeyframeValue());
   EXPECT_EQ(1u, effect3->Properties().size());
   EXPECT_TRUE(CheckCanStartEffectOnCompositor(timing_, *element_.Get(),
                                               animation3, *effect3) &
diff --git a/third_party/blink/renderer/core/animation/element_animations.cc b/third_party/blink/renderer/core/animation/element_animations.cc
index 91375f7..375a6d8 100644
--- a/third_party/blink/renderer/core/animation/element_animations.cc
+++ b/third_party/blink/renderer/core/animation/element_animations.cc
@@ -173,4 +173,16 @@
   base_computed_style_ = nullptr;
 }
 
+bool ElementAnimations::AnimationsPreserveAxisAlignment() const {
+  for (const auto& entry : animations_) {
+    const Animation& animation = *entry.key;
+    DCHECK(animation.effect());
+    DCHECK(animation.effect()->IsKeyframeEffect());
+    const KeyframeEffect& effect = *ToKeyframeEffect(animation.effect());
+    if (!effect.AnimationsPreserveAxisAlignment())
+      return false;
+  }
+  return true;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/element_animations.h b/third_party/blink/renderer/core/animation/element_animations.h
index 63743d1c..b1ed18d 100644
--- a/third_party/blink/renderer/core/animation/element_animations.h
+++ b/third_party/blink/renderer/core/animation/element_animations.h
@@ -84,6 +84,8 @@
   void UpdateBaseComputedStyle(const ComputedStyle*);
   void ClearBaseComputedStyle();
 
+  bool AnimationsPreserveAxisAlignment() const;
+
   void Trace(blink::Visitor*);
 
  private:
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc
index 4a39dd0e..3fb57f27 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -32,6 +32,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/unrestricted_double_or_keyframe_effect_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+#include "third_party/blink/renderer/core/animation/css/compositor_keyframe_transform.h"
 #include "third_party/blink/renderer/core/animation/effect_input.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/animation/sampled_effect.h"
@@ -328,6 +329,47 @@
   AnimationEffect::Trace(visitor);
 }
 
+bool KeyframeEffect::AnimationsPreserveAxisAlignment(
+    const PropertyHandle& property) const {
+  const auto* keyframes = Model()->GetPropertySpecificKeyframes(property);
+  if (!keyframes)
+    return true;
+  for (const auto& keyframe : *keyframes) {
+    const auto* value = keyframe->GetCompositorKeyframeValue();
+    if (!value)
+      continue;
+    DCHECK(value->IsTransform());
+    const auto& transform_operations =
+        ToCompositorKeyframeTransform(value)->GetTransformOperations();
+    if (!transform_operations.PreservesAxisAlignment())
+      return false;
+  }
+  return true;
+}
+
+namespace {
+
+static const size_t num_transform_properties = 4;
+
+const CSSProperty** TransformProperties() {
+  static const CSSProperty* kTransformProperties[num_transform_properties] = {
+      &GetCSSPropertyTransform(), &GetCSSPropertyScale(),
+      &GetCSSPropertyRotate(), &GetCSSPropertyTranslate()};
+  return kTransformProperties;
+}
+
+}  // namespace
+
+bool KeyframeEffect::AnimationsPreserveAxisAlignment() const {
+  static const auto** properties = TransformProperties();
+  for (size_t i = 0; i < num_transform_properties; i++) {
+    if (!AnimationsPreserveAxisAlignment(PropertyHandle(*properties[i])))
+      return false;
+  }
+
+  return true;
+}
+
 EffectModel::CompositeOperation KeyframeEffect::CompositeInternal() const {
   return model_->Composite();
 }
@@ -477,14 +519,14 @@
   if (!target_->GetComputedStyle())
     return false;
 
-  bool affects_transform = Affects(PropertyHandle(GetCSSPropertyTransform())) ||
-                           Affects(PropertyHandle(GetCSSPropertyScale())) ||
-                           Affects(PropertyHandle(GetCSSPropertyRotate())) ||
-                           Affects(PropertyHandle(GetCSSPropertyTranslate()));
-
   if (HasActiveAnimationsOnCompositor()) {
-    if (target_->GetComputedStyle()->HasOffset() && affects_transform)
-      return true;
+    if (target_->GetComputedStyle()->HasOffset()) {
+      static const auto** properties = TransformProperties();
+      for (size_t i = 0; i < num_transform_properties; i++) {
+        if (Affects(PropertyHandle(*properties[i])))
+          return true;
+      }
+    }
     return HasMultipleTransformProperties();
   }
 
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.h b/third_party/blink/renderer/core/animation/keyframe_effect.h
index 5c5c7e6..5d43efc2 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.h
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.h
@@ -123,6 +123,8 @@
 
   void Trace(blink::Visitor*) override;
 
+  bool AnimationsPreserveAxisAlignment() const;
+
  private:
   EffectModel::CompositeOperation CompositeInternal() const;
 
@@ -140,6 +142,8 @@
   bool HasIncompatibleStyle() const;
   bool HasMultipleTransformProperties() const;
 
+  bool AnimationsPreserveAxisAlignment(const PropertyHandle&) const;
+
   Member<Element> target_;
   Member<KeyframeEffectModelBase> model_;
   Member<SampledEffect> sampled_effect_;
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model.h b/third_party/blink/renderer/core/animation/keyframe_effect_model.h
index 970d670..2d2a2e60 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model.h
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model.h
@@ -93,10 +93,13 @@
   CompositeOperation Composite() const { return composite_; }
   void SetComposite(CompositeOperation composite) { composite_ = composite; }
 
-  const PropertySpecificKeyframeVector& GetPropertySpecificKeyframes(
+  const PropertySpecificKeyframeVector* GetPropertySpecificKeyframes(
       const PropertyHandle& property) const {
     EnsureKeyframeGroups();
-    return keyframe_groups_->at(property)->Keyframes();
+    const auto keyframe_group_iter = keyframe_groups_->find(property);
+    if (keyframe_group_iter == keyframe_groups_->end())
+      return nullptr;
+    return &keyframe_group_iter->value->Keyframes();
   }
 
   using KeyframeGroupMap =
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc b/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
index 5c5e1b0..e7f8668b 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
@@ -162,7 +162,7 @@
   EXPECT_TRUE(effect->SnapshotAllCompositorKeyframesIfNecessary(
       *element, *style, nullptr));
 
-  return effect->GetPropertySpecificKeyframes(PropertyHandle(property_name));
+  return *effect->GetPropertySpecificKeyframes(PropertyHandle(property_name));
 }
 
 void ExpectProperty(CSSPropertyID property,
@@ -610,7 +610,7 @@
 
   auto* effect = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes);
   const StringPropertySpecificKeyframeVector& property_specific_keyframes =
-      effect->GetPropertySpecificKeyframes(
+      *effect->GetPropertySpecificKeyframes(
           PropertyHandle(GetCSSPropertyLeft()));
   EXPECT_EQ(3U, property_specific_keyframes.size());
   EXPECT_DOUBLE_EQ(0.0, property_specific_keyframes[0]->Offset());
@@ -636,10 +636,9 @@
   const CompositorKeyframeValue* value;
 
   // Compositor keyframe value should be empty before snapshot
-  value = effect
-              ->GetPropertySpecificKeyframes(
-                  PropertyHandle(GetCSSPropertyOpacity()))[0]
-              ->GetCompositorKeyframeValue();
+  const auto& empty_keyframes = *effect->GetPropertySpecificKeyframes(
+      PropertyHandle(GetCSSPropertyOpacity()));
+  value = empty_keyframes[0]->GetCompositorKeyframeValue();
   EXPECT_FALSE(value);
 
   // Snapshot should update first time after construction
@@ -654,10 +653,9 @@
       *element, *style, nullptr));
 
   // Compositor keyframe value should be available after snapshot
-  value = effect
-              ->GetPropertySpecificKeyframes(
-                  PropertyHandle(GetCSSPropertyOpacity()))[0]
-              ->GetCompositorKeyframeValue();
+  const auto& available_keyframes = *effect->GetPropertySpecificKeyframes(
+      PropertyHandle(GetCSSPropertyOpacity()));
+  value = available_keyframes[0]->GetCompositorKeyframeValue();
   EXPECT_TRUE(value);
   EXPECT_TRUE(value->IsDouble());
 }
@@ -675,10 +673,9 @@
       *element, *style, nullptr));
 
   const CompositorKeyframeValue* value;
-  value = effect
-              ->GetPropertySpecificKeyframes(
-                  PropertyHandle(GetCSSPropertyOpacity()))[0]
-              ->GetCompositorKeyframeValue();
+  const auto& keyframes = *effect->GetPropertySpecificKeyframes(
+      PropertyHandle(GetCSSPropertyOpacity()));
+  value = keyframes[0]->GetCompositorKeyframeValue();
   EXPECT_TRUE(value);
   EXPECT_TRUE(value->IsDouble());
 
@@ -689,10 +686,9 @@
   // Snapshot should update after changing keyframes
   EXPECT_TRUE(effect->SnapshotAllCompositorKeyframesIfNecessary(
       *element, *style, nullptr));
-  value = effect
-              ->GetPropertySpecificKeyframes(
-                  PropertyHandle(GetCSSPropertyFilter()))[0]
-              ->GetCompositorKeyframeValue();
+  const auto& updated_keyframes = *effect->GetPropertySpecificKeyframes(
+      PropertyHandle(GetCSSPropertyFilter()));
+  value = updated_keyframes[0]->GetCompositorKeyframeValue();
   EXPECT_TRUE(value);
   EXPECT_TRUE(value->IsFilterOperations());
 }
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_test.cc b/third_party/blink/renderer/core/animation/keyframe_effect_test.cc
index 5e805cb..7545f7e9 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_test.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_test.cc
@@ -42,6 +42,31 @@
         StringKeyframeVector());
   }
 
+  // Returns a two-frame effect updated styles.
+  KeyframeEffect* GetTwoFrameEffect(const CSSPropertyID& property,
+                                    const String& value_a,
+                                    const String& value_b) {
+    StringKeyframeVector keyframes(2);
+    keyframes[0] = MakeGarbageCollected<StringKeyframe>();
+    keyframes[0]->SetOffset(0.0);
+    keyframes[0]->SetCSSPropertyValue(
+        property, value_a, SecureContextMode::kInsecureContext, nullptr);
+    keyframes[1] = MakeGarbageCollected<StringKeyframe>();
+    keyframes[1]->SetOffset(1.0);
+    keyframes[1]->SetCSSPropertyValue(
+        property, value_b, SecureContextMode::kInsecureContext, nullptr);
+    auto* model = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes);
+    Timing timing;
+    auto* effect = MakeGarbageCollected<KeyframeEffect>(element, model, timing);
+    // Ensure GetCompositorKeyframeValue is updated which would normally happen
+    // when applying the animation styles.
+    UpdateAllLifecyclePhasesForTest();
+    model->SnapshotAllCompositorKeyframesIfNecessary(
+        *element, *element->GetComputedStyle(), nullptr);
+
+    return effect;
+  }
+
   Persistent<Element> element;
 };
 
@@ -188,7 +213,7 @@
 
   PropertyHandle property(GetCSSPropertyWidth());
   const PropertySpecificKeyframeVector& keyframes =
-      effect->Model()->GetPropertySpecificKeyframes(property);
+      *effect->Model()->GetPropertySpecificKeyframes(property);
 
   EXPECT_EQ(EffectModel::kCompositeReplace, keyframes[0]->Composite());
   EXPECT_EQ(EffectModel::kCompositeAdd, keyframes[1]->Composite());
@@ -469,4 +494,29 @@
               CompositorAnimations::kTargetHasMultipleTransformProperties);
 }
 
+TEST_F(KeyframeEffectTest, TranslationTransformsPreserveAxisAlignment) {
+  auto* effect =
+      GetTwoFrameEffect(CSSPropertyID::kTransform, "translate(10px, 10px)",
+                        "translate(20px, 20px)");
+  EXPECT_TRUE(effect->AnimationsPreserveAxisAlignment());
+}
+
+TEST_F(KeyframeEffectTest, ScaleTransformsPreserveAxisAlignment) {
+  auto* effect =
+      GetTwoFrameEffect(CSSPropertyID::kTransform, "scale(2)", "scale(3)");
+  EXPECT_TRUE(effect->AnimationsPreserveAxisAlignment());
+}
+
+TEST_F(KeyframeEffectTest, RotationTransformsDoNotPreserveAxisAlignment) {
+  auto* effect = GetTwoFrameEffect(CSSPropertyID::kTransform, "rotate(10deg)",
+                                   "rotate(20deg)");
+
+  EXPECT_FALSE(effect->AnimationsPreserveAxisAlignment());
+}
+
+TEST_F(KeyframeEffectTest, RotationsDoNotPreserveAxisAlignment) {
+  auto* effect = GetTwoFrameEffect(CSSPropertyID::kRotate, "10deg", "20deg");
+  EXPECT_FALSE(effect->AnimationsPreserveAxisAlignment());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.cc b/third_party/blink/renderer/core/clipboard/data_object_item.cc
index ff792028..0b4a308 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.cc
@@ -144,7 +144,8 @@
       data->AppendBytes(png_data.data(), png_data.size());
       const uint64_t length = data->length();
       auto blob = BlobDataHandle::Create(std::move(data), length);
-      return File::Create("image.png", CurrentTimeMS(), std::move(blob));
+      return File::Create("image.png", base::Time::Now().ToDoubleT() * 1000.0,
+                          std::move(blob));
     }
   }
 
diff --git a/third_party/blink/renderer/core/css/css_unset_value.h b/third_party/blink/renderer/core/css/css_unset_value.h
index 56ff720e..91f23e06 100644
--- a/third_party/blink/renderer/core/css/css_unset_value.h
+++ b/third_party/blink/renderer/core/css/css_unset_value.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_UNSET_VALUE_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/util/type_safety/pass_key.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
@@ -19,9 +20,7 @@
  public:
   static CSSUnsetValue* Create();
 
-  // Only construct through MakeGarbageCollected for the initial value. Use
-  // Create() to get the pooled value.
-  CSSUnsetValue() : CSSValue(kUnsetClass) {}
+  explicit CSSUnsetValue(util::PassKey<CSSValuePool>) : CSSValue(kUnsetClass) {}
 
   String CustomCSSText() const;
 
@@ -30,9 +29,6 @@
   void TraceAfterDispatch(blink::Visitor* visitor) {
     CSSValue::TraceAfterDispatch(visitor);
   }
-
- private:
-  friend class ::blink::CSSValuePool;
 };
 
 }  // namespace cssvalue
diff --git a/third_party/blink/renderer/core/css/css_value_pool.cc b/third_party/blink/renderer/core/css/css_value_pool.cc
index e6b18c2..8cfa8f7 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.cc
+++ b/third_party/blink/renderer/core/css/css_value_pool.cc
@@ -46,7 +46,7 @@
 CSSValuePool::CSSValuePool()
     : inherited_value_(MakeGarbageCollected<CSSInheritedValue>()),
       initial_value_(MakeGarbageCollected<CSSInitialValue>()),
-      unset_value_(MakeGarbageCollected<CSSUnsetValue>()),
+      unset_value_(MakeGarbageCollected<CSSUnsetValue>(PassKey())),
       invalid_variable_value_(MakeGarbageCollected<CSSInvalidVariableValue>()),
       color_transparent_(
           MakeGarbageCollected<CSSColorValue>(Color::kTransparent)),
diff --git a/third_party/blink/renderer/core/css/css_value_pool.h b/third_party/blink/renderer/core/css/css_value_pool.h
index 7fe38566..ce42ae5e 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.h
+++ b/third_party/blink/renderer/core/css/css_value_pool.h
@@ -28,6 +28,7 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/util/type_safety/pass_key.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_color_value.h"
 #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
@@ -51,6 +52,8 @@
     : public GarbageCollectedFinalized<CSSValuePool> {
 
  public:
+  using PassKey = util::PassKey<CSSValuePool>;
+
   // TODO(sashab): Make all the value pools store const CSSValues.
   static const int kMaximumCacheableIntegerValue = 255;
   using CSSColorValue = cssvalue::CSSColorValue;
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 07c432f4..3b3435a 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
@@ -145,6 +145,11 @@
 ScriptPromise DisplayLockContext::acquire(ScriptState* script_state,
                                           DisplayLockOptions* options) {
   TRACE_EVENT0("blink", "DisplayLockContext::acquire()");
+  if (!GetExecutionContext()) {
+    return GetRejectedPromise(script_state,
+                              rejection_names::kExecutionContextDestroyed);
+  }
+
   double timeout_ms = (options && options->hasTimeout())
                           ? options->timeout()
                           : kDefaultLockTimeoutMs;
@@ -264,10 +269,9 @@
 }
 
 bool DisplayLockContext::CleanupAndRejectCommitIfNotConnected() {
-  // If we don't have an element or we're not connected, then the process of
-  // committing is the same as just unlocking the element. Early out if
-  // those conditions *don't* hold.
-  if (element_ && ConnectedToView())
+  // If we're not connected, then the process of committing is the same as just
+  // unlocking the element. Early out if this conditions *doesn't* hold.
+  if (ConnectedToView())
     return false;
 
   state_ = kUnlocked;
@@ -809,7 +813,7 @@
 
   // If we became disconnected for any reason, then we should reject the
   // update promise and go back to the locked state.
-  if (!element_ || !ConnectedToView()) {
+  if (!ConnectedToView()) {
     FinishUpdateResolver(kReject, rejection_names::kElementIsDisconnected);
     update_budget_.reset();
 
@@ -870,8 +874,10 @@
   // 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())
+  if ((!ConnectedToView() && !update_budget_) || !document_ ||
+      !document_->GetPage()) {
     return;
+  }
 
   // Schedule an animation to perform the lifecycle phases.
   document_->GetPage()->Animator().ScheduleVisualUpdate(document_->GetFrame());
@@ -899,7 +905,7 @@
 void DisplayLockContext::TriggerTimeout() {
   // We might have started destroyed the element or started to shut down while
   // we're triggering a timeout. In that case, do nothing.
-  if (!element_ || !document_->Lifecycle().IsActive())
+  if (!element_ || !document_ || !document_->Lifecycle().IsActive())
     return;
   StartCommit();
 }
@@ -978,8 +984,7 @@
 }
 
 bool DisplayLockContext::ConnectedToView() const {
-  DCHECK(element_);
-  return element_->isConnected() && document_->View();
+  return element_ && document_ && element_->isConnected() && document_->View();
 }
 
 // Scoped objects implementation
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 31d52cb..7c054b10 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
@@ -1617,4 +1617,35 @@
   // gracefully exit (and not crash).
   test::RunPendingTasks();
 }
+
+class DisplayLockContextRenderingTest : public RenderingTest,
+                                        private ScopedDisplayLockingForTest {
+ public:
+  DisplayLockContextRenderingTest()
+      : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()),
+        ScopedDisplayLockingForTest(true) {}
+};
+
+TEST_F(DisplayLockContextRenderingTest, FrameDocumentRemovedWhileAcquire) {
+  SetHtmlInnerHTML(R"HTML(
+    <iframe id="frame"></iframe>
+  )HTML");
+  SetChildFrameHTML(R"HTML(
+    <style>
+      div {
+        contain: style layout;
+      }
+    </style>
+    <div id="target"></target>
+  )HTML");
+
+  auto* target = ChildDocument().getElementById("target");
+  GetDocument().getElementById("frame")->remove();
+
+  auto* script_state = ToScriptStateForMainWorld(GetDocument().GetFrame());
+  ScriptState::Scope scope(script_state);
+  DisplayLockOptions options;
+  target->getDisplayLockForBindings()->acquire(script_state, &options);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 154a9dd..ac89475 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2339,23 +2339,23 @@
 
 void Document::PropagateStyleToViewport() {
   DCHECK(InStyleRecalc());
-  if (!documentElement())
-    return;
-
   HTMLElement* body = this->body();
+  Element* document_element = this->documentElement();
 
+  const ComputedStyle* document_element_style =
+      document_element && documentElement()->GetLayoutObject()
+          ? documentElement()->GetComputedStyle()
+          : nullptr;
   const ComputedStyle* body_style =
-      body ? body->EnsureComputedStyle() : nullptr;
-  const ComputedStyle* document_style =
-      documentElement()->EnsureComputedStyle();
+      body && body->GetLayoutObject() ? body->GetComputedStyle() : nullptr;
 
   const ComputedStyle& viewport_style = GetLayoutView()->StyleRef();
   scoped_refptr<ComputedStyle> new_viewport_style =
       ComputedStyle::Clone(viewport_style);
   bool changed = false;
 
-#define PROPAGATE_FROM(source, getter, setter) \
-  PROPAGATE_VALUE(source->getter(), getter, setter);
+#define PROPAGATE_FROM(source, getter, setter, initial) \
+  PROPAGATE_VALUE(source ? source->getter() : initial, getter, setter);
 
 #define PROPAGATE_VALUE(value, getter, setter)     \
   if ((new_viewport_style->getter()) != (value)) { \
@@ -2365,22 +2365,22 @@
 
   // Writing mode and direction
   {
-    const ComputedStyle* direction_style = document_style;
-    if (body_style)
-      direction_style = body_style;
-    PROPAGATE_FROM(direction_style, GetWritingMode, SetWritingMode);
-    PROPAGATE_FROM(direction_style, Direction, SetDirection);
+    const ComputedStyle* direction_style =
+        body_style ? body_style : document_element_style;
+    PROPAGATE_FROM(direction_style, GetWritingMode, SetWritingMode,
+                   WritingMode::kHorizontalTb);
+    PROPAGATE_FROM(direction_style, Direction, SetDirection,
+                   TextDirection::kLtr);
   }
 
   // Background
   {
-    const ComputedStyle* background_style = document_style;
+    const ComputedStyle* background_style = document_element_style;
     // http://www.w3.org/TR/css3-background/#body-background
     // <html> root element with no background steals background from its first
     // <body> child.
     // Also see LayoutBoxModelObject::BackgroundTransfersToView()
-    if (IsHTMLHtmlElement(documentElement()) &&
-        document_style->Display() != EDisplay::kNone &&
+    if (body_style && IsHTMLHtmlElement(documentElement()) &&
         IsHTMLBodyElement(body) && !background_style->HasBackground()) {
       background_style = body_style;
     }
@@ -2389,7 +2389,7 @@
     FillLayer background_layers(EFillLayerType::kBackground, true);
     EImageRendering image_rendering = EImageRendering::kAuto;
 
-    if (background_style->Display() != EDisplay::kNone) {
+    if (background_style) {
       background_color = background_style->VisitedDependentColor(
           GetCSSPropertyBackgroundColor());
       background_layers = background_style->BackgroundLayers();
@@ -2424,98 +2424,115 @@
   // Overflow
   {
     const ComputedStyle* overflow_style = nullptr;
-    Element* viewport_element = ViewportDefiningElement();
-    DCHECK(viewport_element);
-    if (viewport_element == body) {
-      overflow_style = body_style;
-    } else {
-      DCHECK_EQ(viewport_element, documentElement());
-      overflow_style = document_style;
+    if (Element* viewport_element = ViewportDefiningElement()) {
+      if (viewport_element == body) {
+        overflow_style = body_style;
+      } else {
+        DCHECK_EQ(viewport_element, documentElement());
+        overflow_style = document_element_style;
 
-      // The body element has its own scrolling box, independent from the
-      // viewport.  This is a bit of a weird edge case in the CSS spec that we
-      // might want to try to eliminate some day (eg. for ScrollTopLeftInterop -
-      // see http://crbug.com/157855).
-      if (body_style && !body_style->IsOverflowVisible())
-        UseCounter::Count(*this, WebFeature::kBodyScrollsInAdditionToViewport);
+        // The body element has its own scrolling box, independent from the
+        // viewport.  This is a bit of a weird edge case in the CSS spec that we
+        // might want to try to eliminate some day (eg. for ScrollTopLeftInterop
+        // - see http://crbug.com/157855).
+        if (body_style && !body_style->IsOverflowVisible()) {
+          UseCounter::Count(*this,
+                            WebFeature::kBodyScrollsInAdditionToViewport);
+        }
+      }
     }
-    DCHECK(overflow_style);
 
     // TODO(954423, 952711): scroll-snap-* and overscroll-behavior (and most
-    // likely overflow-anchor) should be propagated from documet element and not
-    // the viewport defining element.
+    // likely overflow-anchor) should be propagated from the document element
+    // and not the viewport defining element.
 
     // We only propagate the properties related to snap container since viewport
     // defining element cannot be a snap area.
-    PROPAGATE_FROM(overflow_style, GetScrollSnapType, SetScrollSnapType);
-    PROPAGATE_FROM(overflow_style, ScrollPaddingTop, SetScrollPaddingTop);
-    PROPAGATE_FROM(overflow_style, ScrollPaddingRight, SetScrollPaddingRight);
-    PROPAGATE_FROM(overflow_style, ScrollPaddingBottom, SetScrollPaddingBottom);
-    PROPAGATE_FROM(overflow_style, ScrollPaddingLeft, SetScrollPaddingLeft);
+    PROPAGATE_FROM(overflow_style, GetScrollSnapType, SetScrollSnapType,
+                   cc::ScrollSnapType());
+    PROPAGATE_FROM(overflow_style, ScrollPaddingTop, SetScrollPaddingTop,
+                   Length());
+    PROPAGATE_FROM(overflow_style, ScrollPaddingRight, SetScrollPaddingRight,
+                   Length());
+    PROPAGATE_FROM(overflow_style, ScrollPaddingBottom, SetScrollPaddingBottom,
+                   Length());
+    PROPAGATE_FROM(overflow_style, ScrollPaddingLeft, SetScrollPaddingLeft,
+                   Length());
+
+    PROPAGATE_FROM(overflow_style, OverscrollBehaviorX, SetOverscrollBehaviorX,
+                   EOverscrollBehavior::kAuto);
+    PROPAGATE_FROM(overflow_style, OverscrollBehaviorY, SetOverscrollBehaviorY,
+                   EOverscrollBehavior::kAuto);
 
     // Counts any time scroll snapping and scroll padding break if we change its
     // viewport propagation logic. Scroll snapping only breaks if body has
     // non-none snap type that is different from the document one.
     // TODO(952711): Remove once propagation logic change is complete.
-    bool snap_type_is_different =
-        !overflow_style->GetScrollSnapType().is_none &&
-        (overflow_style->GetScrollSnapType() !=
-         document_style->GetScrollSnapType());
-    bool scroll_padding_is_different =
-        overflow_style->ScrollPaddingTop() !=
-            document_style->ScrollPaddingTop() ||
-        overflow_style->ScrollPaddingBottom() !=
-            document_style->ScrollPaddingBottom() ||
-        overflow_style->ScrollPaddingLeft() !=
-            document_style->ScrollPaddingLeft() ||
-        overflow_style->ScrollPaddingRight() !=
-            document_style->ScrollPaddingRight();
+    if (document_element_style && body_style) {
+      bool snap_type_is_different =
+          !body_style->GetScrollSnapType().is_none &&
+          (body_style->GetScrollSnapType() !=
+           document_element_style->GetScrollSnapType());
+      bool scroll_padding_is_different =
+          body_style->ScrollPaddingTop() !=
+              document_element_style->ScrollPaddingTop() ||
+          body_style->ScrollPaddingBottom() !=
+              document_element_style->ScrollPaddingBottom() ||
+          body_style->ScrollPaddingLeft() !=
+              document_element_style->ScrollPaddingLeft() ||
+          body_style->ScrollPaddingRight() !=
+              document_element_style->ScrollPaddingRight();
 
-    if (snap_type_is_different) {
-      UseCounter::Count(*this, WebFeature::kScrollSnapOnViewportBreaks);
-    }
-    if (scroll_padding_is_different) {
-      UseCounter::Count(*this, WebFeature::kScrollPaddingOnViewportBreaks);
+      if (snap_type_is_different) {
+        UseCounter::Count(*this, WebFeature::kScrollSnapOnViewportBreaks);
+      }
+      if (scroll_padding_is_different) {
+        UseCounter::Count(*this, WebFeature::kScrollPaddingOnViewportBreaks);
+      }
     }
 
-    PROPAGATE_FROM(overflow_style, OverscrollBehaviorX, SetOverscrollBehaviorX);
-    PROPAGATE_FROM(overflow_style, OverscrollBehaviorY, SetOverscrollBehaviorY);
+    EOverflow overflow_x = EOverflow::kAuto;
+    EOverflow overflow_y = EOverflow::kAuto;
+    EOverflowAnchor overflow_anchor = EOverflowAnchor::kAuto;
 
-    EOverflow overflow_x = overflow_style->OverflowX();
-    EOverflow overflow_y = overflow_style->OverflowY();
-    EOverflowAnchor overflow_anchor = overflow_style->OverflowAnchor();
+    if (overflow_style) {
+      overflow_x = overflow_style->OverflowX();
+      overflow_y = overflow_style->OverflowY();
+      overflow_anchor = overflow_style->OverflowAnchor();
+      // Visible overflow on the viewport is meaningless, and the spec says to
+      // treat it as 'auto':
+      if (overflow_x == EOverflow::kVisible)
+        overflow_x = EOverflow::kAuto;
+      if (overflow_y == EOverflow::kVisible)
+        overflow_y = EOverflow::kAuto;
+      if (overflow_anchor == EOverflowAnchor::kVisible)
+        overflow_anchor = EOverflowAnchor::kAuto;
 
-    // Visible overflow on the viewport is meaningless, and the spec says to
-    // treat it as 'auto':
-    if (overflow_x == EOverflow::kVisible)
-      overflow_x = EOverflow::kAuto;
-    if (overflow_y == EOverflow::kVisible)
-      overflow_y = EOverflow::kAuto;
-    if (overflow_anchor == EOverflowAnchor::kVisible)
-      overflow_anchor = EOverflowAnchor::kAuto;
+      if (IsInMainFrame()) {
+        using OverscrollBehaviorType =
+            cc::OverscrollBehavior::OverscrollBehaviorType;
+        GetPage()->GetChromeClient().SetOverscrollBehavior(
+            *GetFrame(),
+            cc::OverscrollBehavior(static_cast<OverscrollBehaviorType>(
+                                       overflow_style->OverscrollBehaviorX()),
+                                   static_cast<OverscrollBehaviorType>(
+                                       overflow_style->OverscrollBehaviorY())));
+      }
+    }
 
     PROPAGATE_VALUE(overflow_x, OverflowX, SetOverflowX)
     PROPAGATE_VALUE(overflow_y, OverflowY, SetOverflowY)
     PROPAGATE_VALUE(overflow_anchor, OverflowAnchor, SetOverflowAnchor);
-
-    if (IsInMainFrame()) {
-      using OverscrollBehaviorType =
-          cc::OverscrollBehavior::OverscrollBehaviorType;
-      GetPage()->GetChromeClient().SetOverscrollBehavior(
-          *GetFrame(),
-          cc::OverscrollBehavior(static_cast<OverscrollBehaviorType>(
-                                     overflow_style->OverscrollBehaviorX()),
-                                 static_cast<OverscrollBehaviorType>(
-                                     overflow_style->OverscrollBehaviorY())));
-    }
   }
 
   // Misc
   {
-    PROPAGATE_FROM(document_style, GetEffectiveTouchAction,
-                   SetEffectiveTouchAction);
-    PROPAGATE_FROM(document_style, GetScrollBehavior, SetScrollBehavior);
-    PROPAGATE_FROM(document_style, DarkColorScheme, SetDarkColorScheme);
+    PROPAGATE_FROM(document_element_style, GetEffectiveTouchAction,
+                   SetEffectiveTouchAction, TouchAction::kTouchActionAuto);
+    PROPAGATE_FROM(document_element_style, GetScrollBehavior, SetScrollBehavior,
+                   kScrollBehaviorAuto);
+    PROPAGATE_FROM(document_element_style, DarkColorScheme, SetDarkColorScheme,
+                   false);
   }
 
   if (changed) {
@@ -3742,7 +3759,7 @@
   if (!root_element)
     return nullptr;
   const ComputedStyle* root_style = root_element->GetComputedStyle();
-  if (!root_style)
+  if (!root_style || root_style->IsEnsuredInDisplayNone())
     return nullptr;
   if (body_element && root_style->IsOverflowVisible() &&
       IsHTMLHtmlElement(*root_element))
@@ -5686,10 +5703,6 @@
 
   CountUse(WebFeature::kCookieGet);
 
-  // FIXME: The HTML5 DOM spec states that this attribute can raise an
-  // InvalidStateError exception on getting if the Document has no
-  // browsing context.
-
   if (!GetSecurityOrigin()->CanAccessCookies()) {
     if (IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
@@ -5716,10 +5729,6 @@
 
   UseCounter::Count(*this, WebFeature::kCookieSet);
 
-  // FIXME: The HTML5 DOM spec states that this attribute can raise an
-  // InvalidStateError exception on setting if the Document has no
-  // browsing context.
-
   if (!GetSecurityOrigin()->CanAccessCookies()) {
     if (IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
@@ -5859,9 +5868,10 @@
   // FIXME: If this document came from the file system, the HTML5
   // specificiation tells us to read the last modification date from the file
   // system.
-  if (!found_date)
+  if (!found_date) {
     date.SetMillisecondsSinceEpochForDateTime(
-        ConvertToLocalTime(CurrentTimeMS()));
+        ConvertToLocalTime(base::Time::Now().ToDoubleT() * 1000.0));
+  }
   return String::Format("%02d/%02d/%04d %02d:%02d:%02d", date.Month() + 1,
                         date.MonthDay(), date.FullYear(), date.Hour(),
                         date.Minute(), date.Second());
diff --git a/third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h b/third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h
index a2b872f5..a4a00d4 100644
--- a/third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h
+++ b/third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOM_HIGH_RES_TIME_STAMP_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOM_HIGH_RES_TIME_STAMP_H_
 
-#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "base/time/time.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl
index 84a5afb..a6ea0464 100644
--- a/third_party/blink/renderer/core/dom/element.idl
+++ b/third_party/blink/renderer/core/dom/element.idl
@@ -144,6 +144,9 @@
 
     // Display locking. Returns a display lock context.
     [RuntimeEnabled=DisplayLocking, ImplementedAs=getDisplayLockForBindings] readonly attribute DisplayLockContext displayLock;
+
+    // Element Timing
+    [RuntimeEnabled=ElementTiming, Affects=Nothing, CEReactions, Reflect=elementtiming] attribute DOMString elementTiming;
 };
 
 Element includes ParentNode;
diff --git a/third_party/blink/renderer/core/events/message_event.cc b/third_party/blink/renderer/core/events/message_event.cc
index bf2ce4c..f941210 100644
--- a/third_party/blink/renderer/core/events/message_event.cc
+++ b/third_party/blink/renderer/core/events/message_event.cc
@@ -126,7 +126,8 @@
                            EventTarget* source,
                            Vector<MessagePortChannel> channels,
                            UserActivation* user_activation,
-                           bool transfer_user_activation)
+                           bool transfer_user_activation,
+                           bool allow_autoplay)
     : Event(event_type_names::kMessage, Bubbles::kNo, Cancelable::kNo),
       data_type_(kDataTypeSerializedScriptValue),
       data_as_serialized_script_value_(
@@ -136,7 +137,8 @@
       source_(source),
       channels_(std::move(channels)),
       user_activation_(user_activation),
-      transfer_user_activation_(transfer_user_activation) {
+      transfer_user_activation_(transfer_user_activation),
+      allow_autoplay_(allow_autoplay) {
   DCHECK(IsValidSource(source_.Get()));
 }
 
@@ -216,7 +218,8 @@
                                     EventTarget* source,
                                     MessagePortArray* ports,
                                     UserActivation* user_activation,
-                                    bool transfer_user_activation) {
+                                    bool transfer_user_activation,
+                                    bool allow_autoplay) {
   if (IsBeingDispatched())
     return;
 
@@ -233,6 +236,7 @@
   is_ports_dirty_ = true;
   user_activation_ = user_activation;
   transfer_user_activation_ = transfer_user_activation;
+  allow_autoplay_ = allow_autoplay;
 }
 
 void MessageEvent::initMessageEvent(const AtomicString& type,
diff --git a/third_party/blink/renderer/core/events/message_event.h b/third_party/blink/renderer/core/events/message_event.h
index 9a8b01f..13462d96 100644
--- a/third_party/blink/renderer/core/events/message_event.h
+++ b/third_party/blink/renderer/core/events/message_event.h
@@ -79,10 +79,11 @@
                               const String& last_event_id = String(),
                               EventTarget* source = nullptr,
                               UserActivation* user_activation = nullptr,
-                              bool transfer_user_activation = false) {
+                              bool transfer_user_activation = false,
+                              bool allow_autoplay = false) {
     return MakeGarbageCollected<MessageEvent>(
         std::move(data), origin, last_event_id, source, std::move(channels),
-        user_activation, transfer_user_activation);
+        user_activation, transfer_user_activation, allow_autoplay);
   }
   static MessageEvent* CreateError(const String& origin = String(),
                                    EventTarget* source = nullptr) {
@@ -121,7 +122,8 @@
                EventTarget* source,
                Vector<MessagePortChannel>,
                UserActivation* user_activation,
-               bool transfer_user_activation);
+               bool transfer_user_activation,
+               bool allow_autoplay);
   // Creates a "messageerror" event.
   MessageEvent(const String& origin, EventTarget* source);
   MessageEvent(const String& data, const String& origin);
@@ -146,7 +148,8 @@
                         EventTarget* source,
                         MessagePortArray*,
                         UserActivation* user_activation,
-                        bool transfer_user_activation = false);
+                        bool transfer_user_activation = false,
+                        bool allow_autoplay = false);
   void initMessageEvent(const AtomicString& type,
                         bool bubbles,
                         bool cancelable,
@@ -165,6 +168,7 @@
   bool isPortsDirty() const { return is_ports_dirty_; }
   UserActivation* userActivation() const { return user_activation_; }
   bool transferUserActivation() const { return transfer_user_activation_; }
+  bool allowAutoplay() const { return allow_autoplay_; }
 
   Vector<MessagePortChannel> ReleaseChannels() { return std::move(channels_); }
 
@@ -231,6 +235,7 @@
   Vector<MessagePortChannel> channels_;
   Member<UserActivation> user_activation_;
   bool transfer_user_activation_ = false;
+  bool allow_autoplay_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_dom_message_event.cc b/third_party/blink/renderer/core/exported/web_dom_message_event.cc
index 3b15ac8..63d0f3ef 100644
--- a/third_party/blink/renderer/core/exported/web_dom_message_event.cc
+++ b/third_party/blink/renderer/core/exported/web_dom_message_event.cc
@@ -92,7 +92,7 @@
   Unwrap<MessageEvent>()->initMessageEvent(
       "message", false, false, std::move(msg.message), origin,
       "" /*lastEventId*/, window, ports, user_activation,
-      msg.transfer_user_activation);
+      msg.transfer_user_activation, msg.allow_autoplay);
 }
 
 WebString WebDOMMessageEvent::Origin() const {
@@ -105,6 +105,7 @@
   msg.ports = Unwrap<MessageEvent>()->ReleaseChannels();
   msg.transfer_user_activation =
       Unwrap<MessageEvent>()->transferUserActivation();
+  msg.allow_autoplay = Unwrap<MessageEvent>()->allowAutoplay();
   UserActivation* user_activation = Unwrap<MessageEvent>()->userActivation();
   TransferableMessage transferable_msg = ToTransferableMessage(std::move(msg));
   if (user_activation) {
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer.cc b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
index 84e4e65..ded2118 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
@@ -228,7 +228,8 @@
   // srcset prevents the problem. Long term we should make sure to MHTML plays
   // nicely with srcset.
   if (IsHTMLImageElement(element) &&
-      attribute.LocalName() == html_names::kSrcsetAttr) {
+      (attribute.LocalName() == html_names::kSrcsetAttr ||
+       attribute.LocalName() == html_names::kSizesAttr)) {
     return true;
   }
 
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
index 82bbb45..7dba928 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
@@ -227,8 +227,17 @@
   String mhtml =
       GenerateMHTMLFromHtml("http://www.test.com", "img_srcset.html");
 
-  // srcset attribute should be skipped.
+  // srcset and sizes attributes should be skipped.
   EXPECT_EQ(WTF::kNotFound, mhtml.Find("srcset="));
+  EXPECT_EQ(WTF::kNotFound, mhtml.Find("sizes="));
+
+  // src attribute with original URL should be preserved.
+  EXPECT_EQ(2,
+            MatchSubstring(mhtml, "src=3D\"http://www.test.com/1x.png\"", 34));
+
+  // The image resource for original URL should be attached.
+  EXPECT_NE(WTF::kNotFound,
+            mhtml.Find("Content-Location: http://www.test.com/1x.png"));
 
   // Width and height attributes should be set when none is present in <img>.
   EXPECT_NE(WTF::kNotFound,
@@ -248,8 +257,17 @@
   String mhtml =
       GenerateMHTMLFromHtml("http://www.test.com", "img_srcset.html");
 
-  // srcset attribute should be skipped.
+  // srcset and sizes attributes should be skipped.
   EXPECT_EQ(WTF::kNotFound, mhtml.Find("srcset="));
+  EXPECT_EQ(WTF::kNotFound, mhtml.Find("sizes="));
+
+  // src attribute with original URL should be preserved.
+  EXPECT_EQ(2,
+            MatchSubstring(mhtml, "src=3D\"http://www.test.com/1x.png\"", 34));
+
+  // The image resource for original URL should be attached.
+  EXPECT_NE(WTF::kNotFound,
+            mhtml.Find("Content-Location: http://www.test.com/1x.png"));
 
   // New width and height attributes should not be set.
   EXPECT_NE(WTF::kNotFound, mhtml.Find("id=3D\"i1\">"));
diff --git a/third_party/blink/renderer/core/exported/web_layer_test.cc b/third_party/blink/renderer/core/exported/web_layer_test.cc
index a165021a..915e9891 100644
--- a/third_party/blink/renderer/core/exported/web_layer_test.cc
+++ b/third_party/blink/renderer/core/exported/web_layer_test.cc
@@ -1044,4 +1044,37 @@
   EXPECT_FALSE(effect_node->HasRenderSurface());
 }
 
+TEST_P(WebLayerListSimTest, NoRenderSurfaceWithAxisAlignedTransformAnimation) {
+  InitializeWithHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        @keyframes translation {
+          0% { transform: translate(10px, 11px); }
+          100% { transform: translate(20px, 21px); }
+        }
+        .animate {
+          animation-name: translation;
+          animation-duration: 1s;
+          width: 100px;
+          height: 100px;
+          overflow: hidden;
+        }
+        .compchild {
+          height: 200px;
+          width: 10px;
+          background: lightblue;
+          will-change: transform;
+        }
+      </style>
+      <div class="animate"><div class="compchild"></div></div>
+  )HTML");
+  Compositor().BeginFrame();
+  // No effect node with kClipAxisAlignment should be created because the
+  // animation is axis-aligned.
+  for (const auto& effect_node : GetPropertyTrees()->effect_tree.nodes()) {
+    EXPECT_NE(cc::RenderSurfaceReason::kClipAxisAlignment,
+              effect_node.render_surface_reason);
+  }
+}
+
 }  // namespace blink
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 6a75713..414e7de2 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3286,13 +3286,17 @@
     root_graphics_layer_ = nullptr;
     visual_viewport_container_layer_ = nullptr;
     root_layer_ = nullptr;
-
-    // This path is called when the local main frame is being detached, which
-    // will destroy the WebWidgetClient in the future, but does not right now.
-    // TODO(crbug.com/419087): There should be no need to clean up the
-    // WebWidgetClient once the RenderWidget is destroyed with the main frame.
     AsWidget().client->SetRootLayer(nullptr);
     AsWidget().client->RegisterViewportLayers(cc::ViewportLayers());
+
+    // When the document in an already-attached main frame is being replaced by
+    // a navigation then SetRootGraphicsLayer(nullptr) will be called. Since we
+    // are navigating, defer BeginMainFrames until the new document is ready for
+    // them.
+    //
+    // TODO(crbug.com/936696): This should not be needed once we always swap
+    // frames when swapping documents.
+    scoped_defer_main_frame_update_ = AsWidget().client->DeferMainFrameUpdate();
   }
 }
 
diff --git a/third_party/blink/renderer/core/fileapi/file.cc b/third_party/blink/renderer/core/fileapi/file.cc
index c53c0e3..28bd8a4c 100644
--- a/third_party/blink/renderer/core/fileapi/file.cc
+++ b/third_party/blink/renderer/core/fileapi/file.cc
@@ -134,7 +134,7 @@
   if (options->hasLastModified())
     last_modified = static_cast<double>(options->lastModified());
   else
-    last_modified = CurrentTimeMS();
+    last_modified = base::Time::Now().ToDoubleT() * 1000.0;
   DCHECK(options->hasEndings());
   bool normalize_line_endings_to_native = options->endings() == "native";
   if (normalize_line_endings_to_native)
@@ -301,7 +301,7 @@
       IsValidFileTime(modification_time_ms))
     return modification_time_ms;
 
-  return CurrentTimeMS();
+  return base::Time::Now().ToDoubleT() * 1000.0;
 }
 
 int64_t File::lastModified() const {
@@ -310,7 +310,7 @@
   // The getter should return the current time when the last modification time
   // isn't known.
   if (!IsValidFileTime(modified_date))
-    modified_date = CurrentTimeMS();
+    modified_date = base::Time::Now().ToDoubleT() * 1000.0;
 
   // lastModified returns a number, not a Date instance,
   // http://dev.w3.org/2006/webapi/FileAPI/#file-attrs
@@ -323,7 +323,7 @@
   // The getter should return the current time when the last modification time
   // isn't known.
   if (!IsValidFileTime(modified_date))
-    modified_date = CurrentTimeMS();
+    modified_date = base::Time::Now().ToDoubleT() * 1000.0;
 
   // lastModifiedDate returns a Date instance,
   // http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index c7f11d4..ba49eb0 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -843,10 +843,11 @@
 }
 
 bool ContentSecurityPolicy::AllowTrustedTypeAssignmentFailure(
-    const String& message) const {
+    const String& message,
+    const String& sample) const {
   bool allow = true;
   for (const auto& policy : policies_) {
-    allow &= policy->AllowTrustedTypeAssignmentFailure(message);
+    allow &= policy->AllowTrustedTypeAssignmentFailure(message, sample);
   }
   return allow;
 }
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 75f8137..cb2b5a4 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
@@ -340,7 +340,8 @@
 
   // Determine whether to enforce the assignment failure. Also handle reporting.
   // Returns whether enforcing Trusted Types CSP directives are present.
-  bool AllowTrustedTypeAssignmentFailure(const String& message) const;
+  bool AllowTrustedTypeAssignmentFailure(const String& message,
+                                         const String& sample = String()) const;
 
   void UsesScriptHashAlgorithms(uint8_t content_security_policy_hash_algorithm);
   void UsesStyleHashAlgorithms(uint8_t content_security_policy_hash_algorithm);
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 8fb0a2e..ff18a796 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -183,7 +183,8 @@
     const String& console_message,
     const KURL& blocked_url,
     ResourceRequest::RedirectStatus redirect_status,
-    ContentSecurityPolicy::ViolationType violation_type) const {
+    ContentSecurityPolicy::ViolationType violation_type,
+    const String& sample) const {
   String message =
       IsReportOnly() ? "[Report Only] " + console_message : console_message;
   policy_->LogToConsole(
@@ -194,7 +195,9 @@
                            header_type_, violation_type,
                            std::unique_ptr<SourceLocation>(),
                            nullptr,  // localFrame
-                           redirect_status);
+                           redirect_status,
+                           nullptr,  // Element*
+                           sample);
 }
 
 void CSPDirectiveList::ReportViolationWithFrame(
@@ -327,7 +330,8 @@
 }
 
 bool CSPDirectiveList::AllowTrustedTypeAssignmentFailure(
-    const String& message) const {
+    const String& message,
+    const String& sample) const {
   if (!trusted_types_)
     return true;
 
@@ -335,7 +339,7 @@
                       ContentSecurityPolicy::DirectiveType::kTrustedTypes),
                   ContentSecurityPolicy::DirectiveType::kTrustedTypes, message,
                   KURL(), RedirectStatus::kFollowedRedirect,
-                  ContentSecurityPolicy::kTrustedTypesViolation);
+                  ContentSecurityPolicy::kTrustedTypesViolation, sample);
   return IsReportOnly();
 }
 
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
index fc9f93e2b..264b22da 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
@@ -100,7 +100,8 @@
                                     ResourceRequest::RedirectStatus,
                                     SecurityViolationReportingPolicy) const;
 
-  bool AllowTrustedTypeAssignmentFailure(const String& message) const;
+  bool AllowTrustedTypeAssignmentFailure(const String& message,
+                                         const String& sample) const;
 
   bool StrictMixedContentChecking() const {
     return strict_mixed_content_checking_enforced_;
@@ -200,7 +201,8 @@
                        const KURL& blocked_url,
                        ResourceRequest::RedirectStatus,
                        ContentSecurityPolicy::ViolationType violation_type =
-                           ContentSecurityPolicy::kURLViolation) const;
+                           ContentSecurityPolicy::kURLViolation,
+                       const String& sample = String()) const;
   void ReportViolationWithFrame(const String& directive_text,
                                 const ContentSecurityPolicy::DirectiveType,
                                 const String& console_message,
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 9fbcf595..8cf2031 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -465,15 +465,26 @@
   if (options->includeUserActivation())
     user_activation = UserActivation::CreateSnapshot(source);
 
+  LocalFrame* source_frame = source->GetFrame();
+
+  bool allow_autoplay = false;
+  if (RuntimeEnabledFeatures::ExperimentalAutoplayDynamicDelegationEnabled(
+          GetExecutionContext()) &&
+      LocalFrame::HasTransientUserActivation(source_frame) &&
+      options->hasAllow()) {
+    Vector<String> policy_entry_list;
+    options->allow().Split(' ', policy_entry_list);
+    allow_autoplay = policy_entry_list.Contains("autoplay");
+  }
+
   MessageEvent* event = MessageEvent::Create(
       std::move(channels), std::move(message), source_origin, String(), source,
-      user_activation, options->transferUserActivation());
+      user_activation, options->transferUserActivation(), allow_autoplay);
 
   // Transfer user activation state in the source's renderer when
   // |transferUserActivation| is true.
   // TODO(lanwei): we should execute the below code after the post task fires
   // (for both local and remote posting messages).
-  LocalFrame* source_frame = source->GetFrame();
   if (RuntimeEnabledFeatures::UserActivationPostMessageTransferEnabled() &&
       options->transferUserActivation() &&
       LocalFrame::HasTransientUserActivation(source_frame)) {
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 8bea126..69df9b1 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -382,13 +382,20 @@
   }
 
   if (const auto* image = ToHTMLImageElementOrNull(element)) {
-    // ImageSourceURL() works for both scenarios:
-    // * parent element is <picture>: best fit image URL from sibling source
-    //   element
-    // * single <img> element: image url contained in href attribute
-    KURL image_url = document.CompleteURL(image->ImageSourceURL());
+    AtomicString image_url_value;
+    const Element* parent = element.parentElement();
+    if (parent && IsHTMLPictureElement(parent)) {
+      // If parent element is <picture>, use ImageSourceURL() to get best fit
+      // image URL from sibling source.
+      image_url_value = image->ImageSourceURL();
+    } else {
+      // Otherwise, it is single <img> element. We should get image url
+      // contained in href attribute. ImageSourceURL() may return a different
+      // URL from srcset attribute.
+      image_url_value = image->getAttribute(html_names::kSrcAttr);
+    }
     ImageResourceContent* cached_image = image->CachedImage();
-    AddImageToResources(cached_image, image_url);
+    AddImageToResources(cached_image, document.CompleteURL(image_url_value));
   } else if (const auto* input = ToHTMLInputElementOrNull(element)) {
     if (input->type() == input_type_names::kImage && input->ImageLoader()) {
       KURL image_url = input->Src();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index a0e9ad3..3215cba 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -114,7 +114,7 @@
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_content_capture_client.h"
 #include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_dom_event.h"
+#include "third_party/blink/public/web/web_dom_message_event.h"
 #include "third_party/blink/public/web/web_form_element.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "third_party/blink/public/web/web_history_item.h"
@@ -2244,7 +2244,7 @@
 
 void WebLocalFrameImpl::DispatchMessageEventWithOriginCheck(
     const WebSecurityOrigin& intended_target_origin,
-    const WebDOMEvent& event,
+    const WebDOMMessageEvent& event,
     bool has_user_gesture) {
   DCHECK(!event.IsNull());
 
@@ -2258,15 +2258,27 @@
     UserGestureIndicator::SetWasForwardedCrossProcess();
   }
 
-  // Transfer user activation state in the target's renderer when
-  // |transferUserActivation| is true.
   MessageEvent* msg_event = static_cast<MessageEvent*>((Event*)event);
   Frame* source_frame = nullptr;
   if (msg_event->source() && msg_event->source()->ToDOMWindow())
     source_frame = msg_event->source()->ToDOMWindow()->GetFrame();
-  if (RuntimeEnabledFeatures::UserActivationPostMessageTransferEnabled() &&
-      msg_event->transferUserActivation()) {
+
+  // Transfer user activation state in the target's renderer when
+  // |transferUserActivation| is true.
+  //
+  // Also do the same as an ad-hoc solution to allow the origin trial of dynamic
+  // delegation of autoplay capability through postMessages.  Note that we
+  // skipped updating the user activation states in all other copies of the
+  // frame tree in this case because this is a temporary hack.
+  //
+  // TODO(mustaq): Remove the ad-hoc solution when the API shape is
+  // ready. crbug.com/985914
+  if ((RuntimeEnabledFeatures::UserActivationPostMessageTransferEnabled() &&
+       msg_event->transferUserActivation()) ||
+      msg_event->allowAutoplay()) {
     GetFrame()->TransferUserActivationFrom(source_frame);
+    if (msg_event->allowAutoplay())
+      UseCounter::Count(GetDocument(), WebFeature::kAutoplayDynamicDelegation);
   }
 
   GetFrame()->DomWindow()->DispatchMessageEventWithOriginCheck(
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 744a3f1..c9a5d1d 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -237,7 +237,7 @@
 
   void DispatchMessageEventWithOriginCheck(
       const WebSecurityOrigin& intended_target_origin,
-      const WebDOMEvent&,
+      const WebDOMMessageEvent&,
       bool has_user_gesture) override;
 
   WebRect GetSelectionBoundsRectForTesting() const override;
diff --git a/third_party/blink/renderer/core/frame/window_post_message_options.idl b/third_party/blink/renderer/core/frame/window_post_message_options.idl
index e078ea3..b82c6f7b 100644
--- a/third_party/blink/renderer/core/frame/window_post_message_options.idl
+++ b/third_party/blink/renderer/core/frame/window_post_message_options.idl
@@ -9,4 +9,6 @@
     [RuntimeEnabled =
         UserActivationPostMessageTransfer] boolean transferUserActivation =
         false;
+    [OriginTrialEnabled =
+        ExperimentalAutoplayDynamicDelegation] DOMString allow = "";
 };
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
index d6ec4af..0c2745c 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
@@ -129,6 +129,15 @@
   }
 }
 
+SkColorType GetColorTypeForConversion(SkColorType color_type) {
+  if (color_type == kRGBA_8888_SkColorType ||
+      color_type == kBGRA_8888_SkColorType) {
+    return color_type;
+  }
+
+  return kN32_SkColorType;
+}
+
 }  // anonymous namespace
 
 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
@@ -191,7 +200,9 @@
   // covnert to the requested color space and pixel format.
   if (function_type_ != kHTMLCanvasConvertToBlobPromise) {
     if (skia_image->colorSpace()) {
-      image_ = image_->ConvertToColorSpace(SkColorSpace::MakeSRGB());
+      image_ = image_->ConvertToColorSpace(
+          SkColorSpace::MakeSRGB(),
+          GetColorTypeForConversion(skia_image->colorType()));
       skia_image = image_->PaintImageForCurrentFrame().GetSkImage();
     }
 
@@ -212,7 +223,8 @@
       DCHECK(skia_image->colorSpace());
     }
 
-    SkColorType target_color_type = kN32_SkColorType;
+    SkColorType target_color_type =
+        GetColorTypeForConversion(skia_image->colorType());
     if (encode_options_->pixelFormat() == kRGBA16ImagePixelFormatName)
       target_color_type = kRGBA_F16_SkColorType;
     // We can do color space and color type conversion together.
diff --git a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
index 8d4cdb42..54f77cc 100644
--- a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
@@ -109,7 +109,8 @@
 }
 
 Decimal BaseTemporalInputType::DefaultValueForStepUp() const {
-  return Decimal::FromDouble(ConvertToLocalTime(CurrentTimeMS()));
+  return Decimal::FromDouble(
+      ConvertToLocalTime(base::Time::Now().ToDoubleT() * 1000.0));
 }
 
 bool BaseTemporalInputType::IsSteppable() const {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
index 35de0956..1c0b62e 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
@@ -664,7 +664,8 @@
 
 static int CurrentFullYear() {
   DateComponents date;
-  date.SetMillisecondsSinceEpochForMonth(ConvertToLocalTime(CurrentTimeMS()));
+  date.SetMillisecondsSinceEpochForMonth(
+      ConvertToLocalTime(base::Time::Now().ToDoubleT() * 1000.0));
   return date.FullYear();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/form_data.cc b/third_party/blink/renderer/core/html/forms/form_data.cc
index f550517e..bd44cb04 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.cc
+++ b/third_party/blink/renderer/core/html/forms/form_data.cc
@@ -359,7 +359,7 @@
   String filename = filename_;
   if (filename.IsNull())
     filename = "blob";
-  return File::Create(filename, CurrentTimeMS(),
+  return File::Create(filename, base::Time::Now().ToDoubleT() * 1000.0,
                       GetBlob()->GetBlobDataHandle());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/month_input_type.cc b/third_party/blink/renderer/core/html/forms/month_input_type.cc
index 00fc06f..50c9fc6 100644
--- a/third_party/blink/renderer/core/html/forms/month_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/month_input_type.cc
@@ -76,7 +76,8 @@
 
 Decimal MonthInputType::DefaultValueForStepUp() const {
   DateComponents date;
-  date.SetMillisecondsSinceEpochForMonth(ConvertToLocalTime(CurrentTimeMS()));
+  date.SetMillisecondsSinceEpochForMonth(
+      ConvertToLocalTime(base::Time::Now().ToDoubleT() * 1000.0));
   double months = date.MonthsSinceEpoch();
   DCHECK(std::isfinite(months));
   return Decimal::FromDouble(months);
diff --git a/third_party/blink/renderer/core/html/forms/time_input_type.cc b/third_party/blink/renderer/core/html/forms/time_input_type.cc
index 9d8170a..5f75f6f3 100644
--- a/third_party/blink/renderer/core/html/forms/time_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/time_input_type.cc
@@ -65,7 +65,8 @@
 
 Decimal TimeInputType::DefaultValueForStepUp() const {
   DateComponents date;
-  date.SetMillisecondsSinceMidnight(ConvertToLocalTime(CurrentTimeMS()));
+  date.SetMillisecondsSinceMidnight(
+      ConvertToLocalTime(base::Time::Now().ToDoubleT() * 1000.0));
   double milliseconds = date.MillisecondsSinceEpoch();
   DCHECK(std::isfinite(milliseconds));
   return Decimal::FromDouble(milliseconds);
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index b1020a9..04a0e8e3 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -57,6 +57,47 @@
 
 namespace blink {
 
+namespace {
+
+// Note: Here it covers download originated from clicking on <a download> link
+// that results in direct download. Features in this method can also be logged
+// from browser for download due to navigations to non-web-renderable content.
+bool ShouldInterveneDownloadByFramePolicy(LocalFrame* frame) {
+  bool should_intervene_download = false;
+  Document& document = *(frame->GetDocument());
+  UseCounter::Count(document, WebFeature::kDownloadPrePolicyCheck);
+  bool has_gesture = LocalFrame::HasTransientUserActivation(frame);
+  if (!has_gesture) {
+    UseCounter::Count(document, WebFeature::kDownloadWithoutUserGesture);
+  }
+  if (frame->IsAdSubframe()) {
+    UseCounter::Count(document, WebFeature::kDownloadInAdFrame);
+    if (!has_gesture) {
+      UseCounter::Count(document,
+                        WebFeature::kDownloadInAdFrameWithoutUserGesture);
+      if (base::FeatureList::IsEnabled(
+              blink::features::
+                  kBlockingDownloadsInAdFrameWithoutUserActivation))
+        should_intervene_download = true;
+    }
+  }
+  if (document.IsSandboxed(WebSandboxFlags::kDownloads)) {
+    UseCounter::Count(document, WebFeature::kDownloadInSandbox);
+    if (!has_gesture) {
+      UseCounter::Count(document,
+                        WebFeature::kDownloadInSandboxWithoutUserGesture);
+      if (RuntimeEnabledFeatures::
+              BlockingDownloadsInSandboxWithoutUserActivationEnabled())
+        should_intervene_download = true;
+    }
+  }
+  if (!should_intervene_download)
+    UseCounter::Count(document, WebFeature::kDownloadPostPolicyCheck);
+  return should_intervene_download;
+}
+
+}  // namespace
+
 using namespace html_names;
 
 HTMLAnchorElement::HTMLAnchorElement(Document& document)
@@ -390,33 +431,8 @@
   if (hasAttribute(kDownloadAttr) &&
       NavigationPolicyFromEvent(&event) != kNavigationPolicyDownload &&
       GetDocument().GetSecurityOrigin()->CanReadContent(completed_url)) {
-    UseCounter::Count(GetDocument(), WebFeature::kDownloadPrePolicyCheck);
-    bool has_gesture = LocalFrame::HasTransientUserActivation(frame);
-    if (frame->IsAdSubframe()) {
-      // Note: Here it covers download originated from clicking on <a download>
-      // link that results in direct download. These two features can also be
-      // logged from browser for download due to navigations to
-      // non-web-renderable content.
-      UseCounter::Count(GetDocument(),
-                        has_gesture
-                            ? WebFeature::kDownloadInAdFrameWithUserGesture
-                            : WebFeature::kDownloadInAdFrameWithoutUserGesture);
-      if (!has_gesture &&
-          base::FeatureList::IsEnabled(
-              blink::features::
-                  kBlockingDownloadsInAdFrameWithoutUserActivation))
-        return;
-    }
-    if (GetDocument().IsSandboxed(WebSandboxFlags::kDownloads)) {
-      if (!has_gesture) {
-        UseCounter::Count(GetDocument(),
-                          WebFeature::kDownloadInSandboxWithoutUserGesture);
-        if (RuntimeEnabledFeatures::
-                BlockingDownloadsInSandboxWithoutUserActivationEnabled())
-          return;
-      }
-    }
-    UseCounter::Count(GetDocument(), WebFeature::kDownloadPostPolicyCheck);
+    if (ShouldInterveneDownloadByFramePolicy(frame))
+      return;
     request.SetSuggestedFilename(
         static_cast<String>(FastGetAttribute(kDownloadAttr)));
     request.SetRequestContext(mojom::RequestContextType::DOWNLOAD);
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 20109961..71baf09 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -435,6 +435,7 @@
 }
 
 void MouseEventManager::MarkHoverStateDirty() {
+  LOG(ERROR) << "MouseEventManager::MarkHoverStateDirty ";
   DCHECK(RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled());
   DCHECK(frame_->IsLocalRoot());
   hover_state_dirty_ = true;
diff --git a/third_party/blink/renderer/core/inspector/console_message.cc b/third_party/blink/renderer/core/inspector/console_message.cc
index 029a7d1..19d5f8ba 100644
--- a/third_party/blink/renderer/core/inspector/console_message.cc
+++ b/third_party/blink/renderer/core/inspector/console_message.cc
@@ -93,7 +93,7 @@
       level_(level),
       message_(message),
       location_(std::move(location)),
-      timestamp_(WTF::CurrentTimeMS()),
+      timestamp_(base::Time::Now().ToDoubleT() * 1000.0),
       frame_(nullptr) {}
 
 ConsoleMessage::~ConsoleMessage() = default;
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 57521c2..728866b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -804,8 +804,8 @@
     maybe_frame_id = frame_id;
   GetFrontend()->requestWillBeSent(
       request_id, loader_id, documentURL, std::move(request_info),
-      base::TimeTicks::Now().since_origin().InSecondsF(), CurrentTime(),
-      std::move(initiator_object),
+      base::TimeTicks::Now().since_origin().InSecondsF(),
+      base::Time::Now().ToDoubleT(), std::move(initiator_object),
       BuildObjectForResourceResponse(redirect_response), resource_type,
       std::move(maybe_frame_id), request.HasUserGesture());
   if (is_handling_sync_xhr_)
@@ -1245,8 +1245,8 @@
           .build();
   GetFrontend()->webSocketWillSendHandshakeRequest(
       IdentifiersFactory::SubresourceRequestId(identifier),
-      base::TimeTicks::Now().since_origin().InSecondsF(), CurrentTime(),
-      std::move(request_object));
+      base::TimeTicks::Now().since_origin().InSecondsF(),
+      base::Time::Now().ToDoubleT(), std::move(request_object));
 }
 
 void InspectorNetworkAgent::DidReceiveWebSocketHandshakeResponse(
@@ -1362,6 +1362,8 @@
 
 Response InspectorNetworkAgent::disable() {
   DCHECK(!pending_request_type_);
+  if (IsMainThread())
+    GetNetworkStateNotifier().ClearOverride();
   instrumenting_agents_->RemoveInspectorNetworkAgent(this);
   agent_state_.ClearAllFields();
   resources_data_->Clear();
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.cc b/third_party/blink/renderer/core/inspector/thread_debugger.cc
index 675108b..de227eb 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.cc
@@ -190,7 +190,7 @@
 }
 
 double ThreadDebugger::currentTimeMS() {
-  return WTF::CurrentTimeMS();
+  return base::Time::Now().ToDoubleT() * 1000.0;
 }
 
 bool ThreadDebugger::isInspectableHeapObject(v8::Local<v8::Object> object) {
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index c5dbaf3e..402133b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -276,7 +276,8 @@
     return does_intersect;
   if (local_ancestor->HasOverflowClip()) {
     intersection_rect.Move(
-        -PhysicalOffset(local_ancestor->ScrolledContentOffset()));
+        -PhysicalOffset(LayoutPoint(local_ancestor->ScrollOrigin()) +
+                        local_ancestor->ScrolledContentOffset()));
   }
   LayoutRect root_clip_rect = root_rect.ToLayoutRect();
   // TODO(szager): This flipping seems incorrect because root_rect is already
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 77d00ef..a6b1d43 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -2269,6 +2269,9 @@
   if (!g_positioned_descendants_map)
     return;
 
+  if (LayoutBlockedByDisplayLock(DisplayLockContext::kChildren))
+    return;
+
   if (TrackedLayoutBoxListHashSet* positioned_descendant_set =
           PositionedObjects()) {
     TrackedLayoutBoxListHashSet::const_iterator end =
@@ -2277,7 +2280,10 @@
              positioned_descendant_set->begin();
          it != end; ++it) {
       LayoutBox* curr_box = *it;
-      DCHECK(!curr_box->NeedsLayout());
+      DCHECK(!curr_box->SelfNeedsLayout());
+      DCHECK(
+          curr_box->LayoutBlockedByDisplayLock(DisplayLockContext::kChildren) ||
+          !curr_box->NeedsLayout());
     }
   }
 }
diff --git a/third_party/blink/renderer/core/layout/layout_view_test.cc b/third_party/blink/renderer/core/layout/layout_view_test.cc
index 44988926..e42db57 100644
--- a/third_party/blink/renderer/core/layout/layout_view_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_view_test.cc
@@ -56,16 +56,13 @@
   LayoutObject* view = frame_doc->GetLayoutView();
   ASSERT_TRUE(view);
   EXPECT_FALSE(view->CanHaveChildren());
+  EXPECT_FALSE(frame_doc->documentElement()->GetComputedStyle());
 
   frame_doc->body()->SetInnerHTMLFromString(R"HTML(
     <div id="div"></div>
   )HTML");
 
-  frame_doc->Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
-  frame_doc->GetStyleEngine().RecalcStyle({});
-
-  Element* div = frame_doc->getElementById("div");
-  EXPECT_FALSE(div->GetComputedStyle());
+  EXPECT_FALSE(frame_doc->NeedsLayoutTreeUpdate());
 }
 
 struct HitTestConfig {
diff --git a/third_party/blink/renderer/core/loader/document_load_timing_test.cc b/third_party/blink/renderer/core/loader/document_load_timing_test.cc
index f837f61..bf97e47 100644
--- a/third_party/blink/renderer/core/loader/document_load_timing_test.cc
+++ b/third_party/blink/renderer/core/loader/document_load_timing_test.cc
@@ -23,7 +23,7 @@
   timing.SetNavigationStart(base::TimeTicks() + base::TimeDelta::FromSecondsD(
                                                     embedder_navigation_start));
 
-  double real_wall_time = CurrentTime();
+  double real_wall_time = base::Time::Now().ToDoubleT();
   base::TimeDelta adjusted_wall_time =
       timing.MonotonicTimeToPseudoWallTime(timing.NavigationStart());
 
@@ -46,7 +46,7 @@
   // Super quick load! Expect the wall time reported by this event to be
   // dominated by the navigationStartDelta, but similar to currentTime().
   timing.MarkLoadEventEnd();
-  double real_wall_load_event_end = CurrentTime();
+  double real_wall_load_event_end = base::Time::Now().ToDoubleT();
   base::TimeDelta adjusted_load_event_end =
       timing.MonotonicTimeToPseudoWallTime(timing.LoadEventEnd());
 
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index 2c2baa3113..e489d40a 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -58,7 +58,7 @@
   // Initialize to the current time to reduce the likelihood of generating
   // identifiers that overlap with those from past/future browser sessions.
   static int64_t next_identifier =
-      static_cast<int64_t>(CurrentTime() * 1000000.0);
+      static_cast<int64_t>(base::Time::Now().ToDoubleT() * 1000000.0);
   return ++next_identifier;
 }
 
diff --git a/third_party/blink/renderer/core/loader/history_item.cc b/third_party/blink/renderer/core/loader/history_item.cc
index 2c77a07c..b96d132 100644
--- a/third_party/blink/renderer/core/loader/history_item.cc
+++ b/third_party/blink/renderer/core/loader/history_item.cc
@@ -41,7 +41,8 @@
 static int64_t GenerateSequenceNumber() {
   // Initialize to the current time to reduce the likelihood of generating
   // identifiers that overlap with those from past/future browser sessions.
-  static int64_t next = static_cast<int64_t>(CurrentTime() * 1000000.0);
+  static int64_t next =
+      static_cast<int64_t>(base::Time::Now().ToDoubleT() * 1000000.0);
   return ++next;
 }
 
diff --git a/third_party/blink/renderer/core/loader/progress_tracker.cc b/third_party/blink/renderer/core/loader/progress_tracker.cc
index 3a0ae51..8b458a21 100644
--- a/third_party/blink/renderer/core/loader/progress_tracker.cc
+++ b/third_party/blink/renderer/core/loader/progress_tracker.cc
@@ -217,7 +217,7 @@
   if (progress_value_ < last_notified_progress_value_)
     return;
 
-  double now = CurrentTime();
+  double now = base::Time::Now().ToDoubleT();
   double notified_progress_time_delta = now - last_notified_progress_time_;
 
   double notification_progress_delta =
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
index 52a247ff..4d0e8b74 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
@@ -82,6 +82,7 @@
         message.user_activation->was_active);
   }
   result.transfer_user_activation = message.transfer_user_activation;
+  result.allow_autoplay = message.allow_autoplay;
 
   if (!message.array_buffer_contents_array.empty()) {
     SerializedScriptValue::ArrayBufferContentsArray array_buffer_contents_array;
@@ -150,6 +151,7 @@
         message.user_activation->was_active);
   }
   result.transfer_user_activation = message.transfer_user_activation;
+  result.allow_autoplay = message.allow_autoplay;
 
   auto& array_buffer_contents_array =
       message.message->GetArrayBufferContentsArray();
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.h b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
index d00868a..ccf6185 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.h
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
@@ -34,6 +34,7 @@
   mojom::blink::UserActivationSnapshotPtr user_activation;
 
   bool transfer_user_activation = false;
+  bool allow_autoplay = false;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BlinkTransferableMessage);
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.cc
index 180fe59..343c797 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.cc
@@ -52,6 +52,7 @@
       std::make_move_iterator(stream_channels.end()));
   out->has_user_gesture = data.has_user_gesture();
   out->transfer_user_activation = data.transfer_user_activation();
+  out->allow_autoplay = data.allow_autoplay();
 
   out->message->SetArrayBufferContentsArray(
       std::move(array_buffer_contents_array));
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.h b/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.h
index 53ee100b..9dcf78fa 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.h
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message_struct_traits.h
@@ -66,6 +66,10 @@
     return input.transfer_user_activation;
   }
 
+  static bool allow_autoplay(const blink::BlinkTransferableMessage& input) {
+    return input.allow_autoplay;
+  }
+
   static bool Read(blink::mojom::blink::TransferableMessage::DataView,
                    blink::BlinkTransferableMessage* out);
 };
diff --git a/third_party/blink/renderer/core/offscreencanvas/OWNERS b/third_party/blink/renderer/core/offscreencanvas/OWNERS
index ffa62011..5ef87b1aa 100644
--- a/third_party/blink/renderer/core/offscreencanvas/OWNERS
+++ b/third_party/blink/renderer/core/offscreencanvas/OWNERS
@@ -1,5 +1,4 @@
 fserb@chromium.org
-xidachen@chromium.org
 
 # TEAM: paint-dev@chromium.org
 # COMPONENT: Blink>Canvas
diff --git a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
index 2c238008..d73535f 100644
--- a/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc
@@ -15,14 +15,22 @@
                                           LocalFrame& frame,
                                           bool same_document_navigation) {
   FragmentAnchor* anchor = nullptr;
+  const bool text_fragment_identifiers_enabled =
+      RuntimeEnabledFeatures::TextFragmentIdentifiersEnabled(
+          frame.GetDocument());
 
-  anchor = ElementFragmentAnchor::TryCreate(url, frame);
+  if (text_fragment_identifiers_enabled) {
+    anchor = TextFragmentAnchor::TryCreateFragmentDirective(
+        url, frame, same_document_navigation);
+  }
+
   if (!anchor) {
-    if (RuntimeEnabledFeatures::TextFragmentIdentifiersEnabled(
-            frame.GetDocument())) {
-      anchor =
-          TextFragmentAnchor::TryCreate(url, frame, same_document_navigation);
-    }
+    anchor = ElementFragmentAnchor::TryCreate(url, frame);
+  }
+
+  if (!anchor && text_fragment_identifiers_enabled) {
+    anchor =
+        TextFragmentAnchor::TryCreate(url, frame, same_document_navigation);
   }
 
   return anchor;
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index b8a3dc3e..671cc77 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -23,9 +23,14 @@
 
 namespace {
 
+constexpr char kFragmentDirectivePrefix[] = "##";
 constexpr char kTextFragmentIdentifierPrefix[] = "targetText=";
-constexpr size_t kTextFragmentIdentifierPrefixArrayLength =
-    base::size(kTextFragmentIdentifierPrefix);
+
+// Subtract 1 because base::size includes the \0 string terminator.
+constexpr size_t kFragmentDirectivePrefixStringLength =
+    base::size(kFragmentDirectivePrefix) - 1;
+constexpr size_t kTextFragmentIdentifierPrefixStringLength =
+    base::size(kTextFragmentIdentifierPrefix) - 1;
 
 bool ParseTargetTextIdentifier(const String& fragment,
                                Vector<TextFragmentSelector>* out_selectors) {
@@ -37,8 +42,7 @@
     if (fragment.Find(kTextFragmentIdentifierPrefix, start_pos) != start_pos)
       return false;
 
-    // The prefix array length includes the \0 string terminator.
-    start_pos += kTextFragmentIdentifierPrefixArrayLength - 1;
+    start_pos += kTextFragmentIdentifierPrefixStringLength;
     end_pos = fragment.find('&', start_pos);
 
     String target_text;
@@ -54,26 +58,46 @@
   return true;
 }
 
+// For fragment directive style text fragment anchors, we strip the directive
+// from the fragment string to avoid breaking pages that rely on the fragment.
+// E.g. "#id##targetText=a" --> "#id"
+String StripFragmentDirective(const String& fragment) {
+  size_t start_pos = fragment.Find(kFragmentDirectivePrefix);
+  if (start_pos == kNotFound)
+    return fragment;
+
+  return fragment.Substring(0, start_pos);
+}
+
+bool CheckSecurityRestrictions(LocalFrame& frame,
+                               bool same_document_navigation) {
+  // For security reasons, we only allow text fragments on the main frame of a
+  // main window. So no iframes, no window.open. Also only on a full
+  // navigation.
+  if (frame.Tree().Parent() || frame.DomWindow()->opener() ||
+      same_document_navigation) {
+    return false;
+  }
+
+  // For security reasons, we only allow text fragment anchors for user or
+  // browser initiated navigations, i.e. no script navigations.
+  if (!(frame.Loader().GetDocumentLoader()->HadTransientActivation() ||
+        frame.Loader().GetDocumentLoader()->IsBrowserInitiated())) {
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace
 
 TextFragmentAnchor* TextFragmentAnchor::TryCreate(
     const KURL& url,
     LocalFrame& frame,
     bool same_document_navigation) {
-  // For security reasons, we only allow text fragments on the main frame of a
-  // main window. So no iframes, no window.open. Also only on a full
-  // navigation.
-  if (frame.Tree().Parent() || frame.DomWindow()->opener() ||
-      same_document_navigation)
+  if (!CheckSecurityRestrictions(frame, same_document_navigation))
     return nullptr;
 
-  // For security reasons, we only allow text fragment anchors for user or
-  // browser initiated navigations, i.e. no script navigations.
-  if (!(frame.Loader().GetDocumentLoader()->HadTransientActivation() ||
-        frame.Loader().GetDocumentLoader()->IsBrowserInitiated())) {
-    return nullptr;
-  }
-
   Vector<TextFragmentSelector> selectors;
 
   if (!ParseTargetTextIdentifier(url.FragmentIdentifier(), &selectors))
@@ -82,6 +106,40 @@
   return MakeGarbageCollected<TextFragmentAnchor>(selectors, frame);
 }
 
+TextFragmentAnchor* TextFragmentAnchor::TryCreateFragmentDirective(
+    const KURL& url,
+    LocalFrame& frame,
+    bool same_document_navigation) {
+  DCHECK(RuntimeEnabledFeatures::TextFragmentIdentifiersEnabled(
+      frame.GetDocument()));
+
+  if (!CheckSecurityRestrictions(frame, same_document_navigation))
+    return nullptr;
+
+  Vector<TextFragmentSelector> selectors;
+
+  // Add the hash to the beginning of the fragment identifier as it is
+  // part of parsing the ##targetText= prefix but is not included by
+  // KURL::FragmentIdentifier().
+  String fragment = "#" + url.FragmentIdentifier();
+  size_t directive_pos = fragment.Find(kFragmentDirectivePrefix);
+  if (directive_pos == kNotFound)
+    return nullptr;
+
+  size_t start_pos = directive_pos + kFragmentDirectivePrefixStringLength;
+  if (!ParseTargetTextIdentifier(fragment.Substring(start_pos), &selectors))
+    return nullptr;
+
+  // Strip the fragment directive from the document URL so that the page cannot
+  // see the directive, to avoid breaking pages that rely on the fragment.
+  String stripped_fragment = StripFragmentDirective(fragment).Substring(1);
+  KURL stripped_url(url);
+  stripped_url.SetFragmentIdentifier(stripped_fragment);
+  frame.GetDocument()->SetURL(std::move(stripped_url));
+
+  return MakeGarbageCollected<TextFragmentAnchor>(selectors, frame);
+}
+
 TextFragmentAnchor::TextFragmentAnchor(
     const Vector<TextFragmentSelector>& text_fragment_selectors,
     LocalFrame& frame)
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
index f787c6c..fa27ab0 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
@@ -26,6 +26,11 @@
                                        LocalFrame& frame,
                                        bool same_document_navigation);
 
+  static TextFragmentAnchor* TryCreateFragmentDirective(
+      const KURL& url,
+      LocalFrame& frame,
+      bool same_document_navigation);
+
   TextFragmentAnchor(
       const Vector<TextFragmentSelector>& text_fragment_selectors,
       LocalFrame& frame);
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
index 1d57026..b97dbb07 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
@@ -1113,6 +1113,105 @@
   EXPECT_EQ(14u, markers.at(0)->EndOffset());
 }
 
+// Test that the ##targetText fragment syntax works properly and is stripped
+// from the URL.
+TEST_F(TextFragmentAnchorTest, DoubleHashSyntax) {
+  SimRequest request("https://example.com/test.html##targetText=test",
+                     "text/html");
+  LoadURL("https://example.com/test.html##targetText=test");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 1200px;
+      }
+      p {
+        position: absolute;
+        top: 1000px;
+      }
+    </style>
+    <p id="text">This is a test page</p>
+  )HTML");
+  Compositor().BeginFrame();
+
+  RunAsyncMatchingTasks();
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  EXPECT_EQ(GetDocument().Url(), "https://example.com/test.html#");
+}
+
+// Test that the ##targetText fragment directive is stripped from the URL when
+// there's also non-directive fragment contents.
+TEST_F(TextFragmentAnchorTest, DoubleHashStrippedWithRemainingFragment) {
+  SimRequest request("https://example.com/test.html#element##targetText=test",
+                     "text/html");
+  LoadURL("https://example.com/test.html#element##targetText=test");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 1200px;
+      }
+      #text {
+        position: absolute;
+        top: 1000px;
+      }
+      #element {
+        position: absolute;
+        top: 2000px;
+      }
+    </style>
+    <p id="text">This is a test page</p>
+    <div id="element"></div>
+  )HTML");
+  Compositor().BeginFrame();
+
+  RunAsyncMatchingTasks();
+
+  EXPECT_EQ(GetDocument().Url(), "https://example.com/test.html#element");
+
+  Element& p = *GetDocument().getElementById("text");
+
+  EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(p)))
+      << "<p> Element wasn't scrolled into view, viewport's scroll offset: "
+      << LayoutViewport()->GetScrollOffset().ToString();
+}
+
+// If the fragment has a double hash, but the double hash isn't followed by a
+// valid targetText syntax, it should be interpreted as an element ID.
+TEST_F(TextFragmentAnchorTest, IdFragmentWithDoubleHash) {
+  SimRequest request("https://example.com/test.html#element##id", "text/html");
+  LoadURL("https://example.com/test.html#element##id");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 2200px;
+      }
+      p {
+        position: absolute;
+        top: 1000px;
+      }
+      div {
+        position: absolute;
+        top: 2000px;
+      }
+    </style>
+    <p id="element">This is a test page</p>
+    <div id="element##id"></div>
+  )HTML");
+  Compositor().BeginFrame();
+
+  RunAsyncMatchingTasks();
+
+  Element& div = *GetDocument().getElementById("element##id");
+
+  EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(div)))
+      << "Should have scrolled <div> into view but didn't, scroll offset: "
+      << LayoutViewport()->GetScrollOffset().ToString();
+}
+
 }  // namespace
 
 }  // namespace blink
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 54fd60c94..3607833 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
@@ -3250,6 +3250,37 @@
     // cases, fall back to painting the first kPixelDistanceToRecord pixels in
     // each direction.
 
+    // Note that since the interest rect mapping above can produce extremely
+    // large numbers in cases of perspective, try our best to "normalize" the
+    // result by ensuring that none of the rect dimensions exceed some large,
+    // but reasonable, limit.
+    const float reasonable_pixel_limit = std::numeric_limits<int>::max() / 2.f;
+    auto unpadded_intersection = local_interest_rect;
+
+    // Note that by clamping X and Y, we are effectively moving the rect right /
+    // down. However, this will at most make us paint more content, which is
+    // better than erroneously deciding that the rect produced here is far
+    // offscreen.
+    if (unpadded_intersection.X() < -reasonable_pixel_limit)
+      unpadded_intersection.SetX(-reasonable_pixel_limit);
+    if (unpadded_intersection.Y() < -reasonable_pixel_limit)
+      unpadded_intersection.SetY(-reasonable_pixel_limit);
+    if (unpadded_intersection.MaxX() > reasonable_pixel_limit) {
+      unpadded_intersection.SetWidth(reasonable_pixel_limit -
+                                     unpadded_intersection.X());
+    }
+    if (unpadded_intersection.MaxY() > reasonable_pixel_limit) {
+      unpadded_intersection.SetHeight(reasonable_pixel_limit -
+                                      unpadded_intersection.Y());
+    }
+
+    unpadded_intersection.Intersect(FloatRect(graphics_layer_bounds));
+    // If our unpadded intersection is not empty, then use that before padding,
+    // since it can produce more stable results, and it would not produce any
+    // smaller area than if we used the original local interest rect.
+    if (!unpadded_intersection.IsEmpty())
+      local_interest_rect = unpadded_intersection;
+
     // Expand by interest rect padding amount, scaled by the approximate scale
     // of the GraphicsLayer relative to screen pixels. If width or height
     // are zero or nearly zero, fall back to kPixelDistanceToRecord.
@@ -3264,7 +3295,10 @@
             : 1.0f;
     // Take the max, to account for situations like rotation transforms, which
     // swap x and y.
-    float scale = max(x_scale, y_scale);
+    // Since at this point we can also have an extremely large scale due to
+    // perspective (see the comments above), cap it to something reasonable.
+    float scale = std::min(std::max(x_scale, y_scale),
+                           reasonable_pixel_limit / kPixelDistanceToRecord);
     local_interest_rect.Inflate(kPixelDistanceToRecord * scale);
   } else {
     // Expand by interest rect padding amount.
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 2e34c930..64509cd 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
@@ -1229,6 +1229,7 @@
 void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() {
   UpdateScrollDimensions();
   UpdateScrollbarProportions();
+  UpdateScrollbarEnabledState();
 
   bool needs_horizontal_scrollbar;
   bool needs_vertical_scrollbar;
@@ -1249,6 +1250,7 @@
   }
 
   ClampScrollOffsetAfterOverflowChange();
+  UpdateScrollableAreaSet();
 }
 
 IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar(
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 9472f47f..aeae43ac 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
@@ -8,6 +8,7 @@
 
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/input/overscroll_behavior.h"
+#include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/frame/link_highlights.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -676,6 +677,20 @@
   return false;
 }
 
+static bool ActiveTransformAnimationIsAxisAligned(
+    const LayoutObject& object,
+    CompositingReasons compositing_reasons) {
+  if (!(compositing_reasons & CompositingReason::kActiveTransformAnimation))
+    return false;
+
+  if (!object.GetNode() || !object.GetNode()->IsElementNode())
+    return false;
+  const Element* element = To<Element>(object.GetNode());
+  const auto* animations = element->GetElementAnimations();
+  DCHECK(animations);
+  return animations->AnimationsPreserveAxisAlignment();
+}
+
 void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
   if (object_.IsSVGChild()) {
     UpdateTransformForNonRootSVG();
@@ -728,6 +743,13 @@
           state.direct_compositing_reasons =
               full_context_.direct_compositing_reasons &
               CompositingReasonsForTransformProperty();
+          // TODO(flackr): This only needs to consider composited transform
+          // animations. This is currently a cyclic dependency but we could
+          // calculate most of the compositable animation reasons up front to
+          // only consider animations which are candidates for compositing.
+          state.animation_is_axis_aligned =
+              ActiveTransformAnimationIsAxisAligned(
+                  object_, full_context_.direct_compositing_reasons);
         }
       }
 
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 114d527c..524cf4e 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
@@ -6875,4 +6875,49 @@
                false);
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, TransformAnimationAxisAlignment) {
+  if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
+    return;
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        @keyframes transform_translation {
+          0% { transform: translate(10px, 11px); }
+          100% { transform: translate(20px, 21px); }
+        }
+        #translation_animation {
+          animation-name: transform_translation;
+          animation-duration: 1s;
+          width: 100px;
+          height: 100px;
+          will-change: transform;
+        }
+        @keyframes transform_rotation {
+          0% { transform: rotateZ(10deg); }
+          100% { transform: rotateZ(20deg); }
+        }
+        #rotation_animation {
+          animation-name: transform_rotation;
+          animation-duration: 1s;
+          width: 100px;
+          height: 100px;
+          will-change: transform;
+        }
+      </style>
+      <div id="translation_animation"></div>
+      <div id="rotation_animation"></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  const auto* translation =
+      PaintPropertiesForElement("translation_animation")->Transform();
+  EXPECT_TRUE(translation->HasActiveTransformAnimation());
+  EXPECT_TRUE(translation->TransformAnimationIsAxisAligned());
+
+  const auto* rotation =
+      PaintPropertiesForElement("rotation_animation")->Transform();
+  EXPECT_TRUE(rotation->HasActiveTransformAnimation());
+  EXPECT_FALSE(rotation->TransformAnimationIsAxisAligned());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 1c4e75e1..f43af5e 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1622,7 +1622,23 @@
       !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
     return;
 
-  SetBodyInnerHTML("<div id='target' style='width: 100px; height: 100px'>");
+  SetBodyInnerHTML(R"HTML(
+      <!DOCTYPE html>
+      <style>
+        @keyframes animation {
+          0% { opacity: 0.3; }
+          100% { opacity: 0.4; }
+        }
+        #target {
+          animation-name: animation;
+          animation-duration: 1s;
+          width: 100px;
+          height: 100px;
+        }
+      </style>
+      <div id='target'></div>
+  )HTML");
+
   auto* target = GetLayoutObjectByElementId("target");
   auto style = ComputedStyle::Clone(target->StyleRef());
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
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 0d198d97..783dec8 100644
--- a/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm
+++ b/third_party/blink/renderer/core/scroll/scroll_animator_mac.mm
@@ -312,7 +312,7 @@
   ~BlinkScrollbarPartAnimationTimer() {}
 
   void Start() {
-    start_time_ = WTF::CurrentTime();
+    start_time_ = base::Time::Now().ToDoubleT();
     // Set the framerate of the animation. NSAnimation uses a default
     // framerate of 60 Hz, so use that here.
     timer_.StartRepeating(base::TimeDelta::FromSecondsD(1.0 / 60.0), FROM_HERE);
@@ -324,7 +324,7 @@
 
  private:
   void TimerFired(TimerBase*) {
-    double current_time = WTF::CurrentTime();
+    double current_time = base::Time::Now().ToDoubleT();
     double delta = current_time - start_time_;
 
     if (delta >= duration_)
diff --git a/third_party/blink/renderer/core/testing/data/frameserialization/img_srcset.html b/third_party/blink/renderer/core/testing/data/frameserialization/img_srcset.html
index 0f3dbe6..cceee51 100644
--- a/third_party/blink/renderer/core/testing/data/frameserialization/img_srcset.html
+++ b/third_party/blink/renderer/core/testing/data/frameserialization/img_srcset.html
@@ -4,7 +4,7 @@
 <title>Test Image with srcset</title>
 </head>
 <body>
-<img src="1x.png" id="i1" srcset="2x.png 2x">
+<img src="1x.png" id="i1" srcset="2x.png 2x" sizes="(max-width: 640px) 6px">
 <img src="1x.png" id="i2" width="8" srcset="2x.png 2x">
 </body>
 </html>
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
index 40d2483..2457079 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
@@ -38,6 +38,11 @@
   if (!element_ || !element_->isConnected() || element_->IsInShadowTree())
     return nullptr;
 
+  // Do not expose |element_| when the document is not 'fully active'.
+  const Document& document = element_->GetDocument();
+  if (!document.IsActive() || !document.GetFrame())
+    return nullptr;
+
   return element_;
 }
 
@@ -48,7 +53,7 @@
   builder.Add("loadTime", load_time_);
   builder.Add("id", id_);
   builder.Add("url", url_);
-  builder.Add("element", element_);
+  builder.Add("element", element());
 }
 
 void LargestContentfulPaint::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc
index 1a03016..f0cab81 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -26,13 +26,15 @@
   DCHECK_GE(naturalWidth, 0);
   DCHECK_GE(naturalHeight, 0);
   DCHECK(element);
+  double start_time = render_time != 0.0 ? render_time : load_time;
   return MakeGarbageCollected<PerformanceElementTiming>(
-      name, url, intersection_rect, render_time, load_time, identifier,
-      naturalWidth, naturalHeight, id, element);
+      name, start_time, url, intersection_rect, render_time, load_time,
+      identifier, naturalWidth, naturalHeight, id, element);
 }
 
 PerformanceElementTiming::PerformanceElementTiming(
     const AtomicString& name,
+    DOMHighResTimeStamp start_time,
     const String& url,
     const FloatRect& intersection_rect,
     DOMHighResTimeStamp render_time,
@@ -42,7 +44,7 @@
     int naturalHeight,
     const AtomicString& id,
     Element* element)
-    : PerformanceEntry(name, 0, 0),
+    : PerformanceEntry(name, start_time, start_time),
       element_(element),
       intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
       render_time_(render_time),
@@ -67,6 +69,11 @@
   if (!element_ || !element_->isConnected() || element_->IsInShadowTree())
     return nullptr;
 
+  // Do not expose |element_| when the document is not 'fully active'.
+  const Document& document = element_->GetDocument();
+  if (!document.IsActive() || !document.GetFrame())
+    return nullptr;
+
   return element_;
 }
 
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h
index f60fc2e0..ad6a994 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -32,6 +32,7 @@
                                           const AtomicString& id,
                                           Element*);
   PerformanceElementTiming(const AtomicString& name,
+                           DOMHighResTimeStamp start_time,
                            const String& url,
                            const FloatRect& intersection_rect,
                            DOMHighResTimeStamp render_time,
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 89cace0..948ffbc 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -24,11 +24,16 @@
 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_url.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
 namespace {
 
+// This value is derived from the Trusted Types spec (draft), and determines the
+// maximum length of the sample value in the violation reports.
+const unsigned kReportedValueMaximumLength = 40;
+
 enum TrustedTypeViolationKind {
   kAnyTrustedTypeAssignment,
   kTrustedHTMLAssignment,
@@ -81,6 +86,31 @@
   return "";
 }
 
+std::pair<String, String> GetMessageAndSample(
+    TrustedTypeViolationKind kind,
+    const ExceptionState& exception_state,
+    const String& value) {
+  const char* interface_name = exception_state.InterfaceName();
+  const char* property_name = exception_state.PropertyName();
+
+  // We have two sample formats, one for eval and one for assignment.
+  // If we don't have the required values being passed in, just leave the
+  // sample empty.
+  StringBuilder sample;
+  if (interface_name && strcmp("eval", interface_name) == 0) {
+    sample.Append("eval");
+  } else if (interface_name && property_name) {
+    sample.Append(interface_name);
+    sample.Append(".");
+    sample.Append(property_name);
+  }
+  if (!sample.IsEmpty()) {
+    sample.Append(" ");
+    sample.Append(value.Left(kReportedValueMaximumLength));
+  }
+  return std::make_pair<String, String>(GetMessage(kind), sample.ToString());
+}
+
 // Handle failure of a Trusted Type assignment.
 //
 // If trusted type assignment fails, we need to
@@ -91,7 +121,8 @@
 // Returns whether the failure should be enforced.
 bool TrustedTypeFail(TrustedTypeViolationKind kind,
                      const ExecutionContext* execution_context,
-                     ExceptionState& exception_state) {
+                     ExceptionState& exception_state,
+                     const String& value) {
   if (!execution_context)
     return true;
 
@@ -100,10 +131,12 @@
   if (execution_context->GetTrustedTypes())
     execution_context->GetTrustedTypes()->CountTrustedTypeAssignmentError();
 
-  const char* message = GetMessage(kind);
+  String message;
+  String sample;
+  std::tie(message, sample) = GetMessageAndSample(kind, exception_state, value);
   bool allow = execution_context->GetSecurityContext()
                    .GetContentSecurityPolicy()
-                   ->AllowTrustedTypeAssignmentFailure(message);
+                   ->AllowTrustedTypeAssignmentFailure(message, sample);
   if (!allow) {
     exception_state.ThrowTypeError(message);
   }
@@ -129,8 +162,9 @@
 
   if (string_or_trusted_type.IsString() &&
       RequireTrustedTypesCheck(execution_context)) {
-    TrustedTypeFail(kAnyTrustedTypeAssignment, execution_context,
-                    exception_state);
+    TrustedTypeFail(
+        kAnyTrustedTypeAssignment, execution_context, exception_state,
+        GetStringFromTrustedTypeWithoutCheck(string_or_trusted_type));
     return g_empty_string;
   }
 
@@ -243,7 +277,7 @@
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedHTMLAssignment, execution_context,
-                        exception_state)) {
+                        exception_state, string)) {
       return g_empty_string;
     }
     return string;
@@ -254,7 +288,7 @@
   if (exception_state.HadException()) {
     exception_state.ClearException();
     TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
-                    execution_context, exception_state);
+                    execution_context, exception_state, string);
     return g_empty_string;
   }
 
@@ -295,7 +329,7 @@
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedScriptAssignment, execution_context,
-                        exception_state)) {
+                        exception_state, potential_script)) {
       return g_empty_string;
     }
     return potential_script;
@@ -307,7 +341,7 @@
   if (exception_state.HadException()) {
     exception_state.ClearException();
     TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
-                    execution_context, exception_state);
+                    execution_context, exception_state, potential_script);
     return g_empty_string;
   }
 
@@ -319,35 +353,36 @@
     const ExecutionContext* execution_context,
     ExceptionState& exception_state) {
   DCHECK(!string_or_trusted_script_url.IsNull());
+  if (string_or_trusted_script_url.IsTrustedScriptURL()) {
+    return string_or_trusted_script_url.GetAsTrustedScriptURL()->toString();
+  }
+
+  DCHECK(string_or_trusted_script_url.IsString());
+  String string = string_or_trusted_script_url.GetAsString();
 
   bool require_trusted_type =
       RequireTrustedTypesCheck(execution_context) &&
       RuntimeEnabledFeatures::TrustedDOMTypesEnabled(execution_context);
-  if (!require_trusted_type && string_or_trusted_script_url.IsString()) {
-    return string_or_trusted_script_url.GetAsString();
-  }
-
-  if (string_or_trusted_script_url.IsTrustedScriptURL()) {
-    return string_or_trusted_script_url.GetAsTrustedScriptURL()->toString();
+  if (!require_trusted_type) {
+    return string;
   }
 
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedScriptURLAssignment, execution_context,
-                        exception_state)) {
+                        exception_state, string)) {
       return g_empty_string;
     }
-    return string_or_trusted_script_url.GetAsString();
+    return string;
   }
 
   TrustedScriptURL* result = default_policy->CreateScriptURL(
-      execution_context->GetIsolate(),
-      string_or_trusted_script_url.GetAsString(), exception_state);
+      execution_context->GetIsolate(), string, exception_state);
 
   if (exception_state.HadException()) {
     exception_state.ClearException();
     TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
-                    execution_context, exception_state);
+                    execution_context, exception_state, string);
     return g_empty_string;
   }
 
@@ -358,32 +393,33 @@
                                const ExecutionContext* execution_context,
                                ExceptionState& exception_state) {
   DCHECK(!string_or_trusted_url.IsNull());
-
-  bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
-  if (!require_trusted_type && string_or_trusted_url.IsUSVString()) {
-    return string_or_trusted_url.GetAsUSVString();
-  }
-
   if (string_or_trusted_url.IsTrustedURL()) {
     return string_or_trusted_url.GetAsTrustedURL()->toString();
   }
 
+  DCHECK(string_or_trusted_url.IsUSVString());
+  String string = string_or_trusted_url.GetAsUSVString();
+
+  bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
+  if (!require_trusted_type) {
+    return string;
+  }
+
   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
   if (!default_policy) {
     if (TrustedTypeFail(kTrustedURLAssignment, execution_context,
-                        exception_state)) {
+                        exception_state, string)) {
       return g_empty_string;
     }
-    return string_or_trusted_url.GetAsUSVString();
+    return string;
   }
 
   TrustedURL* result = default_policy->CreateURL(
-      execution_context->GetIsolate(), string_or_trusted_url.GetAsUSVString(),
-      exception_state);
+      execution_context->GetIsolate(), string, exception_state);
   if (exception_state.HadException()) {
     exception_state.ClearException();
     TrustedTypeFail(kTrustedURLAssignmentAndDefaultPolicyFailed,
-                    execution_context, exception_state);
+                    execution_context, exception_state, string);
     return g_empty_string;
   }
 
@@ -399,7 +435,8 @@
 
   TrustedTypePolicy* default_policy = GetDefaultPolicy(doc);
   if (!default_policy) {
-    return TrustedTypeFail(kTextNodeScriptAssignment, doc, exception_state)
+    return TrustedTypeFail(kTextNodeScriptAssignment, doc, exception_state,
+                           child->textContent())
                ? nullptr
                : child;
   }
@@ -409,7 +446,7 @@
   if (exception_state.HadException()) {
     exception_state.ClearException();
     return TrustedTypeFail(kTextNodeScriptAssignmentAndDefaultPolicyFailed, doc,
-                           exception_state)
+                           exception_state, child->textContent())
                ? nullptr
                : child;
   }
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index 7d01a82..537f373c 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -101,7 +101,7 @@
   "front_end/changes/changesSidebar.css",
   "front_end/changes/ChangesSidebar.js",
   "front_end/changes/module.json",
-  "front_end/cm/activeline.js",
+  "front_end/cm/active-line.js",
   "front_end/cm/brace-fold.js",
   "front_end/cm/closebrackets.js",
   "front_end/cm/codemirror.css",
@@ -109,7 +109,7 @@
   "front_end/cm/comment.js",
   "front_end/cm/foldcode.js",
   "front_end/cm/foldgutter.js",
-  "front_end/cm/markselection.js",
+  "front_end/cm/mark-selection.js",
   "front_end/cm/matchbrackets.js",
   "front_end/cm/module.json",
   "front_end/cm/multiplex.js",
diff --git a/third_party/blink/renderer/devtools/front_end/cm/README.md b/third_party/blink/renderer/devtools/front_end/cm/README.md
index 6a8ff1a..ab9ca0a 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/README.md
+++ b/third_party/blink/renderer/devtools/front_end/cm/README.md
@@ -5,13 +5,16 @@
 Every once in a while, the CodeMirror dependency (which is located in Source/devtools/front_end/cm/ folder) should be updated to a newer version.
 
 ## Updating CodeMirror
+
+Run `python devtools/scripts/roll_codemirror.js <codemirror_dir> <devtools_dir>`
+
+## Manual steps
 This requires the following steps to be done:
 1. File `headlesscodemirror.js` is a `runmode-standalone.js` file from CodeMirror distribution, but wrapped in `(function(window) { ... }(this))`
 construction. This is needed to support in web workers.
-2. File `markselection.js` is a `mark-selection.js` from CodeMirror distribution. The "dash" is removed due to the restriction on the chromium grd generator.
-3. File codemirror.css contains both the default theme of CodeMirror and structural css required for it to work. Discard everything in the file up to the word `/* STOP */`.
-4. All other files in front_end/cm/ folder should be substituted with their newer versions from the upstream. Note that some need to be renamed to remove `-` from the file name.
-5. All files in front_end/cm_web_modes/ and front_end/cm_modes/ should be updated with newer versions from upstream.
+2. File `codemirror.css` contains both the default theme of CodeMirror and structural css required for it to work. Discard everything in the file up to the word `/* STOP */`.
+3. All other files in `front_end/cm/` folder should be substituted with their newer versions from the upstream.
+4. All files in `front_end/cm_web_modes/` and `front_end/cm_modes/` should be updated with newer versions from upstream.
 
 ## Testing
 DevTools wrap CodeMirror via `CodeMirrorTextEditor.js` and `cmdevtools.css` files.
@@ -30,7 +33,7 @@
 3. Go to the Elements panel, select a node and verify the "Edit it as HTML" command works.
 
 ## Committing
-The only changes allowed to front_end/cm/ folder are CodeMirror rolls. There's a presubmit check that enforces this, so make sure you include the phrase "roll CodeMirror" into
+The only changes allowed to `front_end/cm/` folder are CodeMirror rolls. There's a presubmit check that enforces this, so make sure you include the phrase "roll CodeMirror" into
 your patch description.
 
 ## Example
diff --git a/third_party/blink/renderer/devtools/front_end/cm/activeline.js b/third_party/blink/renderer/devtools/front_end/cm/active-line.js
similarity index 100%
rename from third_party/blink/renderer/devtools/front_end/cm/activeline.js
rename to third_party/blink/renderer/devtools/front_end/cm/active-line.js
diff --git a/third_party/blink/renderer/devtools/front_end/cm/codemirror.css b/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
index 66567f0..6cdbd9b 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
+++ b/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
@@ -1,4 +1,3 @@
-/* STOP */
 
 /* The rest of this file contains styles related to the mechanics of
    the editor. You probably shouldn't touch them. */
diff --git a/third_party/blink/renderer/devtools/front_end/cm/codemirror.js b/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
index 9f9704e..c6beeda4 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
@@ -9689,7 +9689,7 @@
 
   addLegacyProps(CodeMirror);
 
-  CodeMirror.version = "5.48.1";
+  CodeMirror.version = "5.48.3";
 
   return CodeMirror;
 
diff --git a/third_party/blink/renderer/devtools/front_end/cm/markselection.js b/third_party/blink/renderer/devtools/front_end/cm/mark-selection.js
similarity index 100%
rename from third_party/blink/renderer/devtools/front_end/cm/markselection.js
rename to third_party/blink/renderer/devtools/front_end/cm/mark-selection.js
diff --git a/third_party/blink/renderer/devtools/front_end/cm/module.json b/third_party/blink/renderer/devtools/front_end/cm/module.json
index ecabb5f..efe284e 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/module.json
+++ b/third_party/blink/renderer/devtools/front_end/cm/module.json
@@ -4,10 +4,10 @@
         "multiplex.js",
         "matchbrackets.js",
         "closebrackets.js",
-        "markselection.js",
+        "mark-selection.js",
         "comment.js",
         "overlay.js",
-        "activeline.js",
+        "active-line.js",
         "foldcode.js",
         "foldgutter.js",
         "brace-fold.js"
@@ -17,10 +17,10 @@
         "multiplex.js",
         "matchbrackets.js",
         "closebrackets.js",
-        "markselection.js",
+        "mark-selection.js",
         "comment.js",
         "overlay.js",
-        "activeline.js",
+        "active-line.js",
         "foldcode.js",
         "foldgutter.js",
         "brace-fold.js"
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
index 85cb666..8055f1ba 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
@@ -67,7 +67,7 @@
     if (ch == '"' || ch == "'") {
       state.tokenize = tokenString(ch);
       return state.tokenize(stream, state);
-    } else if (ch == "." && stream.match(/^[\d_]+(?:[eE][+\-]?[\d_]+)?/)) {
+    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
       return ret("number", "number");
     } else if (ch == "." && stream.match("..")) {
       return ret("spread", "meta");
@@ -195,8 +195,12 @@
         ++depth;
       } else if (wordRE.test(ch)) {
         sawSomething = true;
-      } else if (/["'\/]/.test(ch)) {
-        return;
+      } else if (/["'\/`]/.test(ch)) {
+        for (;; --pos) {
+          if (pos == 0) return
+          var next = stream.string.charAt(pos - 1)
+          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
+        }
       } else if (sawSomething && !depth) {
         ++pos;
         break;
diff --git a/third_party/blink/renderer/devtools/scripts/roll_codemirror.py b/third_party/blink/renderer/devtools/scripts/roll_codemirror.py
new file mode 100644
index 0000000..1019259
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/roll_codemirror.py
@@ -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.
+
+import argparse
+import glob
+import os
+import shutil
+import subprocess
+import sys
+
+
+def parse_options(cli_args):
+    parser = argparse.ArgumentParser(description='Roll CodeMirror')
+    parser.add_argument('cm_dir', help='CodeMirror directory')
+    parser.add_argument('devtools_dir', help='DevTools directory')
+    return parser.parse_args(cli_args)
+
+
+def run_npm(options):
+    print 'Building CodeMirror in %s' % os.path.abspath(options.cm_dir)
+    subprocess.check_output(['npm', 'install'], cwd=options.cm_dir, stderr=subprocess.PIPE)
+    subprocess.check_output(['npm', 'run', 'build'], cwd=options.cm_dir, stderr=subprocess.PIPE)
+
+
+def copy_lib_files(options):
+    print 'Copying codemirror.js and codemirror.css'
+    result = ''
+    target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm')
+
+    with open(os.path.join(options.cm_dir, 'lib', 'codemirror.js'), 'r') as read:
+        lines = read.readlines()
+    with open(os.path.join(target_dir, 'codemirror.js'), 'w') as write:
+        for line in lines:
+            if 'CodeMirror.version =' in line:
+                result = line.strip()
+            write.write(line)
+
+    with open(os.path.join(options.cm_dir, 'lib', 'codemirror.css'), 'r') as read:
+        lines = read.readlines()
+    found_stop = False
+    with open(os.path.join(target_dir, 'codemirror.css'), 'w') as write:
+        for line in lines:
+            if found_stop:
+                write.write(line)
+            elif '/* STOP */' in line:
+                found_stop = True
+    assert found_stop
+    return result
+
+
+def copy_headless_file(options):
+    print 'Copying runmode-standalone.js into headlesscodemirror.js'
+    source_file = os.path.join(options.cm_dir, 'addon', 'runmode', 'runmode-standalone.js')
+    target_file = os.path.join(options.devtools_dir, 'front_end', 'cm_headless', 'headlesscodemirror.js')
+
+    with open(source_file, 'r') as read:
+        lines = read.readlines()
+    with open(target_file, 'w') as write:
+        write.write('// Content of the function is equal to runmode-standalone.js file\n')
+        write.write('// from CodeMirror distribution\n')
+        write.write('(function(window) {\n')
+        for line in lines:
+            write.write(line)
+        write.write('}(this))\n')
+
+
+def find_and_copy_js_files(source_dir, target_dir, filter_fn):
+    for f in os.listdir(target_dir):
+        if not filter_fn(f):
+            continue
+        target_file = os.path.join(target_dir, f)
+        if not os.path.isfile(os.path.join(target_dir, f)):
+            continue
+        source = glob.glob(os.path.join(source_dir, '*', f))
+        assert len(source) == 1
+        source_file = source[0]
+        print 'Copying %s from %s' % (target_file, source_file)
+        shutil.copyfile(source_file, target_file)
+
+
+def copy_cm_files(options):
+    source_dir = os.path.join(options.cm_dir, 'addon')
+    target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm')
+
+    def cm_filter(f):
+        return f.endswith('.js') and f != 'codemirror.js'
+
+    find_and_copy_js_files(source_dir, target_dir, cm_filter)
+
+
+def copy_cm_modes_files(options):
+    source_dir = os.path.join(options.cm_dir, 'mode')
+    target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm_modes')
+
+    def cm_modes_filter(f):
+        return f.endswith('.js') and f != 'DefaultCodeMirrorMimeMode.js'
+
+    find_and_copy_js_files(source_dir, target_dir, cm_modes_filter)
+
+
+def copy_cm_web_modes_files(options):
+    source_dir = os.path.join(options.cm_dir, 'mode')
+    target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm_web_modes')
+
+    def cm_web_modes_filter(f):
+        return f.endswith('.js')
+
+    find_and_copy_js_files(source_dir, target_dir, cm_web_modes_filter)
+
+
+if __name__ == '__main__':
+    OPTIONS = parse_options(sys.argv[1:])
+    run_npm(OPTIONS)
+    copy_cm_files(OPTIONS)
+    copy_cm_modes_files(OPTIONS)
+    copy_cm_web_modes_files(OPTIONS)
+    copy_headless_file(OPTIONS)
+    VERSION = copy_lib_files(OPTIONS)
+    print VERSION
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index ca52113..be6ef77 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -444,7 +444,13 @@
   // after the last character.
   const AXObject* child = ChildAfterTreePosition();
   if (!child) {
-    const AXObject* next_in_order = container_object_->NextInTreeObject();
+    // If this is a static text object, we should not descend into its inline
+    // text boxes when present, because we'll just be creating a text position
+    // in the same piece of text.
+    const AXObject* next_in_order =
+        container_object_->ChildCount()
+            ? container_object_->DeepestLastChild()->NextInTreeObject()
+            : container_object_->NextInTreeObject();
     if (!next_in_order || !next_in_order->ParentObjectIncludedInTree())
       return {};
 
@@ -474,7 +480,10 @@
   // Handles both an "after children" position, or a text position that is
   // before the first character.
   if (!child) {
-    if (container_object_->ChildCount()) {
+    // If this is a static text object, we should not descend into its inline
+    // text boxes when present, because we'll just be creating a text position
+    // in the same piece of text.
+    if (!container_object_->IsTextObject() && container_object_->ChildCount()) {
       const AXObject* last_child = container_object_->LastChild();
       // Dont skip over any intervening text.
       if (last_child->IsTextObject() || last_child->IsNativeTextControl()) {
@@ -531,7 +540,7 @@
   // 5. We arbitrarily decided to ignore positions that are anchored to before a
   // text object. We move such positions to before the first character of the
   // text object. This is in an effort to ensure that two positions, one a
-  // "before object" position anchored to before a text object, and one a "text
+  // "before object" position anchored to a text object, and one a "text
   // position" anchored to before the first character of the same text object,
   // compare as equivalent.
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
index 41c0d31..c28df08 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
@@ -1236,6 +1236,135 @@
   EXPECT_EQ(12, position_after.GetPosition().OffsetInContainerNode());
 }
 
+TEST_F(AccessibilityTest, PositionInTableWithCSSContent) {
+  SetBodyInnerHTML(kHTMLTable);
+
+  // Add some CSS content, i.e. a plus symbol before and a colon after each
+  // table header cell.
+  Element* const style_element =
+      GetDocument().CreateRawElement(html_names::kStyleTag);
+  ASSERT_NE(nullptr, style_element);
+  style_element->setTextContent(R"STYLE(
+      th::before {
+        content: "+";
+      }
+      th::after {
+        content: ":";
+      }
+      )STYLE");
+  GetDocument().body()->insertBefore(style_element,
+                                     GetDocument().body()->firstChild());
+  UpdateAllLifecyclePhasesForTest();
+
+  const Node* first_header_cell = GetElementById("firstHeaderCell");
+  ASSERT_NE(nullptr, first_header_cell);
+  const Node* last_header_cell = GetElementById("lastHeaderCell");
+  ASSERT_NE(nullptr, last_header_cell);
+
+  // CSS text nodes are not in the DOM tree.
+  const Node* first_header_cell_text = first_header_cell->firstChild();
+  ASSERT_NE(nullptr, first_header_cell_text);
+  ASSERT_FALSE(first_header_cell_text->IsPseudoElement());
+  ASSERT_TRUE(first_header_cell_text->IsTextNode());
+  const Node* last_header_cell_text = last_header_cell->firstChild();
+  ASSERT_NE(nullptr, last_header_cell_text);
+  ASSERT_FALSE(last_header_cell_text->IsPseudoElement());
+  ASSERT_TRUE(last_header_cell_text->IsTextNode());
+
+  const AXObject* ax_first_header_cell =
+      GetAXObjectByElementId("firstHeaderCell");
+  ASSERT_NE(nullptr, ax_first_header_cell);
+  ASSERT_EQ(ax::mojom::Role::kColumnHeader, ax_first_header_cell->RoleValue());
+  const AXObject* ax_last_header_cell =
+      GetAXObjectByElementId("lastHeaderCell");
+  ASSERT_NE(nullptr, ax_last_header_cell);
+  ASSERT_EQ(ax::mojom::Role::kColumnHeader, ax_last_header_cell->RoleValue());
+
+  ASSERT_EQ(3, ax_first_header_cell->ChildCount());
+  AXObject* const ax_first_cell_css_before = ax_first_header_cell->FirstChild();
+  ASSERT_NE(nullptr, ax_first_cell_css_before);
+  ASSERT_EQ(ax::mojom::Role::kStaticText,
+            ax_first_cell_css_before->RoleValue());
+
+  ASSERT_EQ(3, ax_last_header_cell->ChildCount());
+  AXObject* const ax_last_cell_css_after = ax_last_header_cell->LastChild();
+  ASSERT_NE(nullptr, ax_last_cell_css_after);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_last_cell_css_after->RoleValue());
+
+  // The first position inside the first header cell should be before the plus
+  // symbol inside the CSS content. It should be valid in the accessibility tree
+  // but not valid in the DOM tree.
+  auto ax_position_before =
+      AXPosition::CreateFirstPositionInObject(*ax_first_header_cell);
+  EXPECT_TRUE(ax_position_before.IsTextPosition());
+  EXPECT_EQ(0, ax_position_before.TextOffset());
+  auto position_before = ax_position_before.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveRight);
+  EXPECT_EQ(first_header_cell_text, position_before.AnchorNode());
+  EXPECT_EQ(0, position_before.GetPosition().OffsetInContainerNode());
+
+  // Same situation as above, but explicitly create a text position inside the
+  // CSS content, instead of having it implicitly created by
+  // CreateFirstPositionInObject.
+  ax_position_before =
+      AXPosition::CreateFirstPositionInObject(*ax_first_cell_css_before);
+  EXPECT_TRUE(ax_position_before.IsTextPosition());
+  EXPECT_EQ(0, ax_position_before.TextOffset());
+  position_before = ax_position_before.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveRight);
+  EXPECT_EQ(first_header_cell_text, position_before.AnchorNode());
+  EXPECT_EQ(0, position_before.GetPosition().OffsetInContainerNode());
+
+  // Same situation as above, but now create a text position inside the inline
+  // text box representing the CSS content after the last header cell.
+  ax_first_cell_css_before->LoadInlineTextBoxes();
+  ASSERT_NE(nullptr, ax_first_cell_css_before->FirstChild());
+  ax_position_before = AXPosition::CreateFirstPositionInObject(
+      *ax_first_cell_css_before->FirstChild());
+  EXPECT_TRUE(ax_position_before.IsTextPosition());
+  EXPECT_EQ(0, ax_position_before.TextOffset());
+  position_before = ax_position_before.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveRight);
+  EXPECT_EQ(first_header_cell_text, position_before.AnchorNode());
+  EXPECT_EQ(0, position_before.GetPosition().OffsetInContainerNode());
+
+  // An "after children" position inside the last header cell should be after
+  // the CSS content that displays a colon. It should be valid in the
+  // accessibility tree but not valid in the DOM tree.
+  auto ax_position_after =
+      AXPosition::CreateLastPositionInObject(*ax_last_header_cell);
+  EXPECT_FALSE(ax_position_after.IsTextPosition());
+  EXPECT_EQ(3, ax_position_after.ChildIndex());
+  auto position_after = ax_position_after.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveLeft);
+  EXPECT_EQ(last_header_cell_text, position_after.AnchorNode());
+  EXPECT_EQ(8, position_after.GetPosition().OffsetInContainerNode());
+
+  // Similar to the last case, but explicitly create a text position inside the
+  // CSS content after the last header cell.
+  ax_position_after =
+      AXPosition::CreateLastPositionInObject(*ax_last_cell_css_after);
+  EXPECT_TRUE(ax_position_after.IsTextPosition());
+  EXPECT_EQ(1, ax_position_after.TextOffset());
+  position_after = ax_position_after.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveLeft);
+  EXPECT_EQ(last_header_cell_text, position_after.AnchorNode());
+  EXPECT_EQ(8, position_after.GetPosition().OffsetInContainerNode());
+
+  // Same situation as above, but now create a text position inside the inline
+  // text box representing the CSS content after the last header cell.
+  ax_last_cell_css_after->LoadInlineTextBoxes();
+  ASSERT_NE(nullptr, ax_last_cell_css_after->FirstChild());
+  ax_position_after = AXPosition::CreateLastPositionInObject(
+      *ax_last_cell_css_after->FirstChild());
+  EXPECT_TRUE(ax_position_after.IsTextPosition());
+  EXPECT_EQ(1, ax_position_after.TextOffset());
+  position_after = ax_position_after.ToPositionWithAffinity(
+      AXPositionAdjustmentBehavior::kMoveLeft);
+  EXPECT_EQ(last_header_cell_text, position_after.AnchorNode());
+  EXPECT_EQ(8, position_after.GetPosition().OffsetInContainerNode());
+}
+
 //
 // Objects deriving from |AXMockObject|, e.g. table columns, are in the
 // accessibility tree but are neither in the DOM or layout trees.
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
index 7a7e7bb..a3a5963b 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
@@ -105,15 +105,28 @@
     domain = cookie_url_host;
   }
 
-  // The cookie store API is only exposed on secure origins. If this changes:
-  // 1) The secure option must default to false for insecure origins.
-  // 2) Only secure origins can set the "secure" option to true.
-  DCHECK(SecurityOrigin::IsSecure(cookie_url));
-
-  const bool secure = options->secure();
+  // Although the Cookie Store API spec always defaults the "secure" cookie
+  // attribute to true, we only default to true on cryptographically secure
+  // origins, where only secure cookies may be written, and to false otherwise,
+  // where only insecure cookies may be written. As a result,
+  // cookieStore.set("name", "value") sets a cookie and
+  // cookieStore.delete("name") deletes a cookie on both http://localhost and
+  // secure origins, without having to specify "secure: false" on
+  // http://localhost.
+  const bool secure = options->hasSecure()
+                          ? options->secure()
+                          : SecurityOrigin::IsSecure(cookie_url);
+  // If attempting to set/delete a secure cookie on an insecure origin, throw an
+  // exception, rather than failing silently as document.cookie does.
+  if (secure && !SecurityOrigin::IsSecure(cookie_url)) {
+    exception_state.ThrowTypeError(
+        "Cannot modify a secure cookie on insecure origin");
+    return base::nullopt;
+  }
   if (!secure && (name.StartsWith("__Secure-") || name.StartsWith("__Host-"))) {
     exception_state.ThrowTypeError(
         "__Secure- and __Host- cookies must be secure");
+    return base::nullopt;
   }
 
   network::mojom::CookieSameSite same_site;
@@ -268,7 +281,8 @@
     set_options->setExpires(options->expires());
   set_options->setDomain(options->domain());
   set_options->setPath(options->path());
-  set_options->setSecure(options->secure());
+  if (options->hasSecure())
+    set_options->setSecure(options->secure());
   set_options->setSameSite(options->sameSite());
   return set(script_state, set_options, exception_state);
 }
@@ -306,7 +320,6 @@
   set_options->setExpires(0);
   set_options->setDomain(options->domain());
   set_options->setPath(options->path());
-  set_options->setSecure(true);
   set_options->setSameSite("strict");
   return DoWrite(script_state, set_options, exception_state);
 }
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store_set_options.idl b/third_party/blink/renderer/modules/cookie_store/cookie_store_set_options.idl
index 4a01a9fa..c9d2cafc 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store_set_options.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store_set_options.idl
@@ -14,6 +14,6 @@
   DOMTimeStamp? expires = null;
   USVString? domain = null;
   USVString path = "/";
-  boolean secure = true;
+  boolean secure;
   CookieSameSite sameSite = "strict";
 };
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index 4b5b647..54e1827 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -405,14 +405,6 @@
     if (credential->echo_hmac_create_secret) {
       extension_outputs->setHmacCreateSecret(credential->hmac_create_secret);
     }
-#if defined(OS_ANDROID)
-    if (credential->echo_user_verification_methods) {
-      extension_outputs->setUvm(
-          UvmEntryToArray(std::move(*credential->user_verification_methods)));
-      UseCounter::Count(resolver->GetExecutionContext(),
-                        WebFeature::kCredentialManagerCreateSuccessWithUVM);
-    }
-#endif
     resolver->Resolve(MakeGarbageCollected<PublicKeyCredential>(
         credential->info->id, raw_id, authenticator_response,
         extension_outputs));
@@ -708,13 +700,6 @@
           resolver->GetExecutionContext(),
           WebFeature::kCredentialManagerCreatePublicKeyCredential);
     }
-#if defined(OS_ANDROID)
-    if (options->publicKey()->hasExtensions() &&
-        options->publicKey()->extensions()->hasUvm()) {
-      UseCounter::Count(resolver->GetExecutionContext(),
-                        WebFeature::kCredentialManagerCreateWithUVM);
-    }
-#endif
 
     const String& relying_party_id = options->publicKey()->rp()->id();
     if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id))
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 98362e5d..3ebfbd5 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -154,14 +154,7 @@
     return false;
   if (document->View()->NeedsLayout())
     return false;
-  DocumentLifecycle::LifecycleState state = document->Lifecycle().GetState();
-  if (state >= DocumentLifecycle::kLayoutClean ||
-      state == DocumentLifecycle::kStyleClean ||
-      state == DocumentLifecycle::kLayoutSubtreeChangeClean) {
-    return true;
-  }
-
-  return false;
+  return document->Lifecycle().GetState() >= DocumentLifecycle::kLayoutClean;
 }
 
 void WebAXObject::Reset() {
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 3fb54af..ff11b93f 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -297,7 +297,7 @@
   if (!options->maximumAge())
     return false;
   DOMTimeStamp current_time_millis =
-      ConvertSecondsToDOMTimeStamp(CurrentTime());
+      ConvertSecondsToDOMTimeStamp(base::Time::Now().ToDoubleT());
   return last_position_->timestamp() >
          current_time_millis - options->maximumAge();
 }
diff --git a/third_party/blink/renderer/modules/image_downloader/BUILD.gn b/third_party/blink/renderer/modules/image_downloader/BUILD.gn
index 88388e7..125f62e 100644
--- a/third_party/blink/renderer/modules/image_downloader/BUILD.gn
+++ b/third_party/blink/renderer/modules/image_downloader/BUILD.gn
@@ -10,8 +10,6 @@
     "fetcher/associated_resource_fetcher.h",
     "fetcher/multi_resolution_image_resource_fetcher.cc",
     "fetcher/multi_resolution_image_resource_fetcher.h",
-    "image_downloader_base.cc",
-    "image_downloader_base.h",
     "image_downloader_impl.cc",
     "image_downloader_impl.h",
   ]
diff --git a/third_party/blink/renderer/modules/image_downloader/image_downloader_base.cc b/third_party/blink/renderer/modules/image_downloader/image_downloader_base.cc
deleted file mode 100644
index 8958861..0000000
--- a/third_party/blink/renderer/modules/image_downloader/image_downloader_base.cc
+++ /dev/null
@@ -1,117 +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/image_downloader/image_downloader_base.h"
-
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/public/platform/web_data.h"
-#include "third_party/blink/public/platform/web_image.h"
-#include "third_party/blink/public/platform/web_size.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/modules/image_downloader/fetcher/multi_resolution_image_resource_fetcher.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/network/network_utils.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "ui/gfx/favicon_size.h"
-
-namespace {
-
-// Decodes a data: URL image or returns an empty image in case of failure.
-SkBitmap ImageFromDataUrl(const blink::KURL& url) {
-  std::string data;
-  if (blink::network_utils::IsDataURLMimeTypeSupported(url, &data) &&
-      !data.empty()) {
-    gfx::Size desired_icon_size =
-        gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize);
-    const unsigned char* src_data =
-        reinterpret_cast<const unsigned char*>(data.data());
-    return blink::WebImage::FromData(
-        blink::WebData(reinterpret_cast<const char*>(src_data), data.size()),
-        blink::WebSize(desired_icon_size));
-  }
-  return SkBitmap();
-}
-
-}  // namespace
-
-namespace blink {
-
-ImageDownloaderBase::ImageDownloaderBase(ExecutionContext* context,
-                                         LocalFrame& frame)
-    : ContextLifecycleObserver(context), frame_(frame) {}
-
-ImageDownloaderBase::~ImageDownloaderBase() {}
-
-void ImageDownloaderBase::DownloadImage(const KURL& image_url,
-                                        bool is_favicon,
-                                        bool bypass_cache,
-                                        DownloadCallback callback) {
-  if (!image_url.ProtocolIsData()) {
-    FetchImage(image_url, is_favicon, bypass_cache, std::move(callback));
-    // Will complete asynchronously via ImageDownloaderBase::DidFetchImage.
-    return;
-  }
-
-  WTF::Vector<SkBitmap> result_images;
-  SkBitmap data_image = ImageFromDataUrl(image_url);
-
-  // Drop null or empty SkBitmap.
-  if (!data_image.drawsNothing())
-    result_images.push_back(data_image);
-
-  std::move(callback).Run(0, result_images);
-}
-
-void ImageDownloaderBase::FetchImage(const KURL& image_url,
-                                     bool is_favicon,
-                                     bool bypass_cache,
-                                     DownloadCallback callback) {
-  // Create an image resource fetcher and assign it with a call back object.
-  image_fetchers_.push_back(
-      std::make_unique<MultiResolutionImageResourceFetcher>(
-          image_url, frame_, 0,
-          is_favicon ? blink::mojom::RequestContextType::FAVICON
-                     : blink::mojom::RequestContextType::IMAGE,
-          bypass_cache ? blink::mojom::FetchCacheMode::kBypassCache
-                       : blink::mojom::FetchCacheMode::kDefault,
-          WTF::Bind(&ImageDownloaderBase::DidFetchImage, WrapPersistent(this),
-                    std::move(callback))));
-}
-
-void ImageDownloaderBase::DidFetchImage(
-    DownloadCallback callback,
-    MultiResolutionImageResourceFetcher* fetcher,
-    const WTF::Vector<SkBitmap>& images) {
-  int32_t http_status_code = fetcher->http_status_code();
-
-  // Remove the image fetcher from our pending list. We're in the callback from
-  // MultiResolutionImageResourceFetcher, best to delay deletion.
-  for (auto* it = image_fetchers_.begin(); it != image_fetchers_.end(); ++it) {
-    MultiResolutionImageResourceFetcher* image_fetcher = it->get();
-    DCHECK(image_fetcher);
-    if (image_fetcher == fetcher) {
-      it = image_fetchers_.erase(it);
-      break;
-    }
-  }
-
-  // |this| may be destructed after callback is run.
-  std::move(callback).Run(http_status_code, images);
-}
-
-void ImageDownloaderBase::Trace(blink::Visitor* visitor) {
-  visitor->Trace(frame_);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-void ImageDownloaderBase::ContextDestroyed(ExecutionContext*) {
-  for (const auto& fetchers : image_fetchers_) {
-    // Will run callbacks with an empty image vector.
-    fetchers->OnRenderFrameDestruct();
-  }
-  image_fetchers_.clear();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/image_downloader/image_downloader_base.h b/third_party/blink/renderer/modules/image_downloader/image_downloader_base.h
deleted file mode 100644
index 755ff801..0000000
--- a/third_party/blink/renderer/modules/image_downloader/image_downloader_base.h
+++ /dev/null
@@ -1,72 +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_IMAGE_DOWNLOADER_IMAGE_DOWNLOADER_BASE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_IMAGE_DOWNLOADER_IMAGE_DOWNLOADER_BASE_H_
-
-#include <stdint.h>
-
-#include "base/callback.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/platform/heap/visitor.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace blink {
-
-class KURL;
-class MultiResolutionImageResourceFetcher;
-class LocalFrame;
-
-class ImageDownloaderBase : public ContextLifecycleObserver {
- public:
-  ImageDownloaderBase(ExecutionContext*, LocalFrame&);
-  ~ImageDownloaderBase();
-
-  using DownloadCallback =
-      base::OnceCallback<void(int32_t, const WTF::Vector<SkBitmap>&)>;
-
-  // Request to asynchronously download an image. When done, |callback| will be
-  // called.
-  void DownloadImage(const KURL& url,
-                     bool is_favicon,
-                     bool bypass_cache,
-                     DownloadCallback callback);
-
-  void Trace(blink::Visitor*) override;
-
- protected:
-  // ContextLifecycleObserver:
-  void ContextDestroyed(ExecutionContext*) override;
-
- private:
-  // Requests to fetch an image. When done, the image downloader is notified by
-  // way of DidFetchImage. If the image is a favicon, cookies will not be sent
-  // nor accepted during download. If the image has multiple frames, all frames
-  // are returned.
-  void FetchImage(const KURL& image_url,
-                  bool is_favicon,
-                  bool bypass_cache,
-                  DownloadCallback callback);
-
-  // This callback is triggered when FetchImage completes, either
-  // successfully or with a failure. See FetchImage for more
-  // details.
-  void DidFetchImage(DownloadCallback callback,
-                     MultiResolutionImageResourceFetcher* fetcher,
-                     const WTF::Vector<SkBitmap>& images);
-
-  typedef WTF::Vector<std::unique_ptr<MultiResolutionImageResourceFetcher>>
-      ImageResourceFetcherList;
-
-  // ImageResourceFetchers schedule via FetchImage.
-  ImageResourceFetcherList image_fetchers_;
-
-  Member<LocalFrame> frame_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImageDownloaderBase);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_IMAGE_DOWNLOADER_IMAGE_DOWNLOADER_BASE_H_
diff --git a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
index fa940a7e..ce3e32ab 100644
--- a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
+++ b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
@@ -9,18 +9,40 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "skia/ext/image_operations.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_registry.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_image.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/image_downloader/fetcher/multi_resolution_image_resource_fetcher.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/network/network_utils.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "ui/gfx/favicon_size.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace {
 
+// Decodes a data: URL image or returns an empty image in case of failure.
+SkBitmap ImageFromDataUrl(const blink::KURL& url) {
+  std::string data;
+  if (blink::network_utils::IsDataURLMimeTypeSupported(url, &data) &&
+      !data.empty()) {
+    gfx::Size desired_icon_size =
+        gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize);
+    const unsigned char* src_data =
+        reinterpret_cast<const unsigned char*>(data.data());
+    return blink::WebImage::FromData(
+        blink::WebData(reinterpret_cast<const char*>(src_data), data.size()),
+        blink::WebSize(desired_icon_size));
+  }
+  return SkBitmap();
+}
+
 //  Proportionally resizes the |image| to fit in a box of size
 // |max_image_size|.
 SkBitmap ResizeImage(const SkBitmap& image, uint32_t max_image_size) {
@@ -109,7 +131,7 @@
 
 ImageDownloaderImpl::ImageDownloaderImpl(LocalFrame& frame)
     : Supplement<LocalFrame>(frame),
-      ImageDownloaderBase(frame.GetDocument()->GetExecutionContext(), frame) {
+      ContextLifecycleObserver(frame.GetDocument()->GetExecutionContext()) {
   frame.GetInterfaceRegistry()->AddInterface(WTF::BindRepeating(
       &ImageDownloaderImpl::CreateMojoService, WrapWeakPersistent(this)));
 }
@@ -129,13 +151,25 @@
                                         uint32_t max_bitmap_size,
                                         bool bypass_cache,
                                         DownloadImageCallback callback) {
-  WTF::Vector<SkBitmap> result_images;
-  WTF::Vector<gfx::Size> result_original_image_sizes;
-
-  ImageDownloaderBase::DownloadImage(
-      image_url, is_favicon, bypass_cache,
+  auto download_callback =
       WTF::Bind(&ImageDownloaderImpl::DidDownloadImage, WrapPersistent(this),
-                max_bitmap_size, std::move(callback)));
+                max_bitmap_size, std::move(callback));
+
+  if (!image_url.ProtocolIsData()) {
+    FetchImage(image_url, is_favicon, bypass_cache,
+               std::move(download_callback));
+    // Will complete asynchronously via ImageDownloaderImpl::DidFetchImage.
+    return;
+  }
+
+  WTF::Vector<SkBitmap> result_images;
+  SkBitmap data_image = ImageFromDataUrl(image_url);
+
+  // Drop null or empty SkBitmap.
+  if (!data_image.drawsNothing())
+    result_images.push_back(data_image);
+
+  std::move(download_callback).Run(0, result_images);
 }
 
 void ImageDownloaderImpl::DidDownloadImage(
@@ -152,18 +186,58 @@
                           result_original_image_sizes);
 }
 
-void ImageDownloaderImpl::ContextDestroyed(ExecutionContext* context) {
-  ImageDownloaderBase::ContextDestroyed(context);
-  Dispose();
-}
-
 void ImageDownloaderImpl::Dispose() {
   binding_.Close();
 }
 
+void ImageDownloaderImpl::FetchImage(const KURL& image_url,
+                                     bool is_favicon,
+                                     bool bypass_cache,
+                                     DownloadCallback callback) {
+  // Create an image resource fetcher and assign it with a call back object.
+  image_fetchers_.push_back(
+      std::make_unique<MultiResolutionImageResourceFetcher>(
+          image_url, GetSupplementable(), 0,
+          is_favicon ? blink::mojom::RequestContextType::FAVICON
+                     : blink::mojom::RequestContextType::IMAGE,
+          bypass_cache ? blink::mojom::FetchCacheMode::kBypassCache
+                       : blink::mojom::FetchCacheMode::kDefault,
+          WTF::Bind(&ImageDownloaderImpl::DidFetchImage, WrapPersistent(this),
+                    std::move(callback))));
+}
+
+void ImageDownloaderImpl::DidFetchImage(
+    DownloadCallback callback,
+    MultiResolutionImageResourceFetcher* fetcher,
+    const WTF::Vector<SkBitmap>& images) {
+  int32_t http_status_code = fetcher->http_status_code();
+
+  // Remove the image fetcher from our pending list. We're in the callback from
+  // MultiResolutionImageResourceFetcher, best to delay deletion.
+  for (auto* it = image_fetchers_.begin(); it != image_fetchers_.end(); ++it) {
+    MultiResolutionImageResourceFetcher* image_fetcher = it->get();
+    DCHECK(image_fetcher);
+    if (image_fetcher == fetcher) {
+      it = image_fetchers_.erase(it);
+      break;
+    }
+  }
+
+  // |this| may be destructed after callback is run.
+  std::move(callback).Run(http_status_code, images);
+}
+
 void ImageDownloaderImpl::Trace(Visitor* visitor) {
   Supplement<LocalFrame>::Trace(visitor);
-  ImageDownloaderBase::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+}
+
+void ImageDownloaderImpl::ContextDestroyed(ExecutionContext*) {
+  for (const auto& fetchers : image_fetchers_) {
+    // Will run callbacks with an empty image vector.
+    fetchers->OnRenderFrameDestruct();
+  }
+  image_fetchers_.clear();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.h b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.h
index a51c6f38f..9d768d57 100644
--- a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.h
+++ b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.h
@@ -7,18 +7,19 @@
 
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom-blink.h"
-#include "third_party/blink/renderer/modules/image_downloader/image_downloader_base.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 
 namespace blink {
 
 class KURL;
 class LocalFrame;
+class MultiResolutionImageResourceFetcher;
 
 class ImageDownloaderImpl final
     : public GarbageCollectedFinalized<ImageDownloaderImpl>,
       public Supplement<LocalFrame>,
-      public ImageDownloaderBase,
+      public ContextLifecycleObserver,
       public mojom::blink::ImageDownloader {
   USING_PRE_FINALIZER(ImageDownloaderImpl, Dispose);
   USING_GARBAGE_COLLECTED_MIXIN(ImageDownloaderImpl);
@@ -29,17 +30,21 @@
   explicit ImageDownloaderImpl(LocalFrame&);
   ~ImageDownloaderImpl() override;
 
+  using DownloadCallback =
+      base::OnceCallback<void(int32_t, const WTF::Vector<SkBitmap>&)>;
+
   static ImageDownloaderImpl* From(LocalFrame&);
 
   static void ProvideTo(LocalFrame&);
 
   void Trace(Visitor*) override;
 
- private:
-  // Override ImageDownloaderBase::ContextDestroyed().
+  // OverContextLifecycleObserver overrides.
   void ContextDestroyed(ExecutionContext*) override;
 
-  // ImageDownloader implementation.
+ private:
+  // ImageDownloader implementation. Request to asynchronously download an
+  // image. When done, |callback| will be called.
   void DownloadImage(const KURL& url,
                      bool is_favicon,
                      uint32_t max_bitmap_size,
@@ -62,6 +67,28 @@
   // Called before the object gets garbage collected.
   void Dispose();
 
+  // Requests to fetch an image. When done, the image downloader is notified by
+  // way of DidFetchImage. If the image is a favicon, cookies will not be sent
+  // nor accepted during download. If the image has multiple frames, all frames
+  // are returned.
+  void FetchImage(const KURL& image_url,
+                  bool is_favicon,
+                  bool bypass_cache,
+                  DownloadCallback callback);
+
+  // This callback is triggered when FetchImage completes, either
+  // successfully or with a failure. See FetchImage for more
+  // details.
+  void DidFetchImage(DownloadCallback callback,
+                     MultiResolutionImageResourceFetcher* fetcher,
+                     const WTF::Vector<SkBitmap>& images);
+
+  typedef WTF::Vector<std::unique_ptr<MultiResolutionImageResourceFetcher>>
+      ImageResourceFetcherList;
+
+  // ImageResourceFetchers schedule via FetchImage.
+  ImageResourceFetcherList image_fetchers_;
+
   mojo::Binding<mojom::blink::ImageDownloader> binding_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ImageDownloaderImpl);
diff --git a/third_party/blink/renderer/modules/manifest/manifest_change_notifier.h b/third_party/blink/renderer/modules/manifest/manifest_change_notifier.h
index 759b4f0d..b49e7f8b 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_change_notifier.h
+++ b/third_party/blink/renderer/modules/manifest/manifest_change_notifier.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MANIFEST_MANIFEST_CHANGE_NOTIFIER_H_
 
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/blink/public/mojom/manifest/manifest_observer.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -30,7 +31,7 @@
   void EnsureManifestChangeObserver();
 
   Member<LocalFrame> frame_;
-  mojom::blink::ManifestUrlChangeObserverAssociatedPtr
+  mojo::AssociatedRemote<mojom::blink::ManifestUrlChangeObserver>
       manifest_change_observer_;
   bool report_task_scheduled_ = false;
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
index 7903f2e..8040fed 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
@@ -6,8 +6,9 @@
 
 #include <memory>
 
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "services/device/public/mojom/screen_orientation.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -63,7 +64,7 @@
 class MockScreenOrientation final
     : public device::mojom::blink::ScreenOrientation {
  public:
-  MockScreenOrientation() : binding_(this) {}
+  MockScreenOrientation() = default;
 
   // device::mojom::blink::ScreenOrientation overrides:
   void LockOrientation(WebScreenOrientationLockType type,
@@ -73,19 +74,21 @@
     LockOrientation(type);
   }
 
-  void BindRequest(
-      device::mojom::blink::ScreenOrientationAssociatedRequest request) {
-    binding_.Bind(std::move(request));
+  void BindPendingReceiver(
+      mojo::PendingAssociatedReceiver<device::mojom::blink::ScreenOrientation>
+          pending_receiver) {
+    receiver_.Bind(std::move(pending_receiver));
   }
 
-  void Close() { binding_.Close(); }
+  void Close() { receiver_.reset(); }
 
   MOCK_METHOD0(UnlockOrientation, void());
 
   MOCK_METHOD1(LockOrientation, void(WebScreenOrientationLockType));
 
  private:
-  mojo::AssociatedBinding<device::mojom::blink::ScreenOrientation> binding_;
+  mojo::AssociatedReceiver<device::mojom::blink::ScreenOrientation> receiver_{
+      this};
 };
 
 void DidEnterFullscreen(Document* document) {
@@ -107,11 +110,12 @@
   void InstallSupplements(LocalFrame& frame) override {
     EmptyChromeClient::InstallSupplements(frame);
     ScreenOrientationControllerImpl::ProvideTo(frame);
-    device::mojom::blink::ScreenOrientationAssociatedPtr screen_orientation;
-    ScreenOrientationClient().BindRequest(
-        mojo::MakeRequestAssociatedWithDedicatedPipe(&screen_orientation));
+    mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>
+        screen_orientation;
+    ScreenOrientationClient().BindPendingReceiver(
+        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
     ScreenOrientationControllerImpl::From(frame)
-        ->SetScreenOrientationAssociatedPtrForTests(
+        ->SetScreenOrientationAssociatedRemoteForTests(
             std::move(screen_orientation));
   }
   // The real ChromeClient::EnterFullscreen/ExitFullscreen implementation is
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
index 0b438f9..af6efa41 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.h"
 
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "services/device/public/mojom/screen_orientation.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -57,10 +57,12 @@
   void InstallSupplements(LocalFrame& frame) override {
     EmptyChromeClient::InstallSupplements(frame);
     ScreenOrientationControllerImpl::ProvideTo(frame);
-    device::mojom::blink::ScreenOrientationAssociatedPtr screen_orientation;
-    mojo::MakeRequestAssociatedWithDedicatedPipe(&screen_orientation);
+    mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>
+        screen_orientation;
+    ignore_result(
+        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
     ScreenOrientationControllerImpl::From(frame)
-        ->SetScreenOrientationAssociatedPtrForTests(
+        ->SetScreenOrientationAssociatedRemoteForTests(
             std::move(screen_orientation));
   }
   void EnterFullscreen(LocalFrame& frame, const FullscreenOptions*) override {
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
index 2f6e044..c8fb6674 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -319,7 +319,7 @@
     return;
   }
   WriteData(nullptr /* data */, 0 /* length */, true /* lastInSlice */,
-            WTF::CurrentTimeMS());
+            base::Time::Now().ToDoubleT() * 1000.0);
 }
 
 bool MediaRecorder::isTypeSupported(ExecutionContext* context,
@@ -352,7 +352,7 @@
     return;
 
   WriteData(nullptr /* data */, 0 /* length */, true /* lastInSlice */,
-            WTF::CurrentTimeMS());
+            base::Time::Now().ToDoubleT() * 1000.0);
 
   stopped_ = true;
   stream_.Clear();
@@ -404,7 +404,7 @@
   recorder_handler_->Stop();
 
   WriteData(nullptr /* data */, 0 /* length */, true /* lastInSlice */,
-            WTF::CurrentTimeMS());
+            base::Time::Now().ToDoubleT() * 1000.0);
   ScheduleDispatchEvent(Event::Create(event_type_names::kStop));
 }
 
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 10fd72f..c6bea870 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -119,7 +119,8 @@
       audio_codec_id_(AudioTrackRecorder::CodecId::LAST),
       recording_(false),
       recorder_(nullptr),
-      task_runner_(std::move(task_runner)) {}
+      task_runner_(std::move(task_runner)),
+      weak_factory_(this) {}
 
 MediaRecorderHandler::~MediaRecorderHandler() = default;
 
@@ -253,7 +254,7 @@
                            CodecIdToMediaAudioCodec(audio_codec_id_),
                            use_video_tracks, use_audio_tracks,
                            WTF::BindRepeating(&MediaRecorderHandler::WriteData,
-                                              WrapWeakPersistent(this))));
+                                              weak_factory_.GetWeakPtr())));
 
   if (use_video_tracks) {
     // TODO(mcasas): The muxer API supports only one video track. Extend it to
@@ -266,7 +267,7 @@
 
     const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb =
         media::BindToCurrentLoop(WTF::BindRepeating(
-            &MediaRecorderHandler::OnEncodedVideo, WrapWeakPersistent(this)));
+            &MediaRecorderHandler::OnEncodedVideo, weak_factory_.GetWeakPtr()));
 
     video_recorders_.emplace_back(MakeGarbageCollected<VideoTrackRecorder>(
         video_codec_id_, video_tracks_[0], on_encoded_video_cb,
@@ -284,7 +285,7 @@
 
     const AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb =
         media::BindToCurrentLoop(base::Bind(
-            &MediaRecorderHandler::OnEncodedAudio, WrapWeakPersistent(this)));
+            &MediaRecorderHandler::OnEncodedAudio, weak_factory_.GetWeakPtr()));
 
     audio_recorders_.emplace_back(MakeGarbageCollected<AudioTrackRecorder>(
         audio_codec_id_, audio_tracks_[0], std::move(on_encoded_audio_cb),
@@ -299,6 +300,7 @@
   DCHECK(IsMainThread());
   // Don't check |recording_| since we can go directly from pause() to stop().
 
+  weak_factory_.InvalidateWeakPtrs();
   recording_ = false;
   timeslice_ = base::TimeDelta::FromMilliseconds(0);
   video_recorders_.clear();
@@ -458,9 +460,6 @@
     bool is_key_frame) {
   DCHECK(IsMainThread());
 
-  if (video_recorders_.IsEmpty())
-    return;
-
   if (UpdateTracksAndCheckIfChanged()) {
     recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
@@ -480,9 +479,6 @@
                                           base::TimeTicks timestamp) {
   DCHECK(IsMainThread());
 
-  if (audio_recorders_.IsEmpty())
-    return;
-
   if (UpdateTracksAndCheckIfChanged()) {
     recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
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 7c3da873..f979b4ba 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_checker.h"
@@ -139,6 +140,8 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
+  base::WeakPtrFactory<MediaRecorderHandler> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandler);
 };
 
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 0298705..cf616ab 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -219,7 +219,6 @@
           "mediastream/media_stream_track_event.idl",
           "mediastream/overconstrained_error.idl",
           "native_file_system/native_file_system_directory_iterator.idl",
-          "native_file_system/native_file_system_writable_file_stream.idl",
           "native_file_system/file_system_directory_handle.idl",
           "native_file_system/file_system_file_handle.idl",
           "native_file_system/file_system_handle.idl",
diff --git a/third_party/blink/renderer/modules/native_file_system/BUILD.gn b/third_party/blink/renderer/modules/native_file_system/BUILD.gn
index 3470b58e..3fa676a9 100644
--- a/third_party/blink/renderer/modules/native_file_system/BUILD.gn
+++ b/third_party/blink/renderer/modules/native_file_system/BUILD.gn
@@ -14,8 +14,6 @@
     "native_file_system_file_handle.h",
     "native_file_system_handle.cc",
     "native_file_system_handle.h",
-    "native_file_system_writable_file_stream.cc",
-    "native_file_system_writable_file_stream.h",
     "native_file_system_writer.cc",
     "native_file_system_writer.h",
     "window_native_file_system.cc",
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
index 2dc91c5c..1b9f6375 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
@@ -10,6 +10,5 @@
     ImplementedAs=NativeFileSystemFileHandle
 ] interface FileSystemFileHandle : FileSystemHandle {
     [CallWith=ScriptState] Promise<FileSystemWriter> createWriter();
-    [CallWith=ScriptState] Promise<FileSystemWritableFileStream> createWritable();
     [CallWith=ScriptState] Promise<File> getFile();
 };
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
index aadd59b..b385ee5 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/file.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.h"
 #include "third_party/blink/renderer/modules/native_file_system/native_file_system_writer.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -52,32 +51,6 @@
   return result;
 }
 
-ScriptPromise NativeFileSystemFileHandle::createWritable(
-    ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  mojo_ptr_->CreateFileWriter(WTF::Bind(
-      [](ScriptPromiseResolver* resolver,
-         mojom::blink::NativeFileSystemErrorPtr result,
-         mojom::blink::NativeFileSystemFileWriterPtr writer) {
-        ExecutionContext* context = resolver->GetExecutionContext();
-        if (!context)
-          return;
-        if (result->error_code == base::File::FILE_OK) {
-          resolver->Resolve(MakeGarbageCollected<
-                            NativeFileSystemWritableFileStream>(
-              RevocableInterfacePtr<mojom::blink::NativeFileSystemFileWriter>(
-                  writer.PassInterface(), context->GetInterfaceInvalidator(),
-                  context->GetTaskRunner(TaskType::kMiscPlatformAPI))));
-        } else {
-          resolver->Reject(file_error::CreateDOMException(result->error_code));
-        }
-      },
-      WrapPersistent(resolver)));
-  return result;
-}
-
 ScriptPromise NativeFileSystemFileHandle::getFile(ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
index 88586f5f..fe24da2 100644
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
+++ b/third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h
@@ -22,7 +22,6 @@
   bool isFile() const override { return true; }
 
   ScriptPromise createWriter(ScriptState*);
-  ScriptPromise createWritable(ScriptState*);
   ScriptPromise getFile(ScriptState*);
 
   mojom::blink::NativeFileSystemTransferTokenPtr Transfer() override;
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.cc b/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.cc
deleted file mode 100644
index 395abea1..0000000
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.cc
+++ /dev/null
@@ -1,175 +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 "third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.h"
-
-#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
-#include "third_party/blink/renderer/core/dom/dom_exception.h"
-#include "third_party/blink/renderer/core/fileapi/blob.h"
-#include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/native_file_system/native_file_system_file_handle.h"
-#include "third_party/blink/renderer/platform/blob/blob_data.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-NativeFileSystemWritableFileStream::NativeFileSystemWritableFileStream(
-    RevocableInterfacePtr<mojom::blink::NativeFileSystemFileWriter> mojo_ptr)
-    : mojo_ptr_(std::move(mojo_ptr)) {
-  DCHECK(mojo_ptr_);
-}
-
-ScriptPromise NativeFileSystemWritableFileStream::write(
-    ScriptState* script_state,
-    uint64_t position,
-    const ArrayBufferOrArrayBufferViewOrBlobOrUSVString& data,
-    ExceptionState& exception_state) {
-  DCHECK(!data.IsNull());
-
-  auto blob_data = std::make_unique<BlobData>();
-  Blob* blob = nullptr;
-  if (data.IsArrayBuffer()) {
-    DOMArrayBuffer* array_buffer = data.GetAsArrayBuffer();
-    blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength());
-  } else if (data.IsArrayBufferView()) {
-    DOMArrayBufferView* array_buffer_view = data.GetAsArrayBufferView().View();
-    blob_data->AppendBytes(array_buffer_view->BaseAddress(),
-                           array_buffer_view->byteLength());
-  } else if (data.IsBlob()) {
-    blob = data.GetAsBlob();
-  } else if (data.IsUSVString()) {
-    // Let the developer be explicit about line endings.
-    blob_data->AppendText(data.GetAsUSVString(),
-                          /*normalize_line_endings_to_native=*/false);
-  }
-
-  if (!blob) {
-    uint64_t size = blob_data->length();
-    blob = Blob::Create(BlobDataHandle::Create(std::move(blob_data), size));
-  }
-
-  return WriteBlob(script_state, position, blob);
-}
-
-ScriptPromise NativeFileSystemWritableFileStream::truncate(
-    ScriptState* script_state,
-    uint64_t size) {
-  if (!mojo_ptr_ || pending_operation_) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state, MakeGarbageCollected<DOMException>(
-                          DOMExceptionCode::kInvalidStateError));
-  }
-  pending_operation_ =
-      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = pending_operation_->Promise();
-  mojo_ptr_->Truncate(
-      size, WTF::Bind(&NativeFileSystemWritableFileStream::TruncateComplete,
-                      WrapPersistent(this)));
-  return result;
-}
-
-void NativeFileSystemWritableFileStream::WriteComplete(
-    mojom::blink::NativeFileSystemErrorPtr result,
-    uint64_t bytes_written) {
-  DCHECK(pending_operation_);
-  if (result->error_code == base::File::FILE_OK) {
-    pending_operation_->Resolve();
-  } else {
-    pending_operation_->Reject(
-        file_error::CreateDOMException(result->error_code));
-  }
-  pending_operation_ = nullptr;
-}
-
-void NativeFileSystemWritableFileStream::TruncateComplete(
-    mojom::blink::NativeFileSystemErrorPtr result) {
-  DCHECK(pending_operation_);
-  if (result->error_code == base::File::FILE_OK) {
-    pending_operation_->Resolve();
-  } else {
-    pending_operation_->Reject(
-        file_error::CreateDOMException(result->error_code));
-  }
-  pending_operation_ = nullptr;
-}
-
-ScriptPromise NativeFileSystemWritableFileStream::WriteBlob(
-    ScriptState* script_state,
-    uint64_t position,
-    Blob* blob) {
-  if (!mojo_ptr_ || pending_operation_) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state, MakeGarbageCollected<DOMException>(
-                          DOMExceptionCode::kInvalidStateError));
-  }
-  pending_operation_ =
-      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = pending_operation_->Promise();
-  mojo_ptr_->Write(position, blob->AsMojoBlob(),
-                   WTF::Bind(&NativeFileSystemWritableFileStream::WriteComplete,
-                             WrapPersistent(this)));
-  return result;
-}
-
-ScriptPromise NativeFileSystemWritableFileStream::abort(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state,
-      MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotSupportedError));
-}
-
-ScriptPromise NativeFileSystemWritableFileStream::abort(
-    ScriptState* script_state,
-    ScriptValue reason,
-    ExceptionState& exception_state) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state,
-      MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotSupportedError));
-}
-
-ScriptValue NativeFileSystemWritableFileStream::getWriter(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not Implemented");
-  return ScriptValue();
-}
-
-bool NativeFileSystemWritableFileStream::locked(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  auto result = IsLocked(script_state, exception_state);
-
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not Implemented");
-  return !result || *result;
-}
-
-base::Optional<bool> NativeFileSystemWritableFileStream::IsLocked(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not Implemented");
-  return base::nullopt;
-}
-
-void NativeFileSystemWritableFileStream::Serialize(
-    ScriptState* script_state,
-    MessagePort* port,
-    ExceptionState& exception_state) {
-  DCHECK(port);
-  DCHECK(RuntimeEnabledFeatures::TransferableStreamsEnabled());
-  exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                    "Not Implemented");
-}
-
-void NativeFileSystemWritableFileStream::Trace(Visitor* visitor) {
-  ScriptWrappable::Trace(visitor);
-  visitor->Trace(pending_operation_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.h b/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.h
deleted file mode 100644
index 2f17f7d..0000000
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.h
+++ /dev/null
@@ -1,62 +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 THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
-
-#include "third_party/blink/public/mojom/native_file_system/native_file_system_error.mojom-blink.h"
-#include "third_party/blink/public/mojom/native_file_system/native_file_system_file_writer.mojom-blink.h"
-#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
-#include "third_party/blink/renderer/core/streams/writable_stream.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h"
-
-namespace blink {
-
-class Blob;
-class ExceptionState;
-class ScriptPromise;
-class ScriptPromiseResolver;
-class ScriptState;
-
-class NativeFileSystemWritableFileStream final : public WritableStream {
-  DEFINE_WRAPPERTYPEINFO();
-
- public:
-  explicit NativeFileSystemWritableFileStream(
-      RevocableInterfacePtr<mojom::blink::NativeFileSystemFileWriter>);
-
-  void Trace(Visitor* visitor) override;
-
-  // IDL defined functions for WritableStream.
-  bool locked(ScriptState*, ExceptionState&) const override;
-  ScriptPromise abort(ScriptState*, ExceptionState&) override;
-  ScriptPromise abort(ScriptState*,
-                      ScriptValue reason,
-                      ExceptionState&) override;
-  ScriptValue getWriter(ScriptState*, ExceptionState&) override;
-  void Serialize(ScriptState*, MessagePort* port, ExceptionState&) override;
-  base::Optional<bool> IsLocked(ScriptState*, ExceptionState&) const override;
-
-  // IDL defined functions specific to NativeFileSystemWritableFileStream.
-  ScriptPromise write(ScriptState*,
-                      uint64_t position,
-                      const ArrayBufferOrArrayBufferViewOrBlobOrUSVString& data,
-                      ExceptionState&);
-  ScriptPromise truncate(ScriptState*, uint64_t size);
-
- private:
-  ScriptPromise WriteBlob(ScriptState*, uint64_t position, Blob*);
-
-  void WriteComplete(mojom::blink::NativeFileSystemErrorPtr result,
-                     uint64_t bytes_written);
-  void TruncateComplete(mojom::blink::NativeFileSystemErrorPtr result);
-
-  RevocableInterfacePtr<mojom::blink::NativeFileSystemFileWriter> mojo_ptr_;
-
-  Member<ScriptPromiseResolver> pending_operation_;
-};
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
diff --git a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.idl b/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.idl
deleted file mode 100644
index 81435e3a..0000000
--- a/third_party/blink/renderer/modules/native_file_system/native_file_system_writable_file_stream.idl
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// https://wicg.github.io/native-file-system/#filesystemwritablefilestream
-[
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=NativeFileSystemWritableFileStream
- ] interface NativeFileSystemWritableFileStream : WritableStream {
-   [CallWith=ScriptState, RaisesException] Promise<void> write(unsigned long long position, (BufferSource or Blob or USVString) data);
-   [CallWith=ScriptState] Promise<void> truncate(unsigned long long size);
- };
diff --git a/third_party/blink/renderer/modules/notifications/notification_data.cc b/third_party/blink/renderer/modules/notifications/notification_data.cc
index cf54e46..efc43ca 100644
--- a/third_party/blink/renderer/modules/notifications/notification_data.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_data.cc
@@ -83,7 +83,7 @@
                                                vibration_pattern.size());
   notification_data->timestamp = options->hasTimestamp()
                                      ? static_cast<double>(options->timestamp())
-                                     : WTF::CurrentTimeMS();
+                                     : base::Time::Now().ToDoubleT() * 1000.0;
   notification_data->renotify = options->renotify();
   notification_data->silent = options->silent();
   notification_data->require_interaction = options->requireInteraction();
diff --git a/third_party/blink/renderer/modules/notifications/notification_data_test.cc b/third_party/blink/renderer/modules/notifications/notification_data_test.cc
index 8aff9e7..1676bb0 100644
--- a/third_party/blink/renderer/modules/notifications/notification_data_test.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_data_test.cc
@@ -99,7 +99,7 @@
     actions.push_back(action);
   }
 
-  DOMTimeStamp showTimestamp = WTF::CurrentTimeMS();
+  DOMTimeStamp showTimestamp = base::Time::Now().ToDoubleT() * 1000.0;
   TimestampTrigger* showTrigger = TimestampTrigger::Create(showTimestamp);
 
   NotificationOptions* options = NotificationOptions::Create();
@@ -286,7 +286,8 @@
   // The timestamp should be set to the current time since the epoch if it
   // wasn't supplied by the developer. "32" has no significance, but an equal
   // comparison of the value could lead to flaky failures.
-  EXPECT_NEAR(notification_data->timestamp, WTF::CurrentTimeMS(), 32);
+  EXPECT_NEAR(notification_data->timestamp,
+              base::Time::Now().ToDoubleT() * 1000.0, 32);
 }
 
 TEST_F(NotificationDataTest, DirectionValues) {
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 14d33d68..f01a8c90 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -656,7 +656,8 @@
 
   // Make sure no certificates have expired.
   if (!configuration.certificates.empty()) {
-    DOMTimeStamp now = ConvertSecondsToDOMTimeStamp(CurrentTime());
+    DOMTimeStamp now =
+        ConvertSecondsToDOMTimeStamp(base::Time::Now().ToDoubleT());
     for (const rtc::scoped_refptr<rtc::RTCCertificate>& certificate :
          configuration.certificates) {
       DOMTimeStamp expires = certificate->Expires();
@@ -1876,6 +1877,12 @@
   return String();
 }
 
+void RTCPeerConnection::restartIce() {
+  if (closed_)
+    return;
+  peer_handler_->RestartIce();
+}
+
 void RTCPeerConnection::addStream(ScriptState* script_state,
                                   MediaStream* stream,
                                   const Dictionary& media_constraints,
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 894c210e..1cd005d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -193,6 +193,8 @@
 
   String connectionState() const;
 
+  void restartIce();
+
   // A local stream is any stream associated with a sender.
   MediaStreamVector getLocalStreams() const;
   // A remote stream is any stream associated with a receiver.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index 49a9464..1987ea8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -86,6 +86,7 @@
     readonly attribute RTCIceConnectionState iceConnectionState;
     readonly attribute RTCPeerConnectionState connectionState;
     // readonly attribute boolean? canTrickleIceCandidates;
+    void restartIce();
     [CallWith=ScriptState] RTCConfiguration getConfiguration();
     [CallWith=ScriptState, RaisesException] void setConfiguration(RTCConfiguration configuration);
     void close();
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 149fcd9c..1b15dde 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
@@ -124,7 +124,8 @@
     return nullptr;
   }
   for (const auto& certificate : certificates) {
-    if (certificate->expires() < ConvertSecondsToDOMTimeStamp(CurrentTime())) {
+    if (certificate->expires() <
+        ConvertSecondsToDOMTimeStamp(base::Time::Now().ToDoubleT())) {
       exception_state.ThrowTypeError(
           "Cannot construct an RTCQuicTransport with an expired "
           "certificate.");
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
index 5ffd5ba3..eecfa9c 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
@@ -214,7 +214,7 @@
 }
 
 void ScreenOrientationControllerImpl::ContextDestroyed(ExecutionContext*) {
-  screen_orientation_service_ = nullptr;
+  screen_orientation_service_.reset();
   active_lock_ = false;
 }
 
@@ -225,9 +225,11 @@
   Supplement<LocalFrame>::Trace(visitor);
 }
 
-void ScreenOrientationControllerImpl::SetScreenOrientationAssociatedPtrForTests(
-    ScreenOrientationAssociatedPtr screen_orientation_associated_ptr) {
-  screen_orientation_service_ = std::move(screen_orientation_associated_ptr);
+void ScreenOrientationControllerImpl::
+    SetScreenOrientationAssociatedRemoteForTests(
+        mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>
+            remote) {
+  screen_orientation_service_ = std::move(remote);
 }
 
 void ScreenOrientationControllerImpl::OnLockOrientationResult(
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h
index 23a9e4a..55adcb80 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "services/device/public/mojom/screen_orientation.mojom-blink.h"
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h"
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_type.h"
@@ -21,7 +22,6 @@
 
 class ScreenOrientation;
 
-using device::mojom::blink::ScreenOrientationAssociatedPtr;
 using device::mojom::blink::ScreenOrientationLockResult;
 
 class MODULES_EXPORT ScreenOrientationControllerImpl final
@@ -46,8 +46,8 @@
   static void ProvideTo(LocalFrame&);
   static ScreenOrientationControllerImpl* From(LocalFrame&);
 
-  void SetScreenOrientationAssociatedPtrForTests(
-      ScreenOrientationAssociatedPtr);
+  void SetScreenOrientationAssociatedRemoteForTests(
+      mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>);
 
   void Trace(blink::Visitor*) override;
 
@@ -73,7 +73,8 @@
 
   Member<ScreenOrientation> orientation_;
   bool active_lock_ = false;
-  ScreenOrientationAssociatedPtr screen_orientation_service_;
+  mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>
+      screen_orientation_service_;
   std::unique_ptr<WebLockOrientationCallback> pending_callback_;
   int request_id_ = 0;
 
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl_test.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl_test.cc
index bc0ccd0..1cb575c 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl_test.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl_test.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/modules/screen_orientation/web_lock_orientation_callback.h"
@@ -54,14 +54,17 @@
   void SetUp() override {
     PageTestBase::SetUp(IntSize());
     ScreenOrientationControllerImpl::ProvideTo(GetFrame());
-    device::mojom::blink::ScreenOrientationAssociatedPtr screen_orientation;
-    mojo::MakeRequestAssociatedWithDedicatedPipe(&screen_orientation);
-    Controller()->SetScreenOrientationAssociatedPtrForTests(
+    mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>
+        screen_orientation;
+    ignore_result(
+        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
+    Controller()->SetScreenOrientationAssociatedRemoteForTests(
         std::move(screen_orientation));
   }
 
   void TearDown() override {
-    Controller()->SetScreenOrientationAssociatedPtrForTests(nullptr);
+    Controller()->SetScreenOrientationAssociatedRemoteForTests(
+        mojo::AssociatedRemote<device::mojom::blink::ScreenOrientation>());
   }
 
   ScreenOrientationControllerImpl* Controller() {
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 6049ac24..ef8408c 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -380,7 +380,7 @@
 
 void ServiceWorkerGlobalScope::Dispose() {
   DCHECK(IsContextThread());
-  controller_bindings_.CloseAllBindings();
+  controller_receivers_.Clear();
   timeout_timer_.reset();
   service_worker_host_.reset();
   binding_.Close();
@@ -626,14 +626,14 @@
 }
 
 void ServiceWorkerGlobalScope::BindControllerServiceWorker(
-    mojom::blink::ControllerServiceWorkerRequest request) {
+    mojo::PendingReceiver<mojom::blink::ControllerServiceWorker> receiver) {
   DCHECK(IsContextThread());
-  DCHECK(controller_bindings_.empty());
+  DCHECK(controller_receivers_.empty());
   // TODO(falken): Consider adding task types for "the handle fetch task source"
   // and "handle functional event task source" defined in the service worker
   // spec and use them when dispatching events.
-  controller_bindings_.AddBinding(
-      this, std::move(request),
+  controller_receivers_.Add(
+      this, std::move(receiver),
       GetThread()->GetTaskRunner(TaskType::kInternalDefault));
 }
 
@@ -1440,10 +1440,10 @@
 }
 
 void ServiceWorkerGlobalScope::Clone(
-    mojom::blink::ControllerServiceWorkerRequest request) {
+    mojo::PendingReceiver<mojom::blink::ControllerServiceWorker> receiver) {
   DCHECK(IsContextThread());
-  controller_bindings_.AddBinding(
-      this, std::move(request),
+  controller_receivers_.Add(
+      this, std::move(receiver),
       GetThread()->GetTaskRunner(TaskType::kInternalDefault));
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
index d6fbc9e..3ea3f4f 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h
@@ -32,7 +32,8 @@
 
 #include <memory>
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom-blink.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker.mojom-blink.h"
@@ -148,7 +149,7 @@
 
   void BindServiceWorker(mojom::blink::ServiceWorkerRequest);
   void BindControllerServiceWorker(
-      mojom::blink::ControllerServiceWorkerRequest);
+      mojo::PendingReceiver<mojom::blink::ControllerServiceWorker>);
   void OnNavigationPreloadResponse(
       int fetch_event_id,
       std::unique_ptr<WebURLResponse>,
@@ -355,7 +356,8 @@
       mojom::blink::DispatchFetchEventParamsPtr params,
       mojom::blink::ServiceWorkerFetchResponseCallbackPtr response_callback,
       DispatchFetchEventForSubresourceCallback callback) override;
-  void Clone(mojom::blink::ControllerServiceWorkerRequest request) override;
+  void Clone(mojo::PendingReceiver<mojom::blink::ControllerServiceWorker>
+                 reciever) override;
 
   // Implements mojom::blink::ServiceWorker.
   void InitializeGlobalScope(
@@ -522,7 +524,8 @@
   // |timeout_timer_| since the pipe needs to be disconnected before callbacks
   // passed by DispatchSomeEvent() get destructed, which may be stored in
   // |timeout_timer_|
-  mojo::BindingSet<mojom::blink::ControllerServiceWorker> controller_bindings_;
+  mojo::ReceiverSet<mojom::blink::ControllerServiceWorker>
+      controller_receivers_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 63762d1..d1a89c0 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -89,7 +89,8 @@
     mojo::ScopedMessagePipeHandle request) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   WorkerGlobalScope()->BindControllerServiceWorker(
-      mojom::blink::ControllerServiceWorkerRequest(std::move(request)));
+      mojo::PendingReceiver<mojom::blink::ControllerServiceWorker>(
+          std::move(request)));
 }
 
 void ServiceWorkerGlobalScopeProxy::OnNavigationPreloadResponse(
diff --git a/third_party/blink/renderer/modules/shapedetection/barcode_detector.cc b/third_party/blink/renderer/modules/shapedetection/barcode_detector.cc
index e3007ce..27c599799 100644
--- a/third_party/blink/renderer/modules/shapedetection/barcode_detector.cc
+++ b/third_party/blink/renderer/modules/shapedetection/barcode_detector.cc
@@ -131,7 +131,7 @@
       point->setY(corner_point.y);
       corner_points.push_back(point);
     }
-    detected_barcodes.push_back(DetectedBarcode::Create(
+    detected_barcodes.push_back(MakeGarbageCollected<DetectedBarcode>(
         barcode->raw_value,
         DOMRectReadOnly::Create(
             barcode->bounding_box.x, barcode->bounding_box.y,
diff --git a/third_party/blink/renderer/modules/shapedetection/barcode_detector.idl b/third_party/blink/renderer/modules/shapedetection/barcode_detector.idl
index 86f6a7f..f7c5ce69 100644
--- a/third_party/blink/renderer/modules/shapedetection/barcode_detector.idl
+++ b/third_party/blink/renderer/modules/shapedetection/barcode_detector.idl
@@ -8,6 +8,7 @@
     Constructor(optional BarcodeDetectorOptions barcodeDetectorOptions),
     ConstructorCallWith=ExecutionContext,
     Exposed=(Window,Worker),
+    SecureContext,
     MeasureAs=ShapeDetection_BarcodeDetectorConstructor,
     RaisesException=Constructor,
     RuntimeEnabled=ShapeDetection
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_barcode.cc b/third_party/blink/renderer/modules/shapedetection/detected_barcode.cc
index c4a1bb2..9d2467c 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_barcode.cc
+++ b/third_party/blink/renderer/modules/shapedetection/detected_barcode.cc
@@ -4,79 +4,44 @@
 
 #include "third_party/blink/renderer/modules/shapedetection/detected_barcode.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
 
 namespace blink {
 
-DetectedBarcode* DetectedBarcode::Create() {
-  HeapVector<Member<Point2D>> empty_list;
-  return MakeGarbageCollected<DetectedBarcode>(
-      g_empty_string, DOMRectReadOnly::Create(0, 0, 0, 0),
-      shape_detection::mojom::BarcodeFormat::UNKNOWN, empty_list);
-}
-
-DetectedBarcode* DetectedBarcode::Create(
-    String raw_value,
-    DOMRectReadOnly* bounding_box,
-    shape_detection::mojom::BarcodeFormat format,
-    HeapVector<Member<Point2D>> corner_points) {
-  return MakeGarbageCollected<DetectedBarcode>(raw_value, bounding_box, format,
-                                               corner_points);
-}
-
 // static
-WebString DetectedBarcode::BarcodeFormatToString(
+String DetectedBarcode::BarcodeFormatToString(
     const shape_detection::mojom::BarcodeFormat format) {
   switch (format) {
     case shape_detection::mojom::BarcodeFormat::AZTEC:
-      return WebString::FromUTF8("aztec");
+      return "aztec";
     case shape_detection::mojom::BarcodeFormat::CODE_128:
-      return WebString::FromUTF8("code_128");
+      return "code_128";
     case shape_detection::mojom::BarcodeFormat::CODE_39:
-      return WebString::FromUTF8("code_39");
+      return "code_39";
     case shape_detection::mojom::BarcodeFormat::CODE_93:
-      return WebString::FromUTF8("code_93");
+      return "code_93";
     case shape_detection::mojom::BarcodeFormat::CODABAR:
-      return WebString::FromUTF8("codabar");
+      return "codabar";
     case shape_detection::mojom::BarcodeFormat::DATA_MATRIX:
-      return WebString::FromUTF8("data_matrix");
+      return "data_matrix";
     case shape_detection::mojom::BarcodeFormat::EAN_13:
-      return WebString::FromUTF8("ean_13");
+      return "ean_13";
     case shape_detection::mojom::BarcodeFormat::EAN_8:
-      return WebString::FromUTF8("ean_8");
+      return "ean_8";
     case shape_detection::mojom::BarcodeFormat::ITF:
-      return WebString::FromUTF8("itf");
+      return "itf";
     case shape_detection::mojom::BarcodeFormat::PDF417:
-      return WebString::FromUTF8("pdf417");
+      return "pdf417";
     case shape_detection::mojom::BarcodeFormat::QR_CODE:
-      return WebString::FromUTF8("qr_code");
+      return "qr_code";
     case shape_detection::mojom::BarcodeFormat::UNKNOWN:
-      return WebString::FromUTF8("unknown");
+      return "unknown";
     case shape_detection::mojom::BarcodeFormat::UPC_A:
-      return WebString::FromUTF8("upc_a");
+      return "upc_a";
     case shape_detection::mojom::BarcodeFormat::UPC_E:
-      return WebString::FromUTF8("upc_e");
-    default:
-      NOTREACHED() << "Invalid BarcodeFormat";
+      return "upc_e";
   }
-  return WebString();
-}
-
-const String& DetectedBarcode::rawValue() const {
-  return raw_value_;
-}
-
-DOMRectReadOnly* DetectedBarcode::boundingBox() const {
-  return bounding_box_.Get();
-}
-
-const HeapVector<Member<Point2D>>& DetectedBarcode::cornerPoints() const {
-  return corner_points_;
-}
-
-String DetectedBarcode::format() const {
-  return DetectedBarcode::BarcodeFormatToString(format_);
 }
 
 DetectedBarcode::DetectedBarcode(String raw_value,
@@ -88,22 +53,6 @@
       format_(format),
       corner_points_(corner_points) {}
 
-ScriptValue DetectedBarcode::toJSONForBinding(ScriptState* script_state) const {
-  V8ObjectBuilder result(script_state);
-  result.AddString("rawValue", rawValue());
-  result.Add("boundingBox", boundingBox()->toJSONForBinding(script_state));
-  result.AddString("format", format());
-  Vector<ScriptValue> corner_points;
-  for (const auto& corner_point : corner_points_) {
-    V8ObjectBuilder builder(script_state);
-    builder.AddNumber("x", corner_point->x());
-    builder.AddNumber("y", corner_point->y());
-    corner_points.push_back(builder.GetScriptValue());
-  }
-  result.Add("cornerPoints", corner_points);
-  return result.GetScriptValue();
-}
-
 void DetectedBarcode::Trace(blink::Visitor* visitor) {
   visitor->Trace(bounding_box_);
   visitor->Trace(corner_points_);
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_barcode.h b/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
index d4eaeef..560433c 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
+++ b/third_party/blink/renderer/modules/shapedetection/detected_barcode.h
@@ -6,9 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_BARCODE_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"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -16,17 +13,13 @@
 namespace blink {
 
 class DOMRectReadOnly;
+class Point2D;
 
 class MODULES_EXPORT DetectedBarcode final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static DetectedBarcode* Create();
-  static DetectedBarcode* Create(String,
-                                 DOMRectReadOnly*,
-                                 shape_detection::mojom::BarcodeFormat,
-                                 HeapVector<Member<Point2D>>);
-  static WebString BarcodeFormatToString(
+  static String BarcodeFormatToString(
       const shape_detection::mojom::BarcodeFormat format);
 
   DetectedBarcode(String,
@@ -34,12 +27,13 @@
                   shape_detection::mojom::BarcodeFormat,
                   HeapVector<Member<Point2D>>);
 
-  const String& rawValue() const;
-  DOMRectReadOnly* boundingBox() const;
-  String format() const;
-  const HeapVector<Member<Point2D>>& cornerPoints() const;
+  const String& rawValue() const { return raw_value_; }
+  DOMRectReadOnly* boundingBox() const { return bounding_box_; }
+  String format() const { return BarcodeFormatToString(format_); }
+  const HeapVector<Member<Point2D>>& cornerPoints() const {
+    return corner_points_;
+  }
 
-  ScriptValue toJSONForBinding(ScriptState*) const;
   void Trace(blink::Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl b/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
index aa14784..740717e 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
@@ -5,8 +5,9 @@
 // https://wicg.github.io/shape-detection-api/#barcode-detection-api
 
 [
-    Constructor,
     Serializable,
+    Exposed=(Window,Worker),
+    SecureContext,
     RuntimeEnabled=ShapeDetection
 ] interface DetectedBarcode {
     // TODO(mcasas): Implement missing fields. https://crbug.com/646083
@@ -16,7 +17,4 @@
     // 4 corner points in clockwise direction starting with top-left. Due to
     // possible perspective distortions, this is not necessarily a rectangle.
     [SameObject, SaveSameObject] readonly attribute FrozenArray<Point2D> cornerPoints;
-
-    // TODO(peria): toJSON is not in spec. https://crbug.com/736332
-    [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
 };
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_face.cc b/third_party/blink/renderer/modules/shapedetection/detected_face.cc
index beb88c6..bf873d9 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_face.cc
+++ b/third_party/blink/renderer/modules/shapedetection/detected_face.cc
@@ -4,62 +4,15 @@
 
 #include "third_party/blink/renderer/modules/shapedetection/detected_face.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/modules/shapedetection/landmark.h"
 
 namespace blink {
 
-DetectedFace* DetectedFace::Create() {
-  return MakeGarbageCollected<DetectedFace>(
-      DOMRectReadOnly::Create(0, 0, 0, 0));
-}
-
-DetectedFace* DetectedFace::Create(DOMRectReadOnly* bounding_box) {
-  return MakeGarbageCollected<DetectedFace>(bounding_box);
-}
-
-DetectedFace* DetectedFace::Create(
-    DOMRectReadOnly* bounding_box,
-    const HeapVector<Member<Landmark>>& landmarks) {
-  return MakeGarbageCollected<DetectedFace>(bounding_box, landmarks);
-}
-
-DOMRectReadOnly* DetectedFace::boundingBox() const {
-  return bounding_box_.Get();
-}
-
-const HeapVector<Member<Landmark>>& DetectedFace::landmarks() const {
-  return landmarks_;
-}
-
-DetectedFace::DetectedFace(DOMRectReadOnly* bounding_box)
-    : bounding_box_(bounding_box) {}
-
 DetectedFace::DetectedFace(DOMRectReadOnly* bounding_box,
                            const HeapVector<Member<Landmark>>& landmarks)
     : bounding_box_(bounding_box), landmarks_(landmarks) {}
 
-ScriptValue DetectedFace::toJSONForBinding(ScriptState* script_state) const {
-  V8ObjectBuilder result(script_state);
-  result.Add("boundingBox", boundingBox()->toJSONForBinding(script_state));
-  Vector<ScriptValue> landmarks;
-  for (const auto& landmark : landmarks_) {
-    V8ObjectBuilder landmark_builder(script_state);
-    landmark_builder.AddString("type", landmark->type());
-    Vector<ScriptValue> locations;
-    for (const auto& location : landmark->locations()) {
-      V8ObjectBuilder location_builder(script_state);
-      location_builder.AddNumber("x", location->x());
-      location_builder.AddNumber("y", location->y());
-      locations.push_back(location_builder.GetScriptValue());
-    }
-    landmark_builder.Add("locations", locations);
-    landmarks.push_back(landmark_builder.GetScriptValue());
-  }
-  result.Add("landmarks", landmarks);
-  return result.GetScriptValue();
-}
-
 void DetectedFace::Trace(blink::Visitor* visitor) {
   visitor->Trace(bounding_box_);
   visitor->Trace(landmarks_);
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_face.h b/third_party/blink/renderer/modules/shapedetection/detected_face.h
index 40be303..db8828f 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_face.h
+++ b/third_party/blink/renderer/modules/shapedetection/detected_face.h
@@ -5,31 +5,23 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_FACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_FACE_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/modules/shapedetection/landmark.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
 
 class DOMRectReadOnly;
+class Landmark;
 
 class MODULES_EXPORT DetectedFace final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static DetectedFace* Create();
-  static DetectedFace* Create(DOMRectReadOnly*);
-  static DetectedFace* Create(DOMRectReadOnly*,
-                              const HeapVector<Member<Landmark>>&);
-
-  explicit DetectedFace(DOMRectReadOnly*);
   DetectedFace(DOMRectReadOnly*, const HeapVector<Member<Landmark>>&);
 
-  DOMRectReadOnly* boundingBox() const;
-  const HeapVector<Member<Landmark>>& landmarks() const;
+  DOMRectReadOnly* boundingBox() const { return bounding_box_; }
+  const HeapVector<Member<Landmark>>& landmarks() const { return landmarks_; }
 
-  ScriptValue toJSONForBinding(ScriptState*) const;
   void Trace(blink::Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_face.idl b/third_party/blink/renderer/modules/shapedetection/detected_face.idl
index c4b2559..ada91d8 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_face.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_face.idl
@@ -5,14 +5,12 @@
 // https://wicg.github.io/shape-detection-api/#face-detection-api
 
 [
-    Constructor,
     Serializable,
+    Exposed=(Window,Worker),
+    SecureContext,
     RuntimeEnabled=ShapeDetection
 ] interface DetectedFace {
     // TODO(xianglu): Implement any other fields. https://crbug.com/646083
     [SameObject] readonly attribute DOMRectReadOnly boundingBox;
     [SameObject, SaveSameObject] readonly attribute FrozenArray<Landmark> landmarks;
-
-    // TODO(peria): toJSON is not in spec. https://crbug.com/736332
-    [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
 };
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_text.cc b/third_party/blink/renderer/modules/shapedetection/detected_text.cc
index dfd3de3..ba1b3f7 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_text.cc
+++ b/third_party/blink/renderer/modules/shapedetection/detected_text.cc
@@ -4,36 +4,11 @@
 
 #include "third_party/blink/renderer/modules/shapedetection/detected_text.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
 
 namespace blink {
 
-DetectedText* DetectedText::Create() {
-  HeapVector<Member<Point2D>> empty_list;
-  return MakeGarbageCollected<DetectedText>(
-      g_empty_string, DOMRectReadOnly::Create(0, 0, 0, 0), empty_list);
-}
-
-DetectedText* DetectedText::Create(String raw_value,
-                                   DOMRectReadOnly* bounding_box,
-                                   HeapVector<Member<Point2D>> corner_points) {
-  return MakeGarbageCollected<DetectedText>(raw_value, bounding_box,
-                                            corner_points);
-}
-
-const String& DetectedText::rawValue() const {
-  return raw_value_;
-}
-
-DOMRectReadOnly* DetectedText::boundingBox() const {
-  return bounding_box_.Get();
-}
-
-const HeapVector<Member<Point2D>>& DetectedText::cornerPoints() const {
-  return corner_points_;
-}
-
 DetectedText::DetectedText(String raw_value,
                            DOMRectReadOnly* bounding_box,
                            HeapVector<Member<Point2D>> corner_points)
@@ -41,21 +16,6 @@
       bounding_box_(bounding_box),
       corner_points_(corner_points) {}
 
-ScriptValue DetectedText::toJSONForBinding(ScriptState* script_state) const {
-  V8ObjectBuilder result(script_state);
-  result.AddString("rawValue", rawValue());
-  result.Add("boundingBox", boundingBox()->toJSONForBinding(script_state));
-  Vector<ScriptValue> corner_points;
-  for (const auto& corner_point : corner_points_) {
-    V8ObjectBuilder builder(script_state);
-    builder.AddNumber("x", corner_point->x());
-    builder.AddNumber("y", corner_point->y());
-    corner_points.push_back(builder.GetScriptValue());
-  }
-  result.Add("cornerPoints", corner_points);
-  return result.GetScriptValue();
-}
-
 void DetectedText::Trace(blink::Visitor* visitor) {
   visitor->Trace(bounding_box_);
   visitor->Trace(corner_points_);
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_text.h b/third_party/blink/renderer/modules/shapedetection/detected_text.h
index 8af515e..bddec683 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_text.h
+++ b/third_party/blink/renderer/modules/shapedetection/detected_text.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_TEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SHAPEDETECTION_DETECTED_TEXT_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/modules/imagecapture/point_2d.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -14,23 +12,20 @@
 namespace blink {
 
 class DOMRectReadOnly;
+class Point2D;
 
 class MODULES_EXPORT DetectedText final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static DetectedText* Create();
-  static DetectedText* Create(String,
-                              DOMRectReadOnly*,
-                              HeapVector<Member<Point2D>>);
-
   DetectedText(String, DOMRectReadOnly*, HeapVector<Member<Point2D>>);
 
-  const String& rawValue() const;
-  DOMRectReadOnly* boundingBox() const;
-  const HeapVector<Member<Point2D>>& cornerPoints() const;
+  const String& rawValue() const { return raw_value_; }
+  DOMRectReadOnly* boundingBox() const { return bounding_box_; }
+  const HeapVector<Member<Point2D>>& cornerPoints() const {
+    return corner_points_;
+  }
 
-  ScriptValue toJSONForBinding(ScriptState*) const;
   void Trace(blink::Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_text.idl b/third_party/blink/renderer/modules/shapedetection/detected_text.idl
index dcbc270..aa8b1ecd 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_text.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_text.idl
@@ -5,8 +5,9 @@
 // https://wicg.github.io/shape-detection-api/text.html#detectedtext-section
 
 [
-    Constructor,
     Serializable,
+    Exposed=(Window,Worker),
+    SecureContext,
     RuntimeEnabled=ShapeDetection
 ] interface DetectedText {
     [SameObject] readonly attribute DOMString rawValue;
@@ -14,7 +15,4 @@
     // 4 corner points in clockwise direction starting with top-left. Due to
     // possible perspective distortions, this is not necessarily a rectangle.
     [SameObject] readonly attribute FrozenArray<Point2D> cornerPoints;
-
-    // TODO(peria): toJSON is not in spec. https://crbug.com/736332
-    [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
 };
diff --git a/third_party/blink/renderer/modules/shapedetection/face_detector.cc b/third_party/blink/renderer/modules/shapedetection/face_detector.cc
index 008fda9..a0a130d 100644
--- a/third_party/blink/renderer/modules/shapedetection/face_detector.cc
+++ b/third_party/blink/renderer/modules/shapedetection/face_detector.cc
@@ -93,7 +93,7 @@
       landmarks.push_back(web_landmark);
     }
 
-    detected_faces.push_back(DetectedFace::Create(
+    detected_faces.push_back(MakeGarbageCollected<DetectedFace>(
         DOMRectReadOnly::Create(face->bounding_box.x, face->bounding_box.y,
                                 face->bounding_box.width,
                                 face->bounding_box.height),
diff --git a/third_party/blink/renderer/modules/shapedetection/face_detector.idl b/third_party/blink/renderer/modules/shapedetection/face_detector.idl
index 7b943fdb4..574c1a51 100644
--- a/third_party/blink/renderer/modules/shapedetection/face_detector.idl
+++ b/third_party/blink/renderer/modules/shapedetection/face_detector.idl
@@ -8,6 +8,7 @@
     Constructor(optional FaceDetectorOptions faceDetectorOptions),
     ConstructorCallWith=ExecutionContext,
     Exposed=(Window,Worker),
+    SecureContext,
     MeasureAs=ShapeDetection_FaceDetectorConstructor,
     RuntimeEnabled=ShapeDetection
 ] interface FaceDetector {
diff --git a/third_party/blink/renderer/modules/shapedetection/text_detector.cc b/third_party/blink/renderer/modules/shapedetection/text_detector.cc
index 46f08a2e..0db9dc3 100644
--- a/third_party/blink/renderer/modules/shapedetection/text_detector.cc
+++ b/third_party/blink/renderer/modules/shapedetection/text_detector.cc
@@ -67,7 +67,7 @@
       point->setY(corner_point.y);
       corner_points.push_back(point);
     }
-    detected_text.push_back(DetectedText::Create(
+    detected_text.push_back(MakeGarbageCollected<DetectedText>(
         text->raw_value,
         DOMRectReadOnly::Create(text->bounding_box.x, text->bounding_box.y,
                                 text->bounding_box.width,
diff --git a/third_party/blink/renderer/modules/shapedetection/text_detector.idl b/third_party/blink/renderer/modules/shapedetection/text_detector.idl
index e24bf9ac..3fb9ff4a 100644
--- a/third_party/blink/renderer/modules/shapedetection/text_detector.idl
+++ b/third_party/blink/renderer/modules/shapedetection/text_detector.idl
@@ -8,6 +8,7 @@
     Constructor,
     ConstructorCallWith=ExecutionContext,
     Exposed=(Window,Worker),
+    SecureContext,
     MeasureAs=ShapeDetection_TextDetectorConstructor,
     RuntimeEnabled=ShapeDetection
 ] interface TextDetector {
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
index 4f2929d..e6385ef 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
@@ -142,8 +142,14 @@
     return nullptr;
   }
 
-  // Use the first audio track in the media stream.
+  // Find the first track, which is the track whose id comes first given a
+  // lexicographic ordering of the code units of the track id.
   MediaStreamTrack* audio_track = audio_tracks[0];
+  for (auto track : audio_tracks) {
+    if (CodeUnitCompareLessThan(track->id(), audio_track->id())) {
+      audio_track = track;
+    }
+  }
   std::unique_ptr<AudioSourceProvider> provider =
       audio_track->CreateWebAudioSource(context.sampleRate());
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 13b5eb4..282a1c62 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1609,7 +1609,7 @@
   if (resource_provider->IsAccelerated()) {
     base::WeakPtr<WebGraphicsContext3DProviderWrapper> shared_context_wrapper =
         SharedGpuContext::ContextProviderWrapper();
-    if (!shared_context_wrapper)
+    if (!shared_context_wrapper || !shared_context_wrapper->ContextProvider())
       return false;
     gpu::gles2::GLES2Interface* gl =
         shared_context_wrapper->ContextProvider()->ContextGL();
diff --git a/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc b/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
index 5b757df..f9ff4995 100644
--- a/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
+++ b/third_party/blink/renderer/modules/xr/xr_rigid_transform.cc
@@ -24,22 +24,18 @@
   // decompose matrix to position and orientation
   TransformationMatrix::DecomposedType decomposed;
   bool succeeded = matrix_->Decompose(decomposed);
-  if (succeeded) {
-    position_ =
-        DOMPointReadOnly::Create(decomposed.translate_x, decomposed.translate_y,
-                                 decomposed.translate_z, 1.0);
+  DCHECK(succeeded) << "Matrix decompose failed for " << matrix_->ToString();
 
-    // TODO(https://crbug.com/929841): Minuses are needed as a workaround for
-    // bug in TransformationMatrix so that callers can still pass non-inverted
-    // quaternions.
-    orientation_ = makeNormalizedQuaternion(
-        -decomposed.quaternion_x, -decomposed.quaternion_y,
-        -decomposed.quaternion_z, decomposed.quaternion_w);
-  } else {
-    // TODO(crbug.com/969149): Is this the correct way to handle a failure here?
-    position_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
-    orientation_ = DOMPointReadOnly::Create(0.0, 0.0, 0.0, 1.0);
-  }
+  position_ =
+      DOMPointReadOnly::Create(decomposed.translate_x, decomposed.translate_y,
+                               decomposed.translate_z, 1.0);
+
+  // TODO(https://crbug.com/929841): Minuses are needed as a workaround for
+  // bug in TransformationMatrix so that callers can still pass non-inverted
+  // quaternions.
+  orientation_ = makeNormalizedQuaternion(
+      -decomposed.quaternion_x, -decomposed.quaternion_y,
+      -decomposed.quaternion_z, decomposed.quaternion_w);
 }
 
 XRRigidTransform::XRRigidTransform(DOMPointInit* position,
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index ac884299..8b7232f 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -879,12 +879,20 @@
 
     if (display_info_->stage_parameters && display_info->stage_parameters &&
         !display_info_->stage_parameters->Equals(
-            *(display_info->stage_parameters)))
+            *(display_info->stage_parameters))) {
+      // Stage parameters changed.
       stage_parameters_id_++;
-  } else {
-    if (display_info && display_info->stage_parameters) {
+    } else if (!!(display_info_->stage_parameters) !=
+               !!(display_info->stage_parameters)) {
+      // Either stage parameters just became available (sometimes happens if
+      // detecting the bounds doesn't happen until a few seconds into the
+      // session for platforms such as WMR), or the stage parameters just went
+      // away (probably due to tracking loss).
       stage_parameters_id_++;
     }
+  } else if (display_info && display_info->stage_parameters) {
+    // Got stage parameters for the first time this session.
+    stage_parameters_id_++;
   }
 
   display_info_id_++;
diff --git a/third_party/blink/renderer/platform/blob/blob_data_test.cc b/third_party/blink/renderer/platform/blob/blob_data_test.cc
index 6933be5a..4d8821425 100644
--- a/third_party/blink/renderer/platform/blob/blob_data_test.cc
+++ b/third_party/blink/renderer/platform/blob/blob_data_test.cc
@@ -360,7 +360,7 @@
 }
 
 TEST_F(BlobDataHandleTest, CreateFromFileAndFileSystemURL) {
-  double timestamp1 = CurrentTime();
+  double timestamp1 = base::Time::Now().ToDoubleT();
   double timestamp2 = timestamp1 + 1;
   KURL url(NullURL(), "http://example.com/");
   auto data = std::make_unique<BlobData>();
@@ -386,7 +386,7 @@
 }
 
 TEST_F(BlobDataHandleTest, CreateFromFilesystemFileWithUnknownSize) {
-  double timestamp = CurrentTime();
+  double timestamp = base::Time::Now().ToDoubleT();
   KURL url(NullURL(), "http://example.com/");
   Vector<ExpectedElement> expected_elements;
   expected_elements.push_back(ExpectedElement::FileFilesystem(
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 76df417..3164c9b 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
@@ -778,14 +778,15 @@
 //  1. It is not the root transform node.
 //  2. It is a 2d translation only.
 //  3. The transform is not used for scrolling - its ScrollNode() is nullptr.
-//  4. It has no direct compositing reasons, other than k3DTransform. Note
+//  4. The transform is not a StickyTranslation node.
+//  5. It has no direct compositing reasons, other than k3DTransform. Note
 //     that if it has a k3DTransform reason, check #2 above ensures that it
 //     isn't really 3D.
-//  5. It has FlattensInheritedTransform matching that of its direct parent.
-//  6. It has backface visibility matching its direct parent.
-//  7. No clips have local_transform_space referring to this transform node.
-//  8. No effects have local_transform_space referring to this transform node.
-//  9. All child transform nodes are also able to be de-composited.
+//  6. It has FlattensInheritedTransform matching that of its direct parent.
+//  7. It has backface visibility matching its direct parent.
+//  8. No clips have local_transform_space referring to this transform node.
+//  9. No effects have local_transform_space referring to this transform node.
+//  10. All child transform nodes are also able to be de-composited.
 // This algorithm should be O(t+c+e) where t,c,e are the number of transform,
 // clip, and effect nodes in the full tree.
 void PaintArtifactCompositor::DecompositeTransforms(
@@ -819,6 +820,7 @@
          !node->IsRoot() && !can_be_decomposited.Contains(node);
          node = &node->Parent()->Unalias()) {
       if (!node->IsIdentityOr2DTranslation() || node->ScrollNode() ||
+          node->GetStickyConstraint() ||
           node->IsAffectedByOuterViewportBoundsDelta() ||
           node->HasDirectCompositingReasonsOtherThan3dTransform() ||
           !node->FlattensInheritedTransformSameAsParent() ||
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 2586ce96..e62bc0b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -333,14 +333,16 @@
   root_layer_.SetScrollTreeIndex(scroll_node.id);
 }
 
-static bool TransformsToAncestorHaveActiveAnimation(
+static bool TransformsToAncestorHaveNonAxisAlignedActiveAnimation(
     const TransformPaintPropertyNode& descendant,
     const TransformPaintPropertyNode& ancestor) {
   if (&descendant == &ancestor)
     return false;
   for (const auto* n = &descendant; n != &ancestor; n = n->Parent()) {
-    if (n->HasActiveTransformAnimation())
+    if (n->HasActiveTransformAnimation() &&
+        !n->TransformAnimationIsAxisAligned()) {
       return true;
+    }
   }
   return false;
 }
@@ -354,10 +356,9 @@
   if (!translation_2d_or_matrix.IsIdentityOr2DTranslation() &&
       !translation_2d_or_matrix.Matrix().Preserves2dAxisAlignment())
     return true;
-  // Assume any animation can cause 2d axis misalignment.
   const auto& lca = LowestCommonAncestor(a, b);
-  if (TransformsToAncestorHaveActiveAnimation(a, lca) ||
-      TransformsToAncestorHaveActiveAnimation(b, lca))
+  if (TransformsToAncestorHaveNonAxisAlignedActiveAnimation(a, lca) ||
+      TransformsToAncestorHaveNonAxisAlignedActiveAnimation(b, lca))
     return true;
   return false;
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 2b6a3dd3..ba34755 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -128,6 +128,7 @@
     bool flattens_inherited_transform = false;
     bool affected_by_outer_viewport_bounds_delta = false;
     bool in_subtree_of_page_scale = true;
+    bool animation_is_axis_aligned = false;
     BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited;
     unsigned rendering_context_id = 0;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
@@ -141,6 +142,7 @@
           affected_by_outer_viewport_bounds_delta !=
               other.affected_by_outer_viewport_bounds_delta ||
           in_subtree_of_page_scale != other.in_subtree_of_page_scale ||
+          animation_is_axis_aligned != other.animation_is_axis_aligned ||
           backface_visibility != other.backface_visibility ||
           rendering_context_id != other.rendering_context_id ||
           compositor_element_id != other.compositor_element_id ||
@@ -357,6 +359,9 @@
     return state_.direct_compositing_reasons &
            CompositingReason::kActiveTransformAnimation;
   }
+  bool TransformAnimationIsAxisAligned() const {
+    return state_.animation_is_axis_aligned;
+  }
 
   bool RequiresCompositingForRootScroller() const {
     return state_.direct_compositing_reasons & CompositingReason::kRootScroller;
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 94c3050..32222661 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -99,6 +99,7 @@
       marking_worklist_(nullptr),
       not_fully_constructed_worklist_(nullptr),
       weak_callback_worklist_(nullptr),
+      movable_reference_worklist_(nullptr),
       vector_backing_arena_index_(BlinkGC::kVector1ArenaIndex),
       current_arena_ages_(0) {
   if (ThreadState::Current()->IsMainThread())
@@ -162,16 +163,17 @@
 #endif  // DCHECK_IS_ON()
 }
 
-void ThreadHeap::CommitCallbackStacks() {
+void ThreadHeap::SetupWorklists() {
   marking_worklist_.reset(new MarkingWorklist());
   not_fully_constructed_worklist_.reset(new NotFullyConstructedWorklist());
   previously_not_fully_constructed_worklist_.reset(
       new NotFullyConstructedWorklist());
   weak_callback_worklist_.reset(new WeakCallbackWorklist());
+  movable_reference_worklist_.reset(new MovableReferenceWorklist());
   DCHECK(ephemeron_callbacks_.IsEmpty());
 }
 
-void ThreadHeap::DecommitCallbackStacks(BlinkGC::StackState stack_state) {
+void ThreadHeap::DestroyMarkingWorklists(BlinkGC::StackState stack_state) {
   marking_worklist_.reset(nullptr);
   previously_not_fully_constructed_worklist_.reset(nullptr);
   weak_callback_worklist_.reset(nullptr);
@@ -206,14 +208,18 @@
   not_fully_constructed_worklist_.reset(nullptr);
 }
 
+void ThreadHeap::DestroyCompactionWorklists() {
+  movable_reference_worklist_.reset();
+}
+
 HeapCompact* ThreadHeap::Compaction() {
   if (!compaction_)
     compaction_ = std::make_unique<HeapCompact>(this);
   return compaction_.get();
 }
 
-void ThreadHeap::RegisterMovingObjectReference(MovableReference* slot) {
-  Compaction()->RegisterMovingObjectReference(slot);
+bool ThreadHeap::ShouldRegisterMovingObjectReference(MovableReference* slot) {
+  return Compaction()->ShouldRegisterMovingObjectReference(slot);
 }
 
 void ThreadHeap::RegisterMovingObjectCallback(MovableReference* slot,
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 872efa0a..0957a12 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -75,6 +75,10 @@
     Worklist<NotFullyConstructedItem, 16 /* local entries */>;
 using WeakCallbackWorklist =
     Worklist<CustomCallbackItem, 256 /* local entries */>;
+// Using large local segments here (sized 512 entries) to avoid throughput
+// regressions.
+using MovableReferenceWorklist =
+    Worklist<MovableReference*, 512 /* local entries */>;
 
 class PLATFORM_EXPORT HeapAllocHooks {
   STATIC_ONLY(HeapAllocHooks);
@@ -213,17 +217,22 @@
     return weak_callback_worklist_.get();
   }
 
+  MovableReferenceWorklist* GetMovableReferenceWorklist() const {
+    return movable_reference_worklist_.get();
+  }
+
   // Register an ephemeron table for fixed-point iteration.
   void RegisterWeakTable(void* container_object,
                          EphemeronCallback);
 
   // Heap compaction registration methods:
 
-  // Register |slot| as containing a reference to a movable heap object.
+  // Checks whether we need to register |slot| as containing a reference to
+  // a movable heap object.
   //
   // When compaction moves the object pointed to by |*slot| to |newAddress|,
   // |*slot| must be updated to hold |newAddress| instead.
-  void RegisterMovingObjectReference(MovableReference*);
+  bool ShouldRegisterMovingObjectReference(MovableReference*);
 
   // Register a callback to be invoked upon moving the object starting at
   // |reference|; see |MovingObjectCallback| documentation for details.
@@ -394,8 +403,9 @@
  private:
   static int ArenaIndexForObjectSize(size_t);
 
-  void CommitCallbackStacks();
-  void DecommitCallbackStacks(BlinkGC::StackState);
+  void SetupWorklists();
+  void DestroyMarkingWorklists(BlinkGC::StackState);
+  void DestroyCompactionWorklists();
 
   void InvokeEphemeronCallbacks(Visitor*);
 
@@ -428,6 +438,11 @@
   // processed after finishing marking objects.
   std::unique_ptr<WeakCallbackWorklist> weak_callback_worklist_;
 
+  // The worklist is to remember slots that are traced during
+  // marking phases. The mapping between the slots and the backing stores are
+  // created at the atomic pause phase.
+  std::unique_ptr<MovableReferenceWorklist> movable_reference_worklist_;
+
   // No duplicates allowed for ephemeron callbacks. Hence, we use a hashmap
   // with the key being the HashTable.
   WTF::HashMap<void*, EphemeronCallback> ephemeron_callbacks_;
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index a2112be..13d7259d 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -384,13 +384,10 @@
   force_for_next_gc_ = false;
 }
 
-void HeapCompact::RegisterMovingObjectReference(MovableReference* slot) {
+bool HeapCompact::ShouldRegisterMovingObjectReference(MovableReference* slot) {
   CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(slot)));
 
-  if (!do_compact_)
-    return;
-
-  traced_slots_.insert(slot);
+  return do_compact_;
 }
 
 void HeapCompact::RegisterMovingObjectCallback(MovableReference* slot,
@@ -458,13 +455,15 @@
     return;
 
   last_fixup_count_for_testing_ = 0;
-  for (auto** slot : traced_slots_) {
+  MovableReferenceWorklist::View traced_slots(
+      heap_->GetMovableReferenceWorklist(), WorklistTaskId::MainThread);
+  MovableReference* slot;
+  while (traced_slots.Pop(&slot)) {
     if (*slot) {
       Fixups().AddOrFilter(slot);
       last_fixup_count_for_testing_++;
     }
   }
-  traced_slots_.clear();
 }
 
 void HeapCompact::Finish() {
@@ -484,9 +483,9 @@
     return;
 
   last_fixup_count_for_testing_ = 0;
-  traced_slots_.clear();
-  fixups_.reset();
   do_compact_ = false;
+  heap_->GetMovableReferenceWorklist()->Clear();
+  fixups_.reset();
 }
 
 void HeapCompact::AddCompactingPage(BasePage* page) {
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h
index e79e12d6..14f2f4f 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.h
+++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -63,8 +63,8 @@
     return do_compact_ && (compactable_arenas_ & (0x1u << arena_index));
   }
 
-  // See |Heap::RegisterMovingObjectReference()| documentation.
-  void RegisterMovingObjectReference(MovableReference* slot);
+  // See |Heap::ShouldRegisterMovingObjectReference()| documentation.
+  bool ShouldRegisterMovingObjectReference(MovableReference* slot);
 
   // See |Heap::RegisterMovingObjectCallback()| documentation.
   void RegisterMovingObjectCallback(MovableReference*,
@@ -134,11 +134,6 @@
   ThreadHeap* const heap_;
   std::unique_ptr<MovableObjectFixups> fixups_;
 
-  // The set is to remember slots that traced during
-  // marking phases. The mapping between the slots and the backing stores are
-  // created at the atomic pause phase.
-  HashSet<MovableReference*> traced_slots_;
-
   // Set to |true| when a compacting sweep will go ahead.
   bool do_compact_ = false;
   size_t gc_count_since_last_compaction_ = 0;
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 3a04dce..cfe3dee8 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -1408,6 +1408,11 @@
     unfinalized_freelist_.push_back(std::move(entry));
   } else {
     cached_freelist_.Add(start, size);
+#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
+    if (Arena()->GetThreadState()->IsMemoryReducingGC()) {
+      DiscardPages(start + sizeof(FreeListEntry), start + size);
+    }
+#endif
   }
 }
 
@@ -1418,6 +1423,12 @@
 
   for (const FutureFreelistEntry& entry : unfinalized_freelist_) {
     arena->AddToFreeList(entry.start, entry.size);
+#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
+    if (Arena()->GetThreadState()->IsMemoryReducingGC()) {
+      DiscardPages(entry.start + sizeof(FreeListEntry),
+                   entry.start + entry.size);
+    }
+#endif
   }
   unfinalized_freelist_.clear();
 }
@@ -1467,12 +1478,6 @@
       AddToFreeList(start_of_gap, header_address - start_of_gap, finalize_type,
                     found_finalizer);
       found_finalizer = false;
-#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
-      // Discarding pages increases page faults and may regress performance.
-      // So we enable this only on low-RAM devices.
-      if (MemoryPressureListenerRegistry::IsLowEndDevice())
-        DiscardPages(start_of_gap + sizeof(FreeListEntry), header_address);
-#endif
     }
     object_start_bit_map()->SetBit(header_address);
     header->Unmark<HeapObjectHeader::AccessMode::kAtomic>();
@@ -1485,10 +1490,6 @@
   if (start_of_gap != Payload() && start_of_gap != PayloadEnd()) {
     AddToFreeList(start_of_gap, PayloadEnd() - start_of_gap, finalize_type,
                   found_finalizer);
-#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
-    if (MemoryPressureListenerRegistry::IsLowEndDevice())
-      DiscardPages(start_of_gap + sizeof(FreeListEntry), PayloadEnd());
-#endif
   }
   return start_of_gap == Payload();
 }
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index f420ec3..7d1e01ec 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -91,11 +91,12 @@
         thread_state_->IsSweepingInProgress()) {
       TestSupportingGC::PreciselyCollectGarbage();
     }
-    heap_.CommitCallbackStacks();
+    heap_.SetupWorklists();
   }
 
   ~IncrementalMarkingScopeBase() {
-    heap_.DecommitCallbackStacks(BlinkGC::StackState::kNoHeapPointersOnStack);
+    heap_.DestroyMarkingWorklists(BlinkGC::StackState::kNoHeapPointersOnStack);
+    heap_.DestroyCompactionWorklists();
   }
 
   ThreadHeap& heap() const { return heap_; }
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 1c9bfab..2b1ebf65 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -26,6 +26,8 @@
                                       WorklistTaskId::MainThread),
       weak_callback_worklist_(Heap().GetWeakCallbackWorklist(),
                               WorklistTaskId::MainThread),
+      movable_reference_worklist_(Heap().GetMovableReferenceWorklist(),
+                                  WorklistTaskId::MainThread),
       marking_mode_(marking_mode) {
   DCHECK(state->InAtomicMarkingPause());
 #if DCHECK_IS_ON()
@@ -33,6 +35,10 @@
 #endif  // DCHECK_IS_ON
 }
 
+void MarkingVisitorBase::FlushCompactionWorklists() {
+  movable_reference_worklist_.FlushToGlobal();
+}
+
 void MarkingVisitorBase::RegisterWeakCallback(void* object,
                                               WeakCallback callback) {
   // We don't want to run weak processings when taking a snapshot.
@@ -44,10 +50,11 @@
 void MarkingVisitorBase::RegisterBackingStoreReference(void** slot) {
   if (marking_mode_ != kGlobalMarkingWithCompaction)
     return;
-  // TODO(mlippautz): Do not call into heap directly but rather use a Worklist
-  // as temporary storage.
-  Heap().RegisterMovingObjectReference(
-      reinterpret_cast<MovableReference*>(slot));
+  MovableReference* movable_reference =
+      reinterpret_cast<MovableReference*>(slot);
+  if (Heap().ShouldRegisterMovingObjectReference(movable_reference)) {
+    movable_reference_worklist_.Push(movable_reference);
+  }
 }
 
 void MarkingVisitorBase::RegisterBackingStoreCallback(
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index 3c4a767..79cb1b8 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -103,6 +103,9 @@
   // Unused cross-component visit methods.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) override {}
 
+  // Flush private segments remaining in visitor's worklists to global pools.
+  void FlushCompactionWorklists();
+
   size_t marked_bytes() const { return marked_bytes_; }
 
  protected:
@@ -125,6 +128,7 @@
   MarkingWorklist::View marking_worklist_;
   NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
   WeakCallbackWorklist::View weak_callback_worklist_;
+  MovableReferenceWorklist::View movable_reference_worklist_;
   size_t marked_bytes_ = 0;
   const MarkingMode marking_mode_;
 };
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 75a2694..8669aa8 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -1528,6 +1528,7 @@
     SweepForbiddenScope scope(this);
     NoAllocationScope no_allocation_scope(this);
     Heap().Compact();
+    Heap().DestroyCompactionWorklists();
   }
 
 #if defined(ADDRESS_SANITIZER)
@@ -1578,7 +1579,7 @@
                                     BlinkGC::MarkingType marking_type,
                                     BlinkGC::GCReason reason) {
   SetGCPhase(GCPhase::kMarking);
-  Heap().CommitCallbackStacks();
+  Heap().SetupWorklists();
 
   const bool take_snapshot = marking_type == BlinkGC::kTakeSnapshot;
 
@@ -1659,8 +1660,11 @@
     VisitWeakPersistents(visitor);
     Heap().WeakProcessing(visitor);
   }
-  Heap().DecommitCallbackStacks(current_gc_data_.stack_state);
+  Heap().DestroyMarkingWorklists(current_gc_data_.stack_state);
 
+  // TODO(omerkatz): When migrating to concurrent marking, the following 3
+  // lines will need to be wrapped with a loop iterating over all visitors.
+  current_gc_data_.visitor->FlushCompactionWorklists();
   const size_t marked_bytes = current_gc_data_.visitor->marked_bytes();
   current_gc_data_.visitor.reset();
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 5759471..28585e8d 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -432,6 +432,12 @@
 
   bool VerifyMarkingEnabled() const;
 
+  // Returns true if the current GC is a memory reducing GC.
+  bool IsMemoryReducingGC() {
+    return current_gc_data_.reason ==
+           BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC;
+  }
+
  private:
   // Stores whether some ThreadState is currently in incremental marking.
   static AtomicEntryFlag incremental_marking_flag_;
@@ -531,10 +537,6 @@
   // (aka being under "memory pressure".)
   bool ShouldForceMemoryPressureGC();
 
-  // Returns true if shouldForceMemoryPressureGC() held and a
-  // conservative GC was performed to handle the emergency.
-  bool ForceMemoryPressureGCIfNeeded();
-
   size_t EstimatedLiveSize(size_t current_size, size_t size_at_last_gc);
   size_t TotalMemorySize();
   double HeapGrowingRate();
diff --git a/third_party/blink/renderer/platform/heap/worklist.h b/third_party/blink/renderer/platform/heap/worklist.h
index 5dc2bcf..724b91f 100644
--- a/third_party/blink/renderer/platform/heap/worklist.h
+++ b/third_party/blink/renderer/platform/heap/worklist.h
@@ -60,6 +60,8 @@
 
     bool IsGlobalPoolEmpty() { return worklist_->IsGlobalPoolEmpty(); }
 
+    void FlushToGlobal() { worklist_->FlushToGlobal(task_id_); }
+
     size_t LocalPushSegmentSize() const {
       return worklist_->LocalPushSegmentSize(task_id_);
     }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 6c98c72..c0be93eb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -75,6 +75,20 @@
              : blink::mojom::CodeCacheType::kJavascript;
 }
 
+void GetSharedBufferMemoryDump(SharedBuffer* buffer,
+                               const String& dump_prefix,
+                               WebProcessMemoryDump* memory_dump) {
+  size_t dump_size;
+  String dump_name;
+  buffer->GetMemoryDumpNameAndSize(dump_name, dump_size);
+
+  WebMemoryAllocatorDump* dump =
+      memory_dump->CreateMemoryAllocatorDump(dump_prefix + dump_name);
+  dump->AddScalar("size", "bytes", dump_size);
+  memory_dump->AddSuballocation(
+      dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
+}
+
 }  // namespace
 
 // These response headers are not copied from a revalidated response to the
@@ -885,7 +899,7 @@
     dump->AddScalar("dead_size", "bytes", encoded_size_memory_usage_);
 
   if (data_)
-    data_->OnMemoryDump(dump_name, memory_dump);
+    GetSharedBufferMemoryDump(Data(), dump_name, memory_dump);
 
   if (level_of_detail == WebMemoryDumpLevelOfDetail::kDetailed) {
     String url_to_report = Url().GetString();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 6e086fa..d62a5fac 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -546,6 +546,12 @@
       name: "ExpensiveBackgroundTimerThrottling",
       status: "stable",
     },
+    // Experimental feature to try dynamic delegation of autoplay capability
+    // through postMessages.
+    {
+      name: "ExperimentalAutoplayDynamicDelegation",
+      origin_trial_feature_name: "ExperimentalAutoplayDynamicDelegation",
+    },
     {
       name: "ExperimentalContentSecurityPolicyFeatures",
       status: "experimental",
@@ -797,14 +803,14 @@
     },
     {
       name: "LargestContentfulPaint",
-      status: "experimental",
+      status: "stable",
     },
     {
       // Exposes layout shift scores to Javascript. See explainer:
       // http://bit.ly/lsm-explainer.
       name: "LayoutInstabilityAPI",
       origin_trial_feature_name: "LayoutJankAPI",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "LayoutNG",
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread.cc b/third_party/blink/renderer/platform/scheduler/common/thread.cc
index 27fb9276..b657a3c8 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/thread.cc
@@ -29,16 +29,6 @@
 
 namespace {
 
-// Controls whether we use ThreadPriority::DISPLAY for compositor thread.
-const base::Feature kBlinkCompositorUseDisplayThreadPriority {
-  "BlinkCompositorUseDisplayThreadPriority",
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
-
 // Thread-local storage for "blink::Thread"s.
 Thread*& ThreadTLSSlot() {
   DEFINE_THREAD_SAFE_STATIC_LOCAL(WTF::ThreadSpecific<Thread*>, thread_tls_slot,
@@ -112,7 +102,8 @@
   DCHECK(!GetCompositorThread());
 
   ThreadCreationParams params(WebThreadType::kCompositorThread);
-  if (base::FeatureList::IsEnabled(kBlinkCompositorUseDisplayThreadPriority))
+  if (base::FeatureList::IsEnabled(
+          features::kBlinkCompositorUseDisplayThreadPriority))
     params.thread_priority = base::ThreadPriority::DISPLAY;
 
   auto compositor_thread =
@@ -120,7 +111,8 @@
   compositor_thread->Init();
   GetCompositorThread() = std::move(compositor_thread);
 
-  if (base::FeatureList::IsEnabled(kBlinkCompositorUseDisplayThreadPriority)) {
+  if (base::FeatureList::IsEnabled(
+          features::kBlinkCompositorUseDisplayThreadPriority)) {
     // Chrome OS moves tasks between control groups on thread priority changes.
     // This is not possible inside the sandbox, so ask the browser to do it.
     // TODO(spang): Check if we can remove this on non-Chrome OS builds.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h
index d16357c..4739e0e 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h
@@ -16,7 +16,6 @@
   kMainFrame = 0,
   kSameOriginFrame = 1,
   kCrossOriginFrame = 2,
-
   kCount = 3
 };
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 25e86aa..321fb83c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -1094,7 +1094,8 @@
       .SetCanBeThrottled(true)
       .SetCanBeFrozen(true)
       .SetCanBeDeferred(true)
-      .SetCanBePaused(true);
+      .SetCanBePaused(true)
+      .SetShouldUseVirtualTime(true);
 }
 
 // static
@@ -1104,15 +1105,18 @@
       .SetCanBeDeferred(true)
       .SetCanBeFrozen(base::FeatureList::IsEnabled(
           blink::features::kStopNonTimersInBackground))
-      .SetCanBePaused(true);
+      .SetCanBePaused(true)
+      .SetShouldUseVirtualTime(true);
 }
 
 // static
-MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::PausableTaskQueueTraits() {
+MainThreadTaskQueue::QueueTraits
+FrameSchedulerImpl::PausableTaskQueueTraits() {
   return QueueTraits()
       .SetCanBeFrozen(base::FeatureList::IsEnabled(
           blink::features::kStopNonTimersInBackground))
-      .SetCanBePaused(true);
+      .SetCanBePaused(true)
+      .SetShouldUseVirtualTime(true);
 }
 
 // static
@@ -1120,23 +1124,25 @@
 FrameSchedulerImpl::FreezableTaskQueueTraits() {
   // Should not use VirtualTime because using VirtualTime would make the task
   // execution non-deterministic and produce timeouts failures.
-  return QueueTraits().SetCanBeFrozen(true).SetShouldUseVirtualTime(false);
+  return QueueTraits().SetCanBeFrozen(true);
 }
 
 // static
 MainThreadTaskQueue::QueueTraits
 FrameSchedulerImpl::UnpausableTaskQueueTraits() {
-  return QueueTraits();
+  return QueueTraits().SetShouldUseVirtualTime(true);
 }
 
 MainThreadTaskQueue::QueueTraits
 FrameSchedulerImpl::ForegroundOnlyTaskQueueTraits() {
-  return ThrottleableTaskQueueTraits().SetCanRunInBackground(false);
+  return ThrottleableTaskQueueTraits()
+      .SetCanRunInBackground(false)
+      .SetShouldUseVirtualTime(true);
 }
 
 MainThreadTaskQueue::QueueTraits
 FrameSchedulerImpl::DoesNotUseVirtualTimeTaskQueueTraits() {
-  return UnpausableTaskQueueTraits().SetShouldUseVirtualTime(false);
+  return QueueTraits().SetShouldUseVirtualTime(false);
 }
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 403c405..43303c8 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -1775,36 +1775,41 @@
   EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
                                               .SetCanBeThrottled(true)
                                               .SetCanBeFrozen(true)
-                                              .SetCanBePaused(true));
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kMediaElementEvent);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeFrozen(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeFrozen(true)
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kDatabaseAccess);
-  EXPECT_EQ(task_queue->GetQueueTraits(),
-            MainThreadTaskQueue::QueueTraits().SetCanBePaused(true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kDOMManipulation);
   EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
                                               .SetCanBeFrozen(true)
                                               .SetCanBeDeferred(true)
-                                              .SetCanBePaused(true));
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   // Test some task types that were not configured through field trial
   // parameters.
   task_queue = GetTaskQueue(TaskType::kInternalIPC);
-  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits());
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeDeferred(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeDeferred(true)
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 }
 
+
 class FreezableOnlyTaskTypesTest
     : public ThrottleAndFreezeTaskTypesExperimentTest {
  public:
@@ -1826,20 +1831,18 @@
 
   // Check that the overrides work.
   auto task_queue = GetTaskQueue(TaskType::kPostedMessage);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeFrozen(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeFrozen(true)
+                                              .SetCanBePaused(true));
 
   task_queue = GetTaskQueue(TaskType::kMediaElementEvent);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeFrozen(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeFrozen(true)
+                                              .SetCanBePaused(true));
 
   task_queue = GetTaskQueue(TaskType::kDatabaseAccess);
-  EXPECT_EQ(task_queue->GetQueueTraits(),
-            MainThreadTaskQueue::QueueTraits().SetCanBePaused(true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBePaused(true));
 
   task_queue = GetTaskQueue(TaskType::kDOMManipulation);
   EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
@@ -1853,10 +1856,9 @@
   EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits());
 
   task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeDeferred(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeDeferred(true)
+                                              .SetCanBePaused(true));
 }
 
 class ThrottleableOnlyTaskTypesTest
@@ -1879,35 +1881,38 @@
 
   // Check that the overrides work.
   auto task_queue = GetTaskQueue(TaskType::kPostedMessage);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeThrottled(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeThrottled(true)
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kMediaElementEvent);
-  EXPECT_EQ(task_queue->GetQueueTraits(),
-            MainThreadTaskQueue::QueueTraits().SetCanBePaused(true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kDatabaseAccess);
-  EXPECT_EQ(task_queue->GetQueueTraits(),
-            MainThreadTaskQueue::QueueTraits().SetCanBePaused(true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kDOMManipulation);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeDeferred(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeDeferred(true)
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 
   // Test some task types that were not configured through field trial
   // parameters.
   task_queue = GetTaskQueue(TaskType::kInternalIPC);
-  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits());
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                          .SetShouldUseVirtualTime(true));
 
   task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI);
-  EXPECT_EQ(
-      task_queue->GetQueueTraits(),
-      MainThreadTaskQueue::QueueTraits().SetCanBeDeferred(true).SetCanBePaused(
-          true));
+  EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()
+                                              .SetCanBeDeferred(true)
+                                              .SetCanBePaused(true)
+                                              .SetShouldUseVirtualTime(true));
 }
 
 class FrameSchedulerImplDatabaseAccessWithoutHighPriority
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
index d2ea65b..e7273ae 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -123,23 +123,29 @@
                                        .SetCanBeThrottled(true)
                                        .SetCanBeDeferred(true)
                                        .SetCanBeFrozen(true)
-                                       .SetCanBePaused(true));
+                                       .SetCanBePaused(true)
+                                       .SetShouldUseVirtualTime(true));
   EXPECT_FALSE(all_task_queues.Contains(task_queue));
   all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
   EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
 
-  task_queue = NonLoadingTaskQueue(
-      QueueTraits().SetCanBeDeferred(true).SetCanBePaused(true));
+  task_queue = NonLoadingTaskQueue(QueueTraits()
+                                        .SetCanBeDeferred(true)
+                                        .SetCanBePaused(true)
+                                        .SetShouldUseVirtualTime(true));
   EXPECT_FALSE(all_task_queues.Contains(task_queue));
   all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
   EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
 
-  task_queue = NonLoadingTaskQueue(QueueTraits().SetCanBePaused(true));
+  task_queue = NonLoadingTaskQueue(QueueTraits()
+                                        .SetCanBePaused(true)
+                                        .SetShouldUseVirtualTime(true));
   EXPECT_FALSE(all_task_queues.Contains(task_queue));
   all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
   EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
 
-  task_queue = NonLoadingTaskQueue(QueueTraits());
+  task_queue = NonLoadingTaskQueue(QueueTraits()
+                                        .SetShouldUseVirtualTime(true));
   EXPECT_FALSE(all_task_queues.Contains(task_queue));
   all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
   EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 44106d40..2fca44c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -730,8 +730,7 @@
   // If this is a timer queue, and virtual time is enabled and paused, it should
   // be suspended by adding a fence to prevent immediate tasks from running when
   // they're not supposed to.
-  if (queue_class == MainThreadTaskQueue::QueueClass::kTimer &&
-      main_thread_only().virtual_time_stopped &&
+  if (main_thread_only().virtual_time_stopped &&
       main_thread_only().use_virtual_time &&
       task_queue->ShouldUseVirtualTime()) {
     task_queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
@@ -762,7 +761,8 @@
                           .SetCanBeFrozen(true)
                           .SetCanBeDeferred(true)
                           .SetCanBeThrottled(true)
-                          .SetFrameScheduler(frame_scheduler));
+                          .SetFrameScheduler(frame_scheduler)
+                          .SetShouldUseVirtualTime(true));
 }
 
 std::unique_ptr<WebRenderWidgetSchedulingState>
@@ -1810,10 +1810,8 @@
   for (const auto& pair : task_runners_) {
     if (!pair.first->ShouldUseVirtualTime())
       continue;
-    if (pair.first->queue_class() == MainThreadTaskQueue::QueueClass::kTimer) {
-      DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
-      pair.first->InsertFence(TaskQueue::InsertFencePosition::kNow);
-    }
+    DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
+    pair.first->InsertFence(TaskQueue::InsertFencePosition::kNow);
   }
 }
 
@@ -1821,11 +1819,9 @@
   for (const auto& pair : task_runners_) {
     if (!pair.first->ShouldUseVirtualTime())
       continue;
-    if (pair.first->queue_class() == MainThreadTaskQueue::QueueClass::kTimer) {
-      DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
-      DCHECK(pair.first->HasActiveFence());
-      pair.first->RemoveFence();
-    }
+    DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
+    DCHECK(pair.first->HasActiveFence());
+    pair.first->RemoveFence();
   }
 }
 
@@ -2046,7 +2042,7 @@
 MainThreadSchedulerImpl::TimeDomainType
 MainThreadSchedulerImpl::TaskQueuePolicy::GetTimeDomainType(
     MainThreadTaskQueue* task_queue) const {
-  if (use_virtual_time && task_queue->ShouldUseVirtualTime())
+  if (use_virtual_time)
     return TimeDomainType::kVirtual;
   return TimeDomainType::kReal;
 }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 2b577d4..23a4a06 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -104,7 +104,7 @@
           can_be_paused(false),
           can_be_frozen(false),
           can_run_in_background(true),
-          should_use_virtual_time(true),
+          should_use_virtual_time(false),
           is_high_priority(false) {}
 
     QueueTraits(const QueueTraits&) = default;
diff --git a/third_party/blink/renderer/platform/shared_buffer.cc b/third_party/blink/renderer/platform/shared_buffer.cc
index 6b9e68f..9c1c77e 100644
--- a/third_party/blink/renderer/platform/shared_buffer.cc
+++ b/third_party/blink/renderer/platform/shared_buffer.cc
@@ -234,23 +234,11 @@
   return data;
 }
 
-void SharedBuffer::OnMemoryDump(const String& dump_prefix,
-                                WebProcessMemoryDump* memory_dump) const {
-  if (buffer_.size()) {
-    WebMemoryAllocatorDump* dump =
-        memory_dump->CreateMemoryAllocatorDump(dump_prefix + "/shared_buffer");
-    dump->AddScalar("size", "bytes", buffer_.size());
-    memory_dump->AddSuballocation(
-        dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
-  } else {
-    // If there is data in the segments, then it should have been allocated
-    // using fastMalloc.
-    const String data_dump_name = dump_prefix + "/segments";
-    auto* dump = memory_dump->CreateMemoryAllocatorDump(data_dump_name);
-    dump->AddScalar("size", "bytes", size_);
-    memory_dump->AddSuballocation(
-        dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
-  }
+void SharedBuffer::GetMemoryDumpNameAndSize(String& dump_name,
+                                            size_t& dump_size) const {
+  size_t buffer_size = buffer_.size();
+  dump_name = buffer_size ? "/shared_buffer" : "/segments";
+  dump_size = buffer_size ? buffer_size : size_;
 }
 
 SharedBuffer::DeprecatedFlatData::DeprecatedFlatData(
diff --git a/third_party/blink/renderer/platform/shared_buffer.h b/third_party/blink/renderer/platform/shared_buffer.h
index d5310b2e..71cdd0c 100644
--- a/third_party/blink/renderer/platform/shared_buffer.h
+++ b/third_party/blink/renderer/platform/shared_buffer.h
@@ -45,8 +45,6 @@
 
 namespace blink {
 
-class WebProcessMemoryDump;
-
 class PLATFORM_EXPORT SharedBuffer : public RefCounted<SharedBuffer> {
  public:
   // Iterator for ShreadBuffer contents. An Iterator will get invalid once the
@@ -182,7 +180,7 @@
   // SkData without merging segmented buffers into a flat buffer.
   sk_sp<SkData> GetAsSkData() const;
 
-  void OnMemoryDump(const String& dump_prefix, WebProcessMemoryDump*) const;
+  void GetMemoryDumpNameAndSize(String& dump_name, size_t& dump_size) const;
 
   // Helper for providing a contiguous view of the data.  If the SharedBuffer is
   // segmented, this will copy/merge all segments into a temporary buffer.
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 768c64b2..598aa03 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
@@ -316,6 +316,8 @@
   return webrtc::RTCErrorType::NONE;
 }
 
+void MockWebRTCPeerConnectionHandler::RestartIce() {}
+
 void MockWebRTCPeerConnectionHandler::GetStats(const WebRTCStatsRequest&) {}
 
 void MockWebRTCPeerConnectionHandler::GetStats(
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 058efc0..39d569dc 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
@@ -51,6 +51,7 @@
       const override;
   webrtc::RTCErrorType SetConfiguration(
       const webrtc::PeerConnectionInterface::RTCConfiguration&) override;
+  void RestartIce() override;
   void GetStats(const WebRTCStatsRequest&) override;
   void GetStats(WebRTCStatsReportCallback,
                 const WebVector<webrtc::NonStandardGroupId>&) override;
diff --git a/third_party/blink/renderer/platform/transforms/identity_transform_operation.h b/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
index c28d2be..b3d2cab 100644
--- a/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
@@ -58,6 +58,8 @@
 
   scoped_refptr<TransformOperation> Zoom(double factor) final { return this; }
 
+  bool PreservesAxisAlignment() const final { return true; }
+
   IdentityTransformOperation() = default;
 };
 
diff --git a/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h b/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
index 9a2875e7..5b82a7d3 100644
--- a/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
@@ -69,6 +69,10 @@
                   progress_);
   }
 
+  bool PreservesAxisAlignment() const final {
+    return from_.PreservesAxisAlignment() && to_.PreservesAxisAlignment();
+  }
+
   bool DependsOnBoxSize() const override {
     return from_.DependsOnBoxSize() || to_.DependsOnBoxSize();
   }
diff --git a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
index 304425e..c5fd803 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
@@ -69,6 +69,10 @@
       bool blend_to_identity = false) override;
   scoped_refptr<TransformOperation> Zoom(double factor) final;
 
+  bool PreservesAxisAlignment() const final {
+    return matrix_.Preserves2dAxisAlignment();
+  }
+
   Matrix3DTransformOperation(const TransformationMatrix& mat) { matrix_ = mat; }
 
   TransformationMatrix matrix_;
diff --git a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
index 409dd21..2c3c43a3 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
@@ -83,6 +83,10 @@
       bool blend_to_identity = false) override;
   scoped_refptr<TransformOperation> Zoom(double factor) final;
 
+  bool PreservesAxisAlignment() const final {
+    return Matrix().Preserves2dAxisAlignment();
+  }
+
   MatrixTransformOperation(double a,
                            double b,
                            double c,
diff --git a/third_party/blink/renderer/platform/transforms/scale_transform_operation.h b/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
index d21dd97..9ad90334 100644
--- a/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
@@ -84,6 +84,8 @@
 
   scoped_refptr<TransformOperation> Zoom(double factor) final { return this; }
 
+  bool PreservesAxisAlignment() const final { return true; }
+
   ScaleTransformOperation(double sx, double sy, double sz, OperationType type)
       : x_(sx), y_(sy), z_(sz), type_(type) {
     DCHECK(IsMatchingOperationType(type));
diff --git a/third_party/blink/renderer/platform/transforms/transform_operation.h b/third_party/blink/renderer/platform/transforms/transform_operation.h
index acafaeb..ad257d3 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/transform_operation.h
@@ -90,6 +90,8 @@
   }
   virtual bool CanBlendWith(const TransformOperation& other) const = 0;
 
+  virtual bool PreservesAxisAlignment() const { return false; }
+
   bool Is3DOperation() const {
     OperationType op_type = GetType();
     return op_type == kScaleZ || op_type == kScale3D ||
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations.h b/third_party/blink/renderer/platform/transforms/transform_operations.h
index 65e646b6..c7ef91f 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations.h
+++ b/third_party/blink/renderer/platform/transforms/transform_operations.h
@@ -72,6 +72,14 @@
     return false;
   }
 
+  bool PreservesAxisAlignment() const {
+    for (auto& operation : operations_) {
+      if (!operation->PreservesAxisAlignment())
+        return false;
+    }
+    return true;
+  }
+
   // Returns true if any operation has a non-trivial component in the Z
   // axis.
   bool HasNonTrivial3DComponent() const {
diff --git a/third_party/blink/renderer/platform/transforms/translate_transform_operation.h b/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
index ebb90a23..6ead517a 100644
--- a/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
@@ -99,6 +99,8 @@
     return ZoomTranslate(factor);
   }
 
+  bool PreservesAxisAlignment() const final { return true; }
+
   TranslateTransformOperation(const Length& tx,
                               const Length& ty,
                               double tz,
diff --git a/third_party/blink/renderer/platform/wtf/hash_traits.h b/third_party/blink/renderer/platform/wtf/hash_traits.h
index 0c75e386..ab32232 100644
--- a/third_party/blink/renderer/platform/wtf/hash_traits.h
+++ b/third_party/blink/renderer/platform/wtf/hash_traits.h
@@ -57,7 +57,10 @@
 
 // The starting table size. Can be overridden when we know beforehand that a
 // hash table will have at least N entries.
-#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  // The allocation pool for nodes is one big chunk that ASAN has no insight
+  // into, so it can cloak errors. Make it as small as possible to force nodes
+  // to be allocated individually where ASAN can see them.
   static const unsigned kMinimumTableSize = 1;
 #else
   static const unsigned kMinimumTableSize = 8;
diff --git a/third_party/blink/renderer/platform/wtf/list_hash_set.h b/third_party/blink/renderer/platform/wtf/list_hash_set.h
index 4c93a04..046d8390 100644
--- a/third_party/blink/renderer/platform/wtf/list_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/list_hash_set.h
@@ -386,7 +386,7 @@
 
   Node* free_list_;
   bool is_done_with_initial_free_list_;
-#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
   // The allocation pool for nodes is one big chunk that ASAN has no insight
   // into, so it can cloak errors. Make it as small as possible to force nodes
   // to be allocated individually where ASAN can see them.
diff --git a/third_party/blink/renderer/platform/wtf/time.cc b/third_party/blink/renderer/platform/wtf/time.cc
index 7ae6220..21b2e194 100644
--- a/third_party/blink/renderer/platform/wtf/time.cc
+++ b/third_party/blink/renderer/platform/wtf/time.cc
@@ -27,15 +27,3 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
-#include "third_party/blink/renderer/platform/wtf/time.h"
-
-#include "base/time/time.h"
-
-namespace WTF {
-
-double CurrentTime() {
-  return base::Time::Now().ToDoubleT();
-}
-
-}  // namespace WTF
diff --git a/third_party/blink/renderer/platform/wtf/time.h b/third_party/blink/renderer/platform/wtf/time.h
index 9c03b6c..a5075b7 100644
--- a/third_party/blink/renderer/platform/wtf/time.h
+++ b/third_party/blink/renderer/platform/wtf/time.h
@@ -5,24 +5,4 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
 
-#include "base/time/time.h"
-#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
-
-namespace WTF {
-
-// Returns the current UTC time in seconds, counted from January 1, 1970.
-// Precision varies depending on platform but is usually as good or better
-// than a millisecond.
-WTF_EXPORT double CurrentTime();
-
-// Same thing, in milliseconds.
-inline double CurrentTimeMS() {
-  return CurrentTime() * 1000.0;
-}
-
-}  // namespace WTF
-
-using WTF::CurrentTime;
-using WTF::CurrentTimeMS;
-
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index fbe2e3d7..dde7d59 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -49,13 +49,13 @@
 
 namespace WTF {
 
-#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+// The allocation pool for nodes is one big chunk that ASAN has no insight
+// into, so it can cloak errors. Make it as small as possible to force nodes
+// to be allocated individually where ASAN can see them.
 static const wtf_size_t kInitialVectorSize = 1;
 #else
-#ifndef WTF_VECTOR_INITIAL_SIZE
-#define WTF_VECTOR_INITIAL_SIZE 4
-#endif
-static const wtf_size_t kInitialVectorSize = WTF_VECTOR_INITIAL_SIZE;
+static const wtf_size_t kInitialVectorSize = 4;
 #endif
 
 template <typename T, wtf_size_t inlineBuffer, typename Allocator>
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 692f1bf..37b3463 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
@@ -82,6 +82,7 @@
             'base::size',
             'base::span',
             'logging::GetVlogLevel',
+            'util::PassKey',
 
             # //base/observer_list.h.
             'base::ObserverList',
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
index 00215128..617d074 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
@@ -297,3 +297,7 @@
 crbug.com/591099 virtual/gpu/fast/canvas/image-object-in-canvas.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ]
 
+# Just because these need a re-baseline from "LayoutNGBlockFlow" to "LayoutBlockFlow"
+crbug.com/591099 paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Failure ]
+crbug.com/591099 virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Failure ]
+crbug.com/591099 virtual/disable-blink-gen-property-trees/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Failure ]
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 5cda685..66c80dc 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -350,6 +350,8 @@
 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 ]
+crbug.com/931486 paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Failure ]
+crbug.com/931486 virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html [ Failure ]
 
 # See comment regarding this test in NeverFixTests. It also fails for other
 # reasons, in particular that the composited layerization algorithm provides
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index ac7879a..4e5380f 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2145,6 +2145,9 @@
 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/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html [ WontFix ]
+virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-event-is-history-forward-navigation-manual.https.html [ WontFix ]
+virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-event-is-reload-navigation-manual.https.html [ WontFix ]
 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html [ WontFix ]
 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/fetch-event-is-history-forward-navigation-manual.https.html [ WontFix ]
 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/fetch-event-is-reload-navigation-manual.https.html [ WontFix ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c36fa39..d0d13c9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -142,6 +142,7 @@
 # Subpixel differences
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ]
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ]
+crbug.com/986110 external/wpt/css/css-transforms/perspective-transforms-equivalence.html [ Failure ]
 
 crbug.com/807395 fast/multicol/mixed-opacity-test.html [ Failure ]
 
@@ -2219,7 +2220,6 @@
 ### sheriff 2019-07-16
 crbug.com/983799 [ Win ] http/tests/navigation/redirect-on-back-updates-history-item.html [ Timeout Pass ]
 crbug.com/983799 [ Win ] virtual/blink-cors/http/tests/navigation/redirect-on-back-updates-history-item.html [ Timeout Pass ]
-crbug.com/983790 external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Pass Failure Timeout ]
 crbug.com/983789 [ Win7 ] http/tests/cookies/js-set-null.html [ Pass Failure ]
 crbug.com/983788 http/tests/cookies/http-get-cookie-set-in-js.html [ Pass Failure ]
 crbug.com/983788 http/tests/cookies/same-site/popup-cross-site.html [ Pass Timeout ]
@@ -3123,6 +3123,7 @@
 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/not-omt-sw-fetch/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
+crbug.com/832071 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 crbug.com/832071 virtual/cache-storage-sequence/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
@@ -3541,6 +3542,7 @@
 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 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 virtual/cache-storage-sequence/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 ]
@@ -4372,6 +4374,7 @@
 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/648295 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/update-bytecheck.https.html [ Timeout ]
 crbug.com/648295 virtual/cache-storage-sequence/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 ]
@@ -4713,10 +4716,6 @@
 # Needs investigation. These are timing out.
 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 ]
-# These are timing out because filesystem: URL is not supported yet with
-# PlzDedicatedWorker.
-crbug.com/980451 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-script.html [ Skip ]
-crbug.com/980451 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-inheritance.html [ Skip ]
 
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
@@ -4780,6 +4779,7 @@
 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 ]
+crbug.com/691944 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 crbug.com/691944 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 
 # These tests (erroneously) see a platform-specific User-Agent header
@@ -4787,6 +4787,7 @@
 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/595993 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 crbug.com/595993 virtual/cache-storage-sequence/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 ]
@@ -5039,6 +5040,7 @@
 # Service worker updates need to handle redirect appropriately.
 crbug.com/889798 external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
 crbug.com/889798 virtual/blink-cors/external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
+crbug.com/889798 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
 crbug.com/889798 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
 crbug.com/889798 virtual/not-omt-sw-fetch/external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
 crbug.com/889798 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/import-scripts-redirect.https.html [ Skip ]
@@ -5594,6 +5596,8 @@
 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/873873 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
+crbug.com/873873 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]
 crbug.com/873873 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
 crbug.com/873873 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]
 
@@ -5943,6 +5947,7 @@
 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 virtual/cache-storage-parallel/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 virtual/cache-storage-sequence/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 ]
@@ -6320,6 +6325,9 @@
 crbug.com/982149 [ Win7 ] virtual/not-omt-sw-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/cross-origin/http-http/img-tag/no-redirect/generic.http.html [ Pass Timeout ]
 crbug.com/982149 [ Win7 ] virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/cross-origin/http-http/img-tag/no-redirect/generic.http.html [ Pass Timeout ]
 
+# WebRTC "onnegotiationneeded" firing too early causing us to miss the event.
+crbug.com/985797 external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html [ Timeout ]
+
 # Sheriff 2019-07-18
 crbug.com/985232 [ Win7 ] external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html [ Pass Failure ]
 crbug.com/985232 [ Win7 ] virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html [ Pass Failure ]
@@ -6334,7 +6342,6 @@
 crbug.com/986018 fast/scroll-snap/snaps-after-keyboard-scrolling-rtl.html [ Pass Failure ]
 
 # ecosystem-infra sheriff 2019-07-22
-crbug.com/986457 [ Linux ] http/tests/misc/object-image-error-with-onload.html [ Pass Failure ]
 crbug.com/986477 [ Win ] external/wpt/cookies/path/match.html [ Pass Timeout ]
 crbug.com/986477 [ Win ] virtual/samesite-by-default-cookies/external/wpt/cookies/path/match.html [ Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index dd618504..45b1de5e 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -526,6 +526,12 @@
              "--disable-smooth-scrolling"]
   },
   {
+    "prefix": "compositor_threaded_scrollbar_scrolling",
+    "base": "paint/invalidation/scroll/sticky",
+    "args": ["--enable-threaded-compositing",
+             "--enable-prefer-compositing-to-lcd-text"]
+  },
+  {
     "prefix": "main_thread_scrollbar_gestures",
     "base": "fast/scrolling/scrollbars",
     "args": ["--enable-features=ScrollbarInjectScrollGestures"]
@@ -1188,6 +1194,13 @@
     "args": ["--enable-blink-features=CSSVariables2AtProperty"]
   },
   {
+    "prefix": "cache-storage-parallel",
+    "base": "external/wpt/service-workers",
+    "args": ["--enable-features=CacheStorageParallelOps<CSPO",
+             "--force-fieldtrials=CSPO/G1",
+             "--force-fieldtrial-params=CSPO.G1:max_shared_ops/64"]
+  },
+  {
     "prefix": "cache-storage-sequence",
     "base": "external/wpt/service-workers",
     "args": ["--enable-features=CacheStorageSequence"]
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 a7196641..c43973d 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
@@ -31801,6 +31801,18 @@
      {}
     ]
    ],
+   "css/css-backgrounds/background-color-body-propagation-007.html": [
+    [
+     "css/css-backgrounds/background-color-body-propagation-007.html",
+     [
+      [
+       "/css/reference/blank.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-backgrounds/background-color-clip.html": [
     [
      "css/css-backgrounds/background-color-clip.html",
@@ -53075,6 +53087,18 @@
      {}
     ]
    ],
+   "css/css-lists/li-insert-child.html": [
+    [
+     "css/css-lists/li-insert-child.html",
+     [
+      [
+       "/css/css-lists/li-insert-child-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-lists/li-list-item-counter.html": [
     [
      "css/css-lists/li-list-item-counter.html",
@@ -58067,6 +58091,54 @@
      {}
     ]
    ],
+   "css/css-overflow/overflow-body-propagation-001.tentative.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-001.tentative.html",
+     [
+      [
+       "/css/css-overflow/reference/overflow-body-propagation-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-overflow/overflow-body-propagation-002.tentative.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-002.tentative.html",
+     [
+      [
+       "/css/reference/blank.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-overflow/overflow-body-propagation-003.tentative.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-003.tentative.html",
+     [
+      [
+       "/css/reference/blank.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-overflow/overflow-body-propagation-004.tentative.html": [
+    [
+     "css/css-overflow/overflow-body-propagation-004.tentative.html",
+     [
+      [
+       "/css/css-overflow/reference/overflow-body-no-propagation-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-overflow/webkit-line-clamp-001.html": [
     [
      "css/css-overflow/webkit-line-clamp-001.html",
@@ -138492,6 +138564,9 @@
    "css/css-lists/counter-set-002-ref.html": [
     []
    ],
+   "css/css-lists/li-insert-child-ref.html": [
+    []
+   ],
    "css/css-lists/li-list-item-counter-ref.html": [
     []
    ],
@@ -139461,6 +139536,12 @@
    "css/css-overflow/reference/input-scrollable-region-001-ref.html": [
     []
    ],
+   "css/css-overflow/reference/overflow-body-no-propagation-ref.html": [
+    []
+   ],
+   "css/css-overflow/reference/overflow-body-propagation-ref.html": [
+    []
+   ],
    "css/css-overflow/reference/webkit-line-clamp-001-ref.html": [
     []
    ],
@@ -140112,6 +140193,12 @@
    "css/css-shapes/OWNERS": [
     []
    ],
+   "css/css-shapes/parsing/shape-image-threshold-computed-expected.txt": [
+    []
+   ],
+   "css/css-shapes/parsing/shape-image-threshold-valid-expected.txt": [
+    []
+   ],
    "css/css-shapes/parsing/shape-outside-computed-expected.txt": [
     []
    ],
@@ -150729,6 +150816,9 @@
    "element-timing/META.yml": [
     []
    ],
+   "element-timing/idlharness.window-expected.txt": [
+    []
+   ],
    "element-timing/resources/TAOImage.py": [
     []
    ],
@@ -150738,6 +150828,9 @@
    "element-timing/resources/element-timing-helpers.js": [
     []
    ],
+   "element-timing/resources/iframe-stores-entry.html": [
+    []
+   ],
    "element-timing/resources/iframe-with-square-sends-entry.html": [
     []
    ],
@@ -160980,6 +161073,9 @@
    "interfaces/dom.idl": [
     []
    ],
+   "interfaces/element-timing.idl": [
+    []
+   ],
    "interfaces/encoding.idl": [
     []
    ],
@@ -161049,6 +161145,9 @@
    "interfaces/keyboard-map.idl": [
     []
    ],
+   "interfaces/largest-contentful-paint.idl": [
+    []
+   ],
    "interfaces/longtasks.idl": [
     []
    ],
@@ -161358,6 +161457,9 @@
    "kv-storage/secure-context/side-effects-expected.txt": [
     []
    ],
+   "largest-contentful-paint/resources/iframe-stores-entry.html": [
+    []
+   ],
    "layout-instability/resources/slow-image.py": [
     []
    ],
@@ -161676,6 +161778,9 @@
    "mathml/relations/text-and-math/use-typo-metrics-1-ref.html": [
     []
    ],
+   "mathml/support/feature-detection.js": [
+    []
+   ],
    "mathml/tools/axisheight.py": [
     []
    ],
@@ -173517,12 +173622,6 @@
    "web-nfc/OWNERS": [
     []
    ],
-   "web-nfc/idlharness.https.window-expected.txt": [
-    []
-   ],
-   "web-nfc/nfc_hw_disabled-manual.https-expected.txt": [
-    []
-   ],
    "web-nfc/nfc_push_ArrayBuffer-manual.https-expected.txt": [
     []
    ],
@@ -185091,6 +185190,48 @@
      {}
     ]
    ],
+   "IndexedDB/structured-clone.any.js": [
+    [
+     "IndexedDB/structured-clone.any.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "support-promises.js"
+       ],
+       [
+        "title",
+        "Indexed DB and Structured Serializing/Deserializing"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "IndexedDB/structured-clone.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "support-promises.js"
+       ],
+       [
+        "title",
+        "Indexed DB and Structured Serializing/Deserializing"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ]
+   ],
    "IndexedDB/transaction-abort-generator-revert.html": [
     [
      "IndexedDB/transaction-abort-generator-revert.html",
@@ -199463,9 +199604,9 @@
      {}
     ]
    ],
-   "css/css-backgrounds/parsing/border-image-source-computed.html": [
+   "css/css-backgrounds/parsing/border-image-source-computed.sub.html": [
     [
-     "css/css-backgrounds/parsing/border-image-source-computed.html",
+     "css/css-backgrounds/parsing/border-image-source-computed.sub.html",
      {}
     ]
    ],
@@ -203633,9 +203774,9 @@
      {}
     ]
    ],
-   "css/css-lists/parsing/list-style-computed.html": [
+   "css/css-lists/parsing/list-style-computed.sub.html": [
     [
-     "css/css-lists/parsing/list-style-computed.html",
+     "css/css-lists/parsing/list-style-computed.sub.html",
      {}
     ]
    ],
@@ -219142,6 +219283,29 @@
      {}
     ]
    ],
+   "element-timing/element-only-when-fully-active.html": [
+    [
+     "element-timing/element-only-when-fully-active.html",
+     {}
+    ]
+   ],
+   "element-timing/idlharness.window.js": [
+    [
+     "element-timing/idlharness.window.html",
+     {
+      "script_metadata": [
+       [
+        "script",
+        "/resources/WebIDLParser.js"
+       ],
+       [
+        "script",
+        "/resources/idlharness.js"
+       ]
+      ]
+     }
+    ]
+   ],
    "element-timing/image-TAO.sub.html": [
     [
      "element-timing/image-TAO.sub.html",
@@ -248570,6 +248734,12 @@
      {}
     ]
    ],
+   "intersection-observer/rtl-clipped-root.html": [
+    [
+     "intersection-observer/rtl-clipped-root.html",
+     {}
+    ]
+   ],
    "intersection-observer/same-document-no-root.html": [
     [
      "intersection-observer/same-document-no-root.html",
@@ -248938,12 +249108,24 @@
      {}
     ]
    ],
+   "largest-contentful-paint/element-only-when-fully-active.html": [
+    [
+     "largest-contentful-paint/element-only-when-fully-active.html",
+     {}
+    ]
+   ],
    "largest-contentful-paint/expanded-image.html": [
     [
      "largest-contentful-paint/expanded-image.html",
      {}
     ]
    ],
+   "largest-contentful-paint/idlharness.html": [
+    [
+     "largest-contentful-paint/idlharness.html",
+     {}
+    ]
+   ],
    "largest-contentful-paint/image-src-change.html": [
     [
      "largest-contentful-paint/image-src-change.html",
@@ -248956,6 +249138,12 @@
      {}
     ]
    ],
+   "largest-contentful-paint/observe-after-untrusted-scroll.html": [
+    [
+     "largest-contentful-paint/observe-after-untrusted-scroll.html",
+     {}
+    ]
+   ],
    "largest-contentful-paint/observe-image.html": [
     [
      "largest-contentful-paint/observe-image.html",
@@ -249306,6 +249494,18 @@
      {}
     ]
    ],
+   "mathml/presentation-markup/operators/embellished-operator-001.html": [
+    [
+     "mathml/presentation-markup/operators/embellished-operator-001.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/operators/embellished-operator-002.html": [
+    [
+     "mathml/presentation-markup/operators/embellished-operator-002.html",
+     {}
+    ]
+   ],
    "mathml/presentation-markup/operators/mo-axis-height-1.html": [
     [
      "mathml/presentation-markup/operators/mo-axis-height-1.html",
@@ -253372,19 +253572,6 @@
      }
     ]
    ],
-   "native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.js": [
-    [
-     "native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.html",
-     {
-      "script_metadata": [
-       [
-        "script",
-        "resources/test-helpers.js"
-       ]
-      ]
-     }
-    ]
-   ],
    "navigation-timing/buffered-flag.window.js": [
     [
      "navigation-timing/buffered-flag.window.html",
@@ -294297,6 +294484,12 @@
      {}
     ]
    ],
+   "webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html": [
+    [
+     "webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html",
+     {}
+    ]
+   ],
    "webrtc/RTCPeerConnection-restartIce.https.html": [
     [
      "webrtc/RTCPeerConnection-restartIce.https.html",
@@ -299276,6 +299469,12 @@
      {}
     ]
    ],
+   "webxr/exclusive_requestFrame_nolayer.https.html": [
+    [
+     "webxr/exclusive_requestFrame_nolayer.https.html",
+     {}
+    ]
+   ],
    "webxr/getInputPose_handedness.https.html": [
     [
      "webxr/getInputPose_handedness.https.html",
@@ -299305,6 +299504,18 @@
      }
     ]
    ],
+   "webxr/render_state_vertical_fov_immersive.https.html": [
+    [
+     "webxr/render_state_vertical_fov_immersive.https.html",
+     {}
+    ]
+   ],
+   "webxr/render_state_vertical_fov_inline.https.html": [
+    [
+     "webxr/render_state_vertical_fov_inline.https.html",
+     {}
+    ]
+   ],
    "webxr/webGLCanvasContext_create_xrcompatible.https.html": [
     [
      "webxr/webGLCanvasContext_create_xrcompatible.https.html",
@@ -299521,6 +299732,12 @@
      {}
     ]
    ],
+   "webxr/xrSession_requestAnimationFrame_timestamp.https.html": [
+    [
+     "webxr/xrSession_requestAnimationFrame_timestamp.https.html",
+     {}
+    ]
+   ],
    "webxr/xrSession_requestReferenceSpace.https.html": [
     [
      "webxr/xrSession_requestReferenceSpace.https.html",
@@ -299551,12 +299768,54 @@
      {}
     ]
    ],
+   "webxr/xrView_match.https.html": [
+    [
+     "webxr/xrView_match.https.html",
+     {}
+    ]
+   ],
+   "webxr/xrView_oneframeupdate.https.html": [
+    [
+     "webxr/xrView_oneframeupdate.https.html",
+     {}
+    ]
+   ],
+   "webxr/xrViewport_valid.https.html": [
+    [
+     "webxr/xrViewport_valid.https.html",
+     {}
+    ]
+   ],
+   "webxr/xrWebGLLayer_constructor.https.html": [
+    [
+     "webxr/xrWebGLLayer_constructor.https.html",
+     {}
+    ]
+   ],
    "webxr/xrWebGLLayer_framebuffer.https.html": [
     [
      "webxr/xrWebGLLayer_framebuffer.https.html",
      {}
     ]
    ],
+   "webxr/xrWebGLLayer_framebuffer_draw.https.html": [
+    [
+     "webxr/xrWebGLLayer_framebuffer_draw.https.html",
+     {}
+    ]
+   ],
+   "webxr/xrWebGLLayer_framebuffer_scale.https.html": [
+    [
+     "webxr/xrWebGLLayer_framebuffer_scale.https.html",
+     {}
+    ]
+   ],
+   "webxr/xrWebGLLayer_opaque_framebuffer.https.html": [
+    [
+     "webxr/xrWebGLLayer_opaque_framebuffer.https.html",
+     {}
+    ]
+   ],
    "webxr/xrWebGLLayer_viewports.https.html": [
     [
      "webxr/xrWebGLLayer_viewports.https.html",
@@ -317848,6 +318107,10 @@
    "cc905e56ecf83ea4cd4f1fe2ccfac425cdc5f9d0",
    "testharness"
   ],
+  "IndexedDB/structured-clone.any.js": [
+   "8078aaf796b843aaf9f23be5889febbe4c8fb960",
+   "testharness"
+  ],
   "IndexedDB/support-promises.js": [
    "8195be341e18860865c072bc78a1efa1f25f2d56",
    "support"
@@ -320809,7 +321072,7 @@
    "support"
   ],
   "common/security-features/tools/generate.py": [
-   "50c3a2c250c9d992377809cb731441fd6588263d",
+   "50f08a75ca220917cdf484906f43cc139e10e09f",
    "support"
   ],
   "common/security-features/tools/template/disclaimer.template": [
@@ -320829,7 +321092,7 @@
    "support"
   ],
   "common/security-features/tools/util.py": [
-   "77b89769ca27f606ae6adb080fbdc5ee9df8966d",
+   "1f6c86e6efcc3bb5dbde41ba484a026d55a5961e",
    "support"
   ],
   "common/sleep.py": [
@@ -342309,15 +342572,19 @@
    "reftest"
   ],
   "css/css-backgrounds/background-color-body-propagation-004.html": [
-   "c4b21959626ad4ddfe8edb5065c57369920ea772",
+   "ede419096ff1faedd91b4f712e238a76101ca081",
    "reftest"
   ],
   "css/css-backgrounds/background-color-body-propagation-005.html": [
-   "702b2b29ae3cb20358b3c125f5b0cd69a1477d0b",
+   "05c5421977e74259189bab32b85e35c94daadddb",
    "reftest"
   ],
   "css/css-backgrounds/background-color-body-propagation-006.html": [
-   "b575891382c8b9f21998996a48fc7581a0f9e115",
+   "4c58cb9dc212f9b02a94b5bc7121cb50bd70cb61",
+   "reftest"
+  ],
+  "css/css-backgrounds/background-color-body-propagation-007.html": [
+   "6deda8dcdb581e6f064ed522c036e5f1d72ea474",
    "reftest"
   ],
   "css/css-backgrounds/background-color-body-propagation-ref.html": [
@@ -344956,8 +345223,8 @@
    "671120b41b7e55bddd2c286dc7ce4807ce9c3665",
    "testharness"
   ],
-  "css/css-backgrounds/parsing/border-image-source-computed.html": [
-   "96e6bdf2f09ee50e8649e7a918debeaf06902e32",
+  "css/css-backgrounds/parsing/border-image-source-computed.sub.html": [
+   "20343882096f2ce3fe1f0dbfb6d2164279e8aade",
    "testharness"
   ],
   "css/css-backgrounds/parsing/border-image-source-invalid.html": [
@@ -364332,6 +364599,14 @@
    "0fae6e3f6036c46b8f658304787da5879913296f",
    "testharness"
   ],
+  "css/css-lists/li-insert-child-ref.html": [
+   "bbf2e66a0dfddd3f399eea4889fd867444b7eca4",
+   "support"
+  ],
+  "css/css-lists/li-insert-child.html": [
+   "d82bf1374aebc47740285a54a7532b6b7242377f",
+   "reftest"
+  ],
   "css/css-lists/li-list-item-counter-ref.html": [
    "96ca6c1122188015d6e40e01102bdb4fecfb0567",
    "support"
@@ -364456,8 +364731,8 @@
    "9627ce936ae570325b430a1ac673cd66ae7d4252",
    "reftest"
   ],
-  "css/css-lists/parsing/list-style-computed.html": [
-   "67bdc46f8c11ae085d5ee8449ee97ddba319a8b4",
+  "css/css-lists/parsing/list-style-computed.sub.html": [
+   "611fae5bf93636cd4d3402e91b1cd47148303617",
    "testharness"
   ],
   "css/css-lists/parsing/list-style-image-computed.sub-expected.txt": [
@@ -367872,6 +368147,22 @@
    "df1128316f6010f010e6d27c395d7751fa05d7b1",
    "testharness"
   ],
+  "css/css-overflow/overflow-body-propagation-001.tentative.html": [
+   "0998fe68e007d5a46ff11d5ff87fdbad12d1dfe2",
+   "reftest"
+  ],
+  "css/css-overflow/overflow-body-propagation-002.tentative.html": [
+   "5991dd52e8b6aec74f55780587f15c2b9399c968",
+   "reftest"
+  ],
+  "css/css-overflow/overflow-body-propagation-003.tentative.html": [
+   "a329a8dfe3e44c92368bf0496897f74ccec5a63a",
+   "reftest"
+  ],
+  "css/css-overflow/overflow-body-propagation-004.tentative.html": [
+   "2ed8d2687a33608e0e70884ad2ea59330d2b09e3",
+   "reftest"
+  ],
   "css/css-overflow/overflow-inline-transform-relative.html": [
    "4df7b6389432cc6c65bae3fe3b5b9c2f00ba7bde",
    "testharness"
@@ -367980,6 +368271,14 @@
    "d7125ee2ef3285d461b2172208e23d8d4cc27a64",
    "support"
   ],
+  "css/css-overflow/reference/overflow-body-no-propagation-ref.html": [
+   "9795d1f5861f0affaeb3a36b3644d17fd60e1f4d",
+   "support"
+  ],
+  "css/css-overflow/reference/overflow-body-propagation-ref.html": [
+   "340bda9de92352fe1ef6633610da31e867013299",
+   "support"
+  ],
   "css/css-overflow/reference/webkit-line-clamp-001-ref.html": [
    "ef28e01dac0223c9a2768d3fd1415fb9ef2f1c6b",
    "support"
@@ -369661,7 +369960,7 @@
    "support"
   ],
   "css/css-properties-values-api/typedom.html": [
-   "2fc6447f22b0078d57e6e54eed751323c322a090",
+   "b8e6f4aaf840c017ff7e49c14333c635c8483c50",
    "testharness"
   ],
   "css/css-properties-values-api/unit-cycles.html": [
@@ -371020,16 +371319,24 @@
    "490775dd8ce24721046f89234237d8f7c200623c",
    "testharness"
   ],
+  "css/css-shapes/parsing/shape-image-threshold-computed-expected.txt": [
+   "8a246fca5bbb8077ddf3a9a0cb9c989b9540eb59",
+   "support"
+  ],
   "css/css-shapes/parsing/shape-image-threshold-computed.html": [
-   "a90d23a6044af840cc8b4251e8825097f6a43646",
+   "81bc0ccb8453109ca99fed496ef607ee99e95035",
    "testharness"
   ],
   "css/css-shapes/parsing/shape-image-threshold-invalid.html": [
-   "6299e2ecaad8247affa1e54ace4aea276322c114",
+   "c0cac033fb6d10a00275fcce6bb5e27ed4514984",
    "testharness"
   ],
+  "css/css-shapes/parsing/shape-image-threshold-valid-expected.txt": [
+   "b6e14a08a5931acc9a4c701ec12c845edde1ba2a",
+   "support"
+  ],
   "css/css-shapes/parsing/shape-image-threshold-valid.html": [
-   "914abd02210d4db881017c9453d3bcb68fd8e9f2",
+   "4ed1fb9fbaf1bd4aed1cdb89753d4f15c6a72fd9",
    "testharness"
   ],
   "css/css-shapes/parsing/shape-margin-computed.html": [
@@ -412992,6 +413299,18 @@
    "82e7461b0417fb6f667ad266c08789b58f06c457",
    "testharness"
   ],
+  "element-timing/element-only-when-fully-active.html": [
+   "5608685a34219d831dd6c57743e829967d3ed247",
+   "testharness"
+  ],
+  "element-timing/idlharness.window-expected.txt": [
+   "97021720108cbc18cb4adea42959525e34f002c6",
+   "support"
+  ],
+  "element-timing/idlharness.window.js": [
+   "f0e5d06658e773fd5f9ebf85511636e74c92dc80",
+   "testharness"
+  ],
   "element-timing/image-TAO.sub.html": [
    "032ae6b12198b76107ab3dfcc2ab3b7f92b13c00",
    "testharness"
@@ -413100,6 +413419,10 @@
    "8933732616c116f1617c64f07182c9cbeb9f59fe",
    "support"
   ],
+  "element-timing/resources/iframe-stores-entry.html": [
+   "2fa24769729f705547aa526cf4eca16c483cad78",
+   "support"
+  ],
   "element-timing/resources/iframe-with-square-sends-entry.html": [
    "b8af505d32bc68d7f98b79bf2d2575778a49b26e",
    "support"
@@ -427537,7 +427860,7 @@
    "support"
   ],
   "html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html": [
-   "d42599661bf595d27de82db892b17a5ec400062d",
+   "e3e0778c97a2a04ded5a151d31b1dc4f82334187",
    "testharness"
   ],
   "html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html": [
@@ -440716,6 +441039,10 @@
    "1788faae5207bb7a21b23771bf2b0e978ea49a69",
    "support"
   ],
+  "interfaces/element-timing.idl": [
+   "dc3b886cfe5b6da608cc71fbc13c97e3d7695b76",
+   "support"
+  ],
   "interfaces/encoding.idl": [
    "b3086b8588cdfec129a4c038c074021cacff83c5",
    "support"
@@ -440808,6 +441135,10 @@
    "70aaaffd3cfb5781b77f92eedf57bd4205e308bc",
    "support"
   ],
+  "interfaces/largest-contentful-paint.idl": [
+   "f16a7ff812c96ab28f7d3b8d0f1b92d141e552a0",
+   "support"
+  ],
   "interfaces/longtasks.idl": [
    "333b842f86e5d41f6f1aab8827cf086356c5a782",
    "support"
@@ -440889,7 +441220,7 @@
    "support"
   ],
   "interfaces/performance-timeline.idl": [
-   "8ded59d8a269f83037a8845417c1275dd91ec538",
+   "d9362ea27516ac4e40caa7632d9aa1e2ed9edb11",
    "support"
   ],
   "interfaces/permissions.idl": [
@@ -441005,7 +441336,7 @@
    "support"
   ],
   "interfaces/user-timing.idl": [
-   "4130803ff9c45c15ef670813127657a98bd5631a",
+   "8b0f813ba8e6efc34b979ac7323afcbcac41e72b",
    "support"
   ],
   "interfaces/vibration.idl": [
@@ -441041,7 +441372,7 @@
    "support"
   ],
   "interfaces/web-share.idl": [
-   "e275aac96fe9b0daad6a35b21db15c725698d6d7",
+   "ae1f3f8580c0ff038ebe798bd66437ca3eb3fefc",
    "support"
   ],
   "interfaces/webaudio.idl": [
@@ -441252,6 +441583,10 @@
    "898454c4f385794d78ef4635612b6754cdd060af",
    "testharness"
   ],
+  "intersection-observer/rtl-clipped-root.html": [
+   "a30c6e38c5a02df96215cfea2e40be78a871cb20",
+   "testharness"
+  ],
   "intersection-observer/same-document-no-root.html": [
    "63e9f86d9c60533a2b4b193cf4e2860ef712ee4f",
    "testharness"
@@ -441540,10 +441875,18 @@
    "88775b861a6b98b1af0fb5163222a1025a6df02e",
    "testharness"
   ],
+  "largest-contentful-paint/element-only-when-fully-active.html": [
+   "41bdc8f8b984e61ab62f39734a1e19342697ef4e",
+   "testharness"
+  ],
   "largest-contentful-paint/expanded-image.html": [
    "766b61d013da2cefddb9e35398648d4fa2a2bded",
    "testharness"
   ],
+  "largest-contentful-paint/idlharness.html": [
+   "273fef80ce2d855075781512a9a8ab0a736af420",
+   "testharness"
+  ],
   "largest-contentful-paint/image-src-change.html": [
    "3e083625bdc1812a2c344b8eefaaa10d3b31e623",
    "testharness"
@@ -441552,6 +441895,10 @@
    "fb0eddb220ec660d529a43b112c576bf146a87e8",
    "testharness"
   ],
+  "largest-contentful-paint/observe-after-untrusted-scroll.html": [
+   "abe753fc14a33f88a398900632146ae3b8e8ee13",
+   "testharness"
+  ],
   "largest-contentful-paint/observe-image.html": [
    "16b3502eb340cbce179bf440c245cafafe4beea4",
    "testharness"
@@ -441564,6 +441911,10 @@
    "94406b20d62b417a690ce6a96d909bb29c791081",
    "testharness"
   ],
+  "largest-contentful-paint/resources/iframe-stores-entry.html": [
+   "cd600254805570deab8447ea843657d7f268b7c5",
+   "support"
+  ],
   "largest-contentful-paint/supported-lcp-type.html": [
    "25d4eaa0367f45440d286c6c1c14de4458465d7b",
    "testharness"
@@ -441917,7 +442268,7 @@
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-1.html": [
-   "6be38d5439da9181ee53464abcf22863a9d176ce",
+   "5ecb66ecf6aabec264d8452a1277eb7769dde3fd",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-bar-001-ref.html": [
@@ -442013,11 +442364,11 @@
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-1.html": [
-   "55404fa562c4dd304eb84702bee4578d02ae7925",
+   "b8bc405107bd643519fef9e0fb0dba1ba0a66762",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-2.html": [
-   "63ab97760fc03161b9b3c63d09819463131864ad",
+   "ce2d299f281b639c1e4026caa9354a8bc8c5298d",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-gap-001-ref.html": [
@@ -442077,11 +442428,19 @@
    "reftest"
   ],
   "mathml/presentation-markup/mrow/inferred-mrow-baseline.html": [
-   "672d90de93363331a418f33ecac8f2380bd5fb5f",
+   "1541b2d6ce17c90a52c15f3ce5461a5c7d42920f",
    "testharness"
   ],
   "mathml/presentation-markup/mrow/inferred-mrow-stretchy.html": [
-   "75587d076c2790608133b7a81558a42dc2581cff",
+   "ee40561a634c00eb9e29dd66d4a28f579a78af59",
+   "testharness"
+  ],
+  "mathml/presentation-markup/operators/embellished-operator-001.html": [
+   "78c5069b90cab40ec2703c6c01c5d8643b5f836d",
+   "testharness"
+  ],
+  "mathml/presentation-markup/operators/embellished-operator-002.html": [
+   "da343dd0bc35a79e0f309c38bbff34a69aa7df34",
    "testharness"
   ],
   "mathml/presentation-markup/operators/mo-axis-height-1.html": [
@@ -442153,55 +442512,55 @@
    "reftest"
   ],
   "mathml/presentation-markup/radicals/root-parameters-1.html": [
-   "5ad0b7315e02b9d893937a376b0545ff7bb0eada",
+   "d09d117b8ad5b52ea3c8778dcd6c4dd56a7b9270",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-1.html": [
-   "01a6b0e1ed1fed2d069ac2b8b3e3716ffeee08b7",
+   "e5321d6c4a3c3dafca68caacdb651fed5c778c9b",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-2.html": [
-   "2fd6963b6a7906aaeabd17a16a53a9ebbe56116f",
+   "1a5b80d34397962c2e7d3400c782bed8cd6f149b",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-3.html": [
-   "60df24a799027c0704be4622976ea8e2ae2a3d22",
+   "5494bb0c919cba3c02320d4f59481dc4eebed74f",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-4.html": [
-   "6886766097cecff350ddcf3ce81bb87e70b4ce28",
+   "00bd45413c85667a028394c3b3f983a3b11fed75",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-5.html": [
-   "2cc4e6d9554a1f6fde6c16e37fec2a39c441a0e8",
+   "566b0133ec68d12c69e6f70d8a9205e2336f8936",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-parameters-1.html": [
-   "7503ad166af33745701ab379cf51fd5cda8ce7dc",
+   "a5f21ec5458f0cce4b6517e39572d3e928585e20",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-parameters-2.html": [
-   "62aa7a9dc3be90a343dbcb4810e5cb479707be2e",
+   "0abf01838c8c8c05f1c6cb4bf72e9fea57bca8d9",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-1.html": [
-   "b17835528a0ed8d78d643ef9f24dd7822b62f559",
+   "5b718707de9ab316c6abb0ae7bf26667af191447",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-1.html": [
-   "d8a564a1159518195e889694aee8d0b167efec91",
+   "26a1b3964cc10ced8762314390840db94efdb970",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-2.html": [
-   "c10f77ee2c807b8a24566574f6ff6377736ae514",
+   "aed22235b8c192ee69237c63face745e7e35e1bd",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-3.html": [
-   "86562fd374bdb44983657954f5b554ae62db8984",
+   "6a1e51cdc4516ae1fdd2784c0e77682222773b2f",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-4.html": [
-   "f7fb389b59feb23276773a91b854fd6ab105fc15",
+   "e569c15a6993321051b2490f62c5b6ae39666b7a",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/mspace-children-ref.html": [
@@ -442225,23 +442584,23 @@
    "reftest"
   ],
   "mathml/presentation-markup/spaces/space-like-001.html": [
-   "d2db45657ad5f89c6b6fadfc443c203caf9558c9",
+   "61375bcd8e320be57e471815891c68aa01e76065",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/space-like-002.html": [
-   "78f4c927c276a60beaefb219d3d73114e7ae5cee",
+   "f5f4e5a84b248184bf33aa46ffacf35241209ce3",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/space-like-003.html": [
-   "8831657a3eb9dfa6ba7131534f6baee495a0ffb3",
+   "6d3d007744e7a346cee178187f39928407ac1511",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/space-like-004.html": [
-   "28a2871b1368a2fb3654b8f08d622512fc8dc61e",
+   "091a3ea0a628c9b18b4b262aef737d7a7c9dac0a",
    "testharness"
   ],
   "mathml/presentation-markup/tables/table-axis-height.html": [
-   "feb29077e8e34bcd3155f0661f4878bb93bdbf19",
+   "3eaf9c8fff483232eb6794b603f270bb86c47d3d",
    "testharness"
   ],
   "mathml/relations/css-styling/color-1-ref.html": [
@@ -442580,6 +442939,10 @@
    "4e92a846461b65d150cef9a9444de27abf40a26d",
    "reftest"
   ],
+  "mathml/support/feature-detection.js": [
+   "5beb78c2776153e586ff6ffa1265a00bceba3f67",
+   "support"
+  ],
   "mathml/tools/axisheight.py": [
    "43827e7031665bdd57ee54e208ea0f875a9a60ec",
    "support"
@@ -444297,7 +444660,7 @@
    "support"
   ],
   "mixed-content/generic/tools/generate.py": [
-   "fe4305c4eea53b1e0317e92303078f0b6875c772",
+   "1edc22908c0c815b875eba81e09f430599ea7494",
    "support"
   ],
   "mixed-content/generic/tools/regenerate": [
@@ -444305,7 +444668,7 @@
    "support"
   ],
   "mixed-content/generic/tools/spec_validator.py": [
-   "686579ece5797abfdb5441e080db85758fe1b220",
+   "fe24657e78a678b1c73382313e4d4b4e00fad5d0",
    "support"
   ],
   "mixed-content/imageset.https.sub.html": [
@@ -446508,10 +446871,6 @@
    "f2c2fc82866b83953e2783f8dc56ceaa0803b097",
    "testharness"
   ],
-  "native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.js": [
-   "1dbcb2b136220954c777b2cc3b0197039cf730be",
-   "testharness"
-  ],
   "native-file-system/README.md": [
    "6905a68e7901ce26bc1a363062304e1536604400",
    "support"
@@ -456217,7 +456576,7 @@
    "support"
   ],
   "referrer-policy/generic/tools/generate.py": [
-   "b3e3340d97b346c3f2aa787dd55635dffa63f929",
+   "71d5ad37de134e9b07b53b8ed08244e0afd98dc9",
    "support"
   ],
   "referrer-policy/generic/tools/regenerate": [
@@ -456225,7 +456584,7 @@
    "support"
   ],
   "referrer-policy/generic/tools/spec_validator.py": [
-   "70656db25d2ce4f09c3fbb8a90294cb7fa20ba97",
+   "569afc305f43ed9fc9f939769e4c8313276e5cb8",
    "support"
   ],
   "referrer-policy/generic/unsupported-csp-referrer-directive.html": [
@@ -471137,7 +471496,7 @@
    "testharness"
   ],
   "std-toast/options.html": [
-   "b75ff72cb9e86857e249a50f1ccb0713ad5c9144",
+   "9f53bf88601ce914b1acf4054121d9420370fffa",
    "testharness"
   ],
   "std-toast/ref-tests/toast-slotting-expected.html": [
@@ -474489,11 +474848,11 @@
    "support"
   ],
   "tools/requirements_flake8.txt": [
-   "480aacd466257f82068f5c133ec90e59a2b1eb4f",
+   "d73e7c3dbbe2f0cf20637e639c449b0fa4538caf",
    "support"
   ],
   "tools/requirements_mypy.txt": [
-   "8f1d67cd21230939a2ff4fe09f7e3960b58ca560",
+   "c1bf4bc088e7003d7ea2b362a28048e8952d2031",
    "support"
   ],
   "tools/runner/css/bootstrap-theme.min.css": [
@@ -478337,7 +478696,7 @@
    "support"
   ],
   "tools/tox.ini": [
-   "ba6dc038a172895f0bda838c6456a5db95a75952",
+   "63fb8ed9832ba9d11dae965e3c335e3d26289720",
    "support"
   ],
   "tools/webdriver/README.md": [
@@ -478401,7 +478760,7 @@
    "support"
   ],
   "tools/wpt/run.py": [
-   "6acc5b8e7400f5726538657239de5710ecea396d",
+   "d5d7e7a21f5f3b195ce5b93141d6798bdfeb5e2d",
    "support"
   ],
   "tools/wpt/testfiles.py": [
@@ -478425,7 +478784,7 @@
    "support"
   ],
   "tools/wpt/wpt.py": [
-   "4130e1eecf8ae52dbc0b68f6cd59010dd4e435e4",
+   "93301dd86b4ffe202902a492204eba6537fb50d5",
    "support"
   ],
   "tools/wptrunner/LICENSE": [
@@ -478441,7 +478800,7 @@
    "support"
   ],
   "tools/wptrunner/requirements.txt": [
-   "e25eec38283e0825a7a402912376c3a396d6de70",
+   "d6c7a4ff3983663d088c64f6608e3fcf7de3bbfe",
    "support"
   ],
   "tools/wptrunner/requirements_chrome.txt": [
@@ -478589,7 +478948,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/environment.py": [
-   "2493f1fa4407a39aad3ac3c2a724322b75b0944a",
+   "924409216411050b038b61c7402eab687b122429",
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/__init__.py": [
@@ -478797,7 +479156,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/update/update.py": [
-   "a0f42377734b1963d7df1ce1dcb3472be4b7781c",
+   "0bcaf823b72c30e346a64f3c0344bc2275df64f5",
    "support"
   ],
   "tools/wptrunner/wptrunner/vcs.py": [
@@ -479265,11 +479624,11 @@
    "support"
   ],
   "trusted-types/trusted-types-reporting.tentative.https.html": [
-   "3074895ba13f7f734d00898e723b4b604234a9cb",
+   "8dda8b23c8f06bfa375b6c6de874c7de0fbfc3c3",
    "testharness"
   ],
   "trusted-types/trusted-types-reporting.tentative.https.html.headers": [
-   "8093b8474d686fd665c6e835c6d0ed8f337cf2cd",
+   "947a151c874ce9d372baa786c1b2964cbd9bc279",
    "support"
   ],
   "uievents/META.yml": [
@@ -481529,7 +481888,7 @@
    "testharness"
   ],
   "web-nfc/NFCReader_options_mediaType-manual.https-expected.txt": [
-   "129e4ce74bcb9d8296dd6d2223ae06d4acec807a",
+   "091fef83617159b165adb6cc6d434d2a33352013",
    "support"
   ],
   "web-nfc/NFCReader_options_mediaType-manual.https.html": [
@@ -481537,7 +481896,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_recordType_empty-manual.https-expected.txt": [
-   "438f4b6793f044b90d7614a099a29eb243096c7c",
+   "ec071f4f19f9b96ab44e47457bc96abfe8b0fe8a",
    "support"
   ],
   "web-nfc/NFCReader_options_recordType_empty-manual.https.html": [
@@ -481545,7 +481904,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_recordType_json-manual.https-expected.txt": [
-   "a8f75b34c373b47bf1ac65de29bf802ffcab0012",
+   "f0d8fa8ec368df1fb4c04626a89c36e99f88c082",
    "support"
   ],
   "web-nfc/NFCReader_options_recordType_json-manual.https.html": [
@@ -481553,7 +481912,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_recordType_opaque-manual.https-expected.txt": [
-   "bd09f8bd971319c6a39dde64c993c4f21339d269",
+   "9020bf9b4f1334e36c9ee86ca2a82d1f51db28cc",
    "support"
   ],
   "web-nfc/NFCReader_options_recordType_opaque-manual.https.html": [
@@ -481561,7 +481920,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_recordType_text-manual.https-expected.txt": [
-   "b2b18c5455fef22f83b55bf9657210fcd214a0ad",
+   "57045f0e2992c42b526c2b411a98c7cca90e1fb9",
    "support"
   ],
   "web-nfc/NFCReader_options_recordType_text-manual.https.html": [
@@ -481569,7 +481928,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_recordType_url-manual.https-expected.txt": [
-   "f53c2e72e5af9901a5cd3d896fe92c99b5622918",
+   "8ef29659d814e7e54d8a4777c86ad0f62b370e5e",
    "support"
   ],
   "web-nfc/NFCReader_options_recordType_url-manual.https.html": [
@@ -481577,7 +481936,7 @@
    "manual"
   ],
   "web-nfc/NFCReader_options_url-manual.https-expected.txt": [
-   "fdf8c72499da615f36431e0f5b1e3bfa869ea3df",
+   "5a8462e66cc208a5727a0950783a87b0c8f7630c",
    "support"
   ],
   "web-nfc/NFCReader_options_url-manual.https.html": [
@@ -481597,7 +481956,7 @@
    "testharness"
   ],
   "web-nfc/NFCWriter_push_signal-manual.https-expected.txt": [
-   "27dd82df88a4bb43fc02733183e89d6ce57b337d",
+   "ff64f6129d67cb99b1eb5c505e1a91b03ab96169",
    "support"
   ],
   "web-nfc/NFCWriter_push_signal-manual.https.html": [
@@ -481608,18 +481967,10 @@
    "2a0e82019549591b88878ca70fc6baf616d34c29",
    "support"
   ],
-  "web-nfc/idlharness.https.window-expected.txt": [
-   "5e3a1003a9a46e8b9df411a370cda6985f7ed558",
-   "support"
-  ],
   "web-nfc/idlharness.https.window.js": [
    "c19458aed83f505472e35c8f3affa3247a814989",
    "testharness"
   ],
-  "web-nfc/nfc_hw_disabled-manual.https-expected.txt": [
-   "fd955dd5886bc597b07ff0d05dd4f70ba106c160",
-   "support"
-  ],
   "web-nfc/nfc_hw_disabled-manual.https.html": [
    "bb2cd42f1e65cca49b03385e9e553cc8077aa08d",
    "manual"
@@ -481629,7 +481980,7 @@
    "testharness"
   ],
   "web-nfc/nfc_push_ArrayBuffer-manual.https-expected.txt": [
-   "9f00a673b4f983c6c0a1d1222dd382108836b6a6",
+   "0377296be29411c97f8115f591c08a369055ab0a",
    "support"
   ],
   "web-nfc/nfc_push_ArrayBuffer-manual.https.html": [
@@ -481637,7 +481988,7 @@
    "manual"
   ],
   "web-nfc/nfc_push_DOMString-manual.https-expected.txt": [
-   "29791a39dd73e276d92febd8ea82f467e73b6a64",
+   "c4e37077e0e058cb2e18575190701ae766852b0f",
    "support"
   ],
   "web-nfc/nfc_push_DOMString-manual.https.html": [
@@ -484668,12 +485019,16 @@
    "f2add7f0d34bada5c156cafeac399e104b2a414a",
    "testharness"
   ],
+  "webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html": [
+   "4dcce451995597ea072c7e491f0cef908014cf0c",
+   "testharness"
+  ],
   "webrtc/RTCPeerConnection-restartIce.https-expected.txt": [
-   "0ec3508566898d37d8c97c1fb26053ac58b0f290",
+   "2c0c15ceca3a4e960d4230bda6bb86849256f103",
    "support"
   ],
   "webrtc/RTCPeerConnection-restartIce.https.html": [
-   "6b66975fdad2b5b11159baf921bdcae93d6deb23",
+   "c069736fa0b37be2bff7174e5ea65aaf91fb5b61",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt": [
@@ -484989,7 +485344,7 @@
    "testharness"
   ],
   "webrtc/idlharness.https.window-expected.txt": [
-   "1f26f0d506c51105c2e769caa4270ebc6d3e472a",
+   "2671b880c6f8d24e05efab15bf321b7064419ca3",
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
@@ -489732,6 +490087,10 @@
    "0e2c23424c0893b6a563c2aa2fbd999e50b1cfef",
    "testharness"
   ],
+  "webxr/exclusive_requestFrame_nolayer.https.html": [
+   "d31b927631f91128933c1badd56747315c767780",
+   "testharness"
+  ],
   "webxr/getInputPose_handedness.https.html": [
    "5a310c6dd77274631d0ef9e751c6f195def9c3d3",
    "testharness"
@@ -489748,6 +490107,14 @@
    "3e54e367787cb95dada398790fe23b10174df29f",
    "testharness"
   ],
+  "webxr/render_state_vertical_fov_immersive.https.html": [
+   "485438cabf6a633df42a0f94c04922e7274013e1",
+   "testharness"
+  ],
+  "webxr/render_state_vertical_fov_inline.https.html": [
+   "8bf384f4f150b0b2e841b33d67e290011694c0f4",
+   "testharness"
+  ],
   "webxr/resources/webxr_check.html": [
    "2d8e5b387dc88588921ccfa49dd14db58009900c",
    "support"
@@ -489916,6 +490283,10 @@
    "bdb5edda566704c4d1434b328d3adbe46d3501ba",
    "testharness"
   ],
+  "webxr/xrSession_requestAnimationFrame_timestamp.https.html": [
+   "daa786b368627dfc60663470e9e339f97eff729c",
+   "testharness"
+  ],
   "webxr/xrSession_requestReferenceSpace.https.html": [
    "51aa8885f5e853ca4bf225134dee120b71d28011",
    "testharness"
@@ -489936,6 +490307,22 @@
    "956edba98365d90eb180ad3c9697dae098bd25db",
    "testharness"
   ],
+  "webxr/xrView_match.https.html": [
+   "142f272d36f812c49455a78bfc62f285f9e64818",
+   "testharness"
+  ],
+  "webxr/xrView_oneframeupdate.https.html": [
+   "9404fcb8aaf33876f2918d3796190f16feafe506",
+   "testharness"
+  ],
+  "webxr/xrViewport_valid.https.html": [
+   "1b7d982d596187f1381bee73eac2990900a37f8f",
+   "testharness"
+  ],
+  "webxr/xrWebGLLayer_constructor.https.html": [
+   "74f0e7611e5e201a275b1a444a6d4b367af3e53d",
+   "testharness"
+  ],
   "webxr/xrWebGLLayer_framebuffer.https-expected.txt": [
    "54e5aa445b27baa62ee3c94a64c96b942edfdbb6",
    "support"
@@ -489944,6 +490331,18 @@
    "ba6b7dc0b922ae45aa714de8d3ca6f4ffbaf414c",
    "testharness"
   ],
+  "webxr/xrWebGLLayer_framebuffer_draw.https.html": [
+   "dd40865e445aafce88f5941f4127236c36f01f2d",
+   "testharness"
+  ],
+  "webxr/xrWebGLLayer_framebuffer_scale.https.html": [
+   "7b5cedb1c83987daf435f662e5687cf6e1bb0559",
+   "testharness"
+  ],
+  "webxr/xrWebGLLayer_opaque_framebuffer.https.html": [
+   "ceff6251d565726ddb56798da7bbbef649d96d2f",
+   "testharness"
+  ],
   "webxr/xrWebGLLayer_viewports.https.html": [
    "94a23dcdb427298722a989987a02f35a8d98d055",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/format_spec_src_json.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/format_spec_src_json.py
new file mode 100644
index 0000000..1276c78
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/format_spec_src_json.py
@@ -0,0 +1,20 @@
+import collections
+import json
+import os
+
+
+def main():
+    '''Formats spec.src.json.'''
+    script_directory = os.path.dirname(os.path.abspath(__file__))
+    for dir in ['mixed-content', 'referrer-policy']:
+        filename = os.path.join(script_directory, '..', '..', '..', dir,
+                                'spec.src.json')
+        spec = json.load(
+            open(filename, 'r'), object_pairs_hook=collections.OrderedDict)
+        with open(filename, 'w') as f:
+            f.write(json.dumps(spec, indent=2, separators=(',', ': ')))
+            f.write('\n')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
index 50c3a2c..fc10e63 100644
--- a/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/generate.py
@@ -28,7 +28,10 @@
     return expansion
 
 
-def permute_expansion(expansion, artifact_order, selection = {}, artifact_index = 0):
+def permute_expansion(expansion,
+                      artifact_order,
+                      selection={},
+                      artifact_index=0):
     assert isinstance(artifact_order, list), "artifact_order should be a list"
 
     if artifact_index >= len(artifact_order):
@@ -39,20 +42,23 @@
 
     for artifact_value in expansion[artifact_key]:
         selection[artifact_key] = artifact_value
-        for next_selection in permute_expansion(expansion,
-                                                artifact_order,
-                                                selection,
-                                                artifact_index + 1):
+        for next_selection in permute_expansion(expansion, artifact_order,
+                                                selection, artifact_index + 1):
             yield next_selection
 
 
-def generate_selection(config, selection, spec, test_html_template_basename):
-    # TODO: Refactor out this referrer-policy-specific part.
-    if 'referrer_policy' in spec:
-      # Oddball: it can be None, so in JS it's null.
-      selection['referrer_policy'] = spec['referrer_policy']
+# Dumps the test config `selection` into a serialized JSON string.
+# We omit `name` parameter because it is not used by tests.
+def dump_test_parameters(selection):
+    selection = dict(selection)
+    del selection['name']
 
-    test_parameters = json.dumps(selection, indent=2, separators=(',', ':'))
+    return json.dumps(
+        selection, indent=2, separators=(',', ': '), sort_keys=True)
+
+
+def generate_selection(config, selection, spec, test_html_template_basename):
+    test_parameters = dump_test_parameters(selection)
     # Adjust the template for the test invoking JS. Indent it to look nice.
     indent = "\n" + " " * 8
     test_parameters = test_parameters.replace("\n", indent)
@@ -66,14 +72,16 @@
       ''' % (config.test_case_name, test_parameters)
 
     selection['spec_name'] = spec['name']
-    selection['test_page_title'] = config.test_page_title_template % spec['title']
+    selection[
+        'test_page_title'] = config.test_page_title_template % spec['title']
     selection['spec_description'] = spec['description']
     selection['spec_specification_url'] = spec['specification_url']
     selection['helper_js'] = config.helper_js
     selection['sanity_checker_js'] = config.sanity_checker_js
     selection['spec_json_js'] = config.spec_json_js
 
-    test_filename = os.path.join(config.spec_directory, config.test_file_path_pattern % selection)
+    test_filename = os.path.join(config.spec_directory,
+                                 config.test_file_path_pattern % selection)
     test_headers_filename = test_filename + ".headers"
     test_directory = os.path.dirname(test_filename)
 
@@ -83,14 +91,15 @@
     html_template_filename = os.path.join(util.template_directory,
                                           test_html_template_basename)
     generated_disclaimer = disclaimer_template \
-        % {'generating_script_filename': os.path.relpath(__file__,
+        % {'generating_script_filename': os.path.relpath(sys.argv[0],
                                                          util.test_root_directory),
            'html_template_filename': os.path.relpath(html_template_filename,
                                                      util.test_root_directory)}
 
     # Adjust the template for the test invoking JS. Indent it to look nice.
     selection['generated_disclaimer'] = generated_disclaimer.rstrip()
-    selection['test_description'] = config.test_description_template % selection
+    selection[
+        'test_description'] = config.test_description_template % selection
     selection['test_description'] = \
         selection['test_description'].rstrip().replace("\n", "\n" + " " * 33)
 
@@ -123,9 +132,11 @@
     specification = spec_json['specification']
 
     spec_json_js_template = util.get_template('spec_json.js.template')
-    generated_spec_json_filename = os.path.join(config.spec_directory, "spec_json.js")
-    util.write_file(generated_spec_json_filename,
-               spec_json_js_template % {'spec_json': json.dumps(spec_json)})
+    generated_spec_json_filename = os.path.join(config.spec_directory,
+                                                "spec_json.js")
+    util.write_file(
+        generated_spec_json_filename,
+        spec_json_js_template % {'spec_json': json.dumps(spec_json)})
 
     # Choose a debug/release template depending on the target.
     html_template = "test.%s.html.template" % target
@@ -149,13 +160,18 @@
         output_dict = {}
 
         for expansion_pattern in spec['test_expansion']:
-            expansion = expand_pattern(expansion_pattern, test_expansion_schema)
+            expansion = expand_pattern(expansion_pattern,
+                                       test_expansion_schema)
             for selection in permute_expansion(expansion, artifact_order):
+                selection['delivery_key'] = spec_json['delivery_key']
                 selection_path = config.selection_pattern % selection
                 if not selection_path in exclusion_dict:
                     if selection_path in output_dict:
                         if expansion_pattern['expansion'] != 'override':
-                            print("Error: %s's expansion is default but overrides %s" % (selection['name'], output_dict[selection_path]['name']))
+                            print(
+                                "Error: %s's expansion is default but overrides %s"
+                                % (selection['name'],
+                                   output_dict[selection_path]['name']))
                             sys.exit(1)
                     output_dict[selection_path] = copy.deepcopy(selection)
                 else:
@@ -163,24 +179,30 @@
 
         for selection_path in output_dict:
             selection = output_dict[selection_path]
-            generate_selection(config,
-                               selection,
-                               spec,
-                               html_template)
+            generate_selection(config, selection, spec, html_template)
 
 
 def main(config):
-    parser = argparse.ArgumentParser(description='Test suite generator utility')
-    parser.add_argument('-t', '--target', type = str,
-        choices = ("release", "debug"), default = "release",
-        help = 'Sets the appropriate template for generating tests')
-    parser.add_argument('-s', '--spec', type = str, default = None,
-        help = 'Specify a file used for describing and generating the tests')
+    parser = argparse.ArgumentParser(
+        description='Test suite generator utility')
+    parser.add_argument(
+        '-t',
+        '--target',
+        type=str,
+        choices=("release", "debug"),
+        default="release",
+        help='Sets the appropriate template for generating tests')
+    parser.add_argument(
+        '-s',
+        '--spec',
+        type=str,
+        default=None,
+        help='Specify a file used for describing and generating the tests')
     # TODO(kristijanburnik): Add option for the spec_json file.
     args = parser.parse_args()
 
     if args.spec:
-      config.spec_directory = args.spec
+        config.spec_directory = args.spec
 
     spec_filename = os.path.join(config.spec_directory, "spec.src.json")
     spec_json = util.load_spec_json(spec_filename)
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/spec_validator.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
similarity index 77%
rename from third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/spec_validator.py
rename to third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
index 686579ec..2c966478 100755
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/spec_validator.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/spec_validator.py
@@ -3,7 +3,7 @@
 from __future__ import print_function
 
 import json, sys
-from common_paths import *
+
 
 def assert_non_empty_string(obj, field):
     assert field in obj, 'Missing field "%s"' % field
@@ -31,12 +31,13 @@
 
 
 def assert_value_from(obj, field, items):
-   assert obj[field] in items, \
-        'Field "%s" must be from: %s' % (field, str(items))
+    assert obj[field] in items, \
+         'Field "%s" must be from: %s' % (field, str(items))
 
 
 def assert_atom_or_list_items_from(obj, field, items):
-    if isinstance(obj[field], basestring) or isinstance(obj[field], int):
+    if isinstance(obj[field], basestring) or isinstance(
+            obj[field], int) or obj[field] is None:
         assert_value_from(obj, field, items)
         return
 
@@ -71,13 +72,15 @@
         assert_valid_artifact(exp_pattern[artifact_key], sub_artifact_key,
                               sub_schema)
 
+
 def validate(spec_json, details):
     """ Validates the json specification for generating tests. """
 
     details['object'] = spec_json
-    assert_contains_only_fields(spec_json, ["specification",
-                                            "test_expansion_schema",
-                                            "excluded_tests"])
+    assert_contains_only_fields(spec_json, [
+        "specification", "delivery_key", "test_expansion_schema",
+        "excluded_tests"
+    ])
     assert_non_empty_list(spec_json, "specification")
     assert_non_empty_dict(spec_json, "test_expansion_schema")
     assert_non_empty_list(spec_json, "excluded_tests")
@@ -93,11 +96,10 @@
         details['object'] = spec
 
         # Validate required fields for a single spec.
-        assert_contains_only_fields(spec, ['name',
-                                           'title',
-                                           'description',
-                                           'specification_url',
-                                           'test_expansion'])
+        assert_contains_only_fields(spec, [
+            'name', 'title', 'description', 'specification_url',
+            'test_expansion'
+        ])
         assert_non_empty_string(spec, 'name')
         assert_non_empty_string(spec, 'title')
         assert_non_empty_string(spec, 'description')
@@ -123,14 +125,10 @@
 
     # Validate the test_expansion schema members.
     details['object'] = test_expansion_schema
-    assert_contains_only_fields(test_expansion_schema, ['expansion',
-                                                        'source_scheme',
-                                                        'opt_in_method',
-                                                        'context_nesting',
-                                                        'redirection',
-                                                        'subresource',
-                                                        'origin',
-                                                        'expectation'])
+    assert_contains_only_fields(test_expansion_schema, [
+        'expansion', 'source_scheme', 'delivery_type', 'delivery_value',
+        'redirection', 'subresource', 'origin', 'expectation'
+    ])
     # Validate excluded tests.
     details['object'] = excluded_tests
     for excluded_test_expansion in excluded_tests:
@@ -139,10 +137,8 @@
         details['object'] = excluded_test_expansion
         for artifact in test_expansion_schema:
             details['test_expansion_field'] = artifact
-            assert_valid_artifact(
-                excluded_test_expansion,
-                artifact,
-                test_expansion_schema[artifact])
+            assert_valid_artifact(excluded_test_expansion, artifact,
+                                  test_expansion_schema[artifact])
             del details['test_expansion_field']
 
     del details['object']
@@ -159,7 +155,7 @@
 
 
 def main():
-    spec_json = load_spec_json();
+    spec_json = load_spec_json()
     assert_valid_spec_json(spec_json)
     print("Spec JSON is valid.")
 
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py b/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
index 77b89769ca..1f6c86e 100644
--- a/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/tools/util.py
@@ -3,10 +3,10 @@
 import os, sys, json, re
 
 script_directory = os.path.dirname(os.path.abspath(__file__))
-template_directory = os.path.abspath(os.path.join(script_directory,
-                                                  'template'))
-test_root_directory = os.path.abspath(os.path.join(script_directory,
-                                                   '..', '..', '..'))
+template_directory = os.path.abspath(
+    os.path.join(script_directory, 'template'))
+test_root_directory = os.path.abspath(
+    os.path.join(script_directory, '..', '..', '..'))
 
 
 def get_template(basename):
@@ -20,22 +20,22 @@
 
 
 def read_nth_line(fp, line_number):
-  fp.seek(0)
-  for i, line in enumerate(fp):
-    if (i + 1) == line_number:
-      return line
+    fp.seek(0)
+    for i, line in enumerate(fp):
+        if (i + 1) == line_number:
+            return line
 
 
 def load_spec_json(path_to_spec):
     re_error_location = re.compile('line ([0-9]+) column ([0-9]+)')
     with open(path_to_spec, "r") as f:
         try:
-          return json.load(f)
+            return json.load(f)
         except ValueError as ex:
-          print(ex.message)
-          match = re_error_location.search(ex.message)
-          if match:
-            line_number, column = int(match.group(1)), int(match.group(2))
-            print(read_nth_line(f, line_number).rstrip())
-            print(" " * (column - 1) + "^")
-          sys.exit(1)
+            print(ex.message)
+            match = re_error_location.search(ex.message)
+            if match:
+                line_number, column = int(match.group(1)), int(match.group(2))
+                print(read_nth_line(f, line_number).rstrip())
+                print(" " * (column - 1) + "^")
+            sys.exit(1)
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_insecure.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_insecure.tentative.https.window.js
new file mode 100644
index 0000000..bb1907f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_insecure.tentative.https.window.js
@@ -0,0 +1,19 @@
+'use strict';
+
+promise_test(async t => {
+  await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+
+  await cookieStore.delete('cookie-name');
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie, null);
+}, 'cookieStore.delete(name) can delete an insecure cookie');
+
+promise_test(async t => {
+  await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+
+  await cookieStore.delete({ name: 'cookie-name' });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie, null);
+}, 'cookieStore.delete(options) can delete an insecure cookie');
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-004.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-004.html
index c4b2195..ede41909 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-004.html
@@ -8,3 +8,4 @@
     display: none
   }
 </style>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-005.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-005.html
index 702b2b2..05c5421 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-005.html
@@ -6,3 +6,4 @@
   html { display: none; }
   body { background: red; }
 </style>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-006.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-006.html
index b575891..4c58cb9d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-006.html
@@ -14,3 +14,4 @@
     }));
   };
 </script>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-007.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-007.html
new file mode 100644
index 0000000..6deda8d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-color-body-propagation-007.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>CSS Backgrounds and Borders Test: don't propagate body background when display:contents</title>
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#special-backgrounds">
+<link rel="match" href="../reference/blank.html">
+<style>
+  body {
+    background: red;
+    display: contents
+  }
+</style>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.sub.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.sub.html
index 96e6bdf..20343882 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/parsing/border-image-source-computed.sub.html
@@ -13,7 +13,7 @@
 <div id="target"></div>
 <script>
 test_computed_value("border-image-source", "none");
-test_computed_value("border-image-source", 'url("http://www.example.com/")');
+test_computed_value("border-image-source", 'url("http://{{host}}/")');
 
 test(() => {
   const target = document.getElementById('target');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child-ref.html
new file mode 100644
index 0000000..bbf2e66a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child-ref.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: Insert text node as first child in LI</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+  <style>
+    ul.inside { list-style-position: inside; }
+    .before::before { content: "before"; }
+    .after::after { content: "after"; }
+  </style>
+</head>
+<body>
+
+<ul class="inside">
+  <li>AB</li>
+</ul>
+
+<ul class="inside">
+  <li class="before">AB</li>
+</ul>
+
+<ul class="inside">
+  <li class="after">AB</li>
+</ul>
+
+<ul class="inside">
+  <li class="before after">AB</li>
+</ul>
+
+<ul>
+  <li>AB</li>
+</ul>
+
+<ul>
+  <li class="before">AB</li>
+</ul>
+
+<ul>
+  <li class="after">AB</li>
+</ul>
+
+<ul>
+  <li class="before after">AB</li>
+</ul>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child.html b/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child.html
new file mode 100644
index 0000000..d82bf13
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/li-insert-child.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html class="reftest-wait"><head>
+  <meta charset="utf-8">
+  <title>Test: Insert text node as first child in LI</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+  <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1567773">
+  <link rel="match" href="li-insert-child-ref.html">
+  <style>
+    ul.inside { list-style-position: inside; }
+    .before::before { content: "before"; }
+    .after::after { content: "after"; }
+  </style>
+</head>
+<body>
+
+<ul class="inside">
+  <li>B</li>
+</ul>
+
+<ul class="inside">
+  <li class="before">B</li>
+</ul>
+
+<ul class="inside">
+  <li class="after">B</li>
+</ul>
+
+<ul class="inside">
+  <li class="before after">B</li>
+</ul>
+
+<ul>
+  <li>B</li>
+</ul>
+
+<ul>
+  <li class="before">B</li>
+</ul>
+
+<ul>
+  <li class="after">B</li>
+</ul>
+
+<ul>
+  <li class="before after">B</li>
+</ul>
+
+<script>
+document.body.offsetHeight;
+let items = Array.prototype.slice.call(document.querySelectorAll('li'));
+items.map(li => li.insertBefore(document.createTextNode('A'), li.firstChild));
+document.documentElement.className = '';
+</script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.sub.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.sub.html
index 67bdc46..611fae5b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-computed.sub.html
@@ -15,10 +15,10 @@
 test_computed_value('list-style', 'none', 'outside none none');
 
 test_computed_value('list-style', 'inside', 'inside none disc');
-test_computed_value('list-style', 'url("https://example.com/")', 'outside url("https://example.com/") disc');
+test_computed_value('list-style', 'url("https://{{host}}/")', 'outside url("https://{{host}}/") disc');
 test_computed_value('list-style', 'square', 'outside none square');
 
-test_computed_value('list-style', 'inside url("https://example.com/") square');
+test_computed_value('list-style', 'inside url("https://{{host}}/") square');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-001.tentative.html
new file mode 100644
index 0000000..0998fe6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-001.tentative.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>CSS Overflow Test: Propagation of body overflow to viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/4148">
+<link rel="match" href="reference/overflow-body-propagation-ref.html">
+<style>
+  body {
+    overflow: scroll;
+    margin-top: 100px;
+  }
+</style>
+<body>The viewport should have scrollbars, not the body.</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-002.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-002.tentative.html
new file mode 100644
index 0000000..5991dd5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-002.tentative.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>CSS Overflow Test: Do not propagate overflow of display:none body to viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/4148">
+<link rel="match" href="/css/reference/blank.html">
+<body style="display:none; overflow:scroll"></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-003.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-003.tentative.html
new file mode 100644
index 0000000..a329a8df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-003.tentative.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>CSS Overflow Test: Do not propagate overflow of display:contents body to viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/4148">
+<link rel="match" href="/css/reference/blank.html">
+<body style="display:contents; overflow:scroll"></body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-004.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-004.tentative.html
new file mode 100644
index 0000000..2ed8d268
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-body-propagation-004.tentative.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/4148">
+<link rel="match" href="reference/overflow-body-no-propagation-ref.html">
+<style>
+  html {
+    overflow: hidden;
+  }
+  body {
+    overflow: scroll;
+    margin-top: 100px;
+  }
+</style>
+<body>The body should have scrollbars, not the viewport.</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-no-propagation-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-no-propagation-ref.html
new file mode 100644
index 0000000..9795d1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-no-propagation-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<html style="overflow:auto">
+<title>CSS Overflow Test Reference</title>
+<body style="margin-top:100px;overflow:scroll">The body should have scrollbars, not the viewport.</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-propagation-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-propagation-ref.html
new file mode 100644
index 0000000..340bda9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/reference/overflow-body-propagation-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<html style="overflow:scroll">
+<title>CSS Overflow Test Reference</title>
+<body style="margin-top:100px">The viewport should have scrollbars, not the body.</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001-ref.html
new file mode 100644
index 0000000..1bf59f90
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/926167">
+<style>
+
+#container {
+  width: 200px;
+  height: 150px;
+  border: 1px solid black;
+  overflow: scroll;
+}
+#target {
+  width: 100px;
+  height: 50px;
+  background: green;
+  transform: scale(4);
+}
+</style>
+<!--  -->
+<div id="container">
+  <div id="target"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001.html b/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001.html
new file mode 100644
index 0000000..643781b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scrollbars/scrollbars-chrome-bug-001.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>CSS Overflow: css-overflow-3</title>
+<link rel="author" href="mailto:atotic@google.com">
+<link rel="help" href="https://crbug.com/926167">
+<link rel="match" href="scrollbars-chrome-bug-001-ref.html">
+<meta name="assert" content="scrollbars keep up to date with a changing transform">
+<style>
+
+#container {
+  width: 200px;
+  height: 150px;
+  border: 1px solid black;
+  overflow: scroll;
+}
+#target {
+  width: 100px;
+  height: 50px;
+  background: green;
+  transform: scale(1);
+}
+</style>
+<!--  -->
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+  // 1st transform triggers layer creation, and full layout.
+  // 2nd transform just updates overflow, which does not update scrollbars.
+  // This triggers the bug.
+  document.body.offsetTop;
+  document.querySelector("#target").style.transform = "scale(1.5)";
+  document.body.offsetTop;
+  document.querySelector("#target").style.transform = "scale(4.0)";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed-expected.txt
new file mode 100644
index 0000000..8a246fc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS Property shape-image-threshold value '-7' computes to '0'
+PASS Property shape-image-threshold value '0.5' computes to '0.5'
+PASS Property shape-image-threshold value '12.5' computes to '1'
+PASS Property shape-image-threshold value '-100%' computes to '0'
+FAIL Property shape-image-threshold value '50%' computes to '0.5' assert_equals: expected "0.5" but got "0"
+FAIL Property shape-image-threshold value '300%' computes to '1' assert_equals: expected "1" but got "0"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed.html b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed.html
index a90d23a6..81bc0cc 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-computed.html
@@ -15,6 +15,11 @@
 test_computed_value("shape-image-threshold", "-7", "0");
 test_computed_value("shape-image-threshold", "0.5");
 test_computed_value("shape-image-threshold", "12.5", "1");
+
+// https://github.com/w3c/csswg-drafts/issues/4102
+test_computed_value("shape-image-threshold", "-100%", "0");
+test_computed_value("shape-image-threshold", "50%", "0.5");
+test_computed_value("shape-image-threshold", "300%", "1");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-invalid.html
index 6299e2e..c0cac03 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-invalid.html
@@ -14,7 +14,6 @@
 <script>
 test_invalid_value("shape-image-threshold", "auto");
 test_invalid_value("shape-image-threshold", "10px");
-test_invalid_value("shape-image-threshold", "100%");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid-expected.txt
new file mode 100644
index 0000000..b6e14a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS e.style['shape-image-threshold'] = "12.5" should set the property value
+PASS e.style['shape-image-threshold'] = "-7" should set the property value
+FAIL e.style['shape-image-threshold'] = "-100%" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['shape-image-threshold'] = "50%" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['shape-image-threshold'] = "300%" should set the property value assert_not_equals: property should be set got disallowed value ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid.html b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid.html
index 914abd0..4ed1fb9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/parsing/shape-image-threshold-valid.html
@@ -14,6 +14,11 @@
 <script>
 test_valid_value("shape-image-threshold", "12.5");
 test_valid_value("shape-image-threshold", "-7");
+
+// https://github.com/w3c/csswg-drafts/issues/4102
+test_valid_value("shape-image-threshold", "-100%", "-1");
+test_valid_value("shape-image-threshold", "50%", "0.5");
+test_valid_value("shape-image-threshold", "300%", "3");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence-ref.html
new file mode 100644
index 0000000..a4fc022
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence-ref.html
@@ -0,0 +1,40 @@
+<!doctype HTML>
+<meta charset="utf8">
+<title>Perspective with transforms equivalencies.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<style>
+
+#container {
+  transform: translate(-200px, -200px);
+  width: 500px;
+  height: 500px;
+  perspective: 500px;
+}
+
+#container > div {
+  width: 100%;
+  height: 100%;
+  transform-style: preserve-3d;
+  transform: translateZ(-250px) rotateZ(45deg);
+}
+
+#container > div > div {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  background-color: rgba(3, 121, 255, 0.3);
+  box-sizing: border-box;
+  border: 3px solid black;
+}
+
+#one { transform: rotateY(90deg)  translateZ(250px); }
+</style>
+
+<div id="container">
+  <div>
+    <div id="one"></div>
+    <div id="one"></div>
+    <div id="one"></div>
+    <div id="one"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence.html
new file mode 100644
index 0000000..2f2ae21
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/perspective-transforms-equivalence.html
@@ -0,0 +1,52 @@
+<!doctype HTML>
+<meta charset="utf8">
+<title>Perspective with transforms equivalencies.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="http://www.w3.org/TR/css-transforms-2/#perspective-property">
+<link rel="help" href="http://www.w3.org/TR/css-transforms-1/#transform-property">
+<link rel="match" href="perspective-transforms-equivalence-ref.html">
+<!--
+Perspective with different transforms can have small anti-aliasing
+pixel differences, so the test should fuzzy patch to the ref.
+-->
+<meta name="fuzzy" content="maxDifference=10;totalPixels=10">
+<style>
+
+#container {
+  transform: translate(-200px, -200px);
+  width: 500px;
+  height: 500px;
+  perspective: 500px;
+}
+
+#container > div {
+  width: 100%;
+  height: 100%;
+  transform-style: preserve-3d;
+  transform: translateZ(-250px) rotateZ(45deg);
+}
+
+#container > div > div {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  background-color: rgba(3, 121, 255, 0.3);
+  box-sizing: border-box;
+  border: 3px solid black;
+}
+
+/* The following four should be equivalent. */
+#one { transform: rotateY(90deg)  translateZ(250px); }
+#two { transform: rotateZ(90deg) rotateX(90deg) translateZ(250px); }
+#three { transform: rotateY(-90deg) translateZ(-250px); }
+#four { transform: rotateZ(-90deg) rotateX(90deg) translateZ(-250px); }
+</style>
+
+<div id="container">
+  <div>
+    <div id="one"></div>
+    <div id="two"></div>
+    <div id="three"></div>
+    <div id="four"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/element-only-when-fully-active.html b/third_party/blink/web_tests/external/wpt/element-timing/element-only-when-fully-active.html
new file mode 100644
index 0000000..5608685a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/element-only-when-fully-active.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>ElementTiming: element is only exposed for fully active documents.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="resources/iframe-stores-entry.html" id="ifr"></iframe>
+<script>
+  let t = async_test('Only expose element attribute for fully active documents');
+  window.triggerTest = t.step_func_done(elementEntry => {
+    assert_not_equals(elementEntry.element, null);
+    const iframe = document.getElementById('ifr');
+    iframe.remove();
+    assert_equals(elementEntry.element, null);
+  });
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/fixed-id-identifier.html b/third_party/blink/web_tests/external/wpt/element-timing/fixed-id-identifier.html
new file mode 100644
index 0000000..00d6d4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/fixed-id-identifier.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Element Timing: entry does not change its id or identifier value</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/element-timing-helpers.js"></script>
+<p elementtiming='my_identifier' id='my_id'>Text</p>
+<script>
+  async_test(function (t) {
+    if (!window.PerformanceElementTiming) {
+      assert_unreached("PerformanceElementTiming is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        assert_equals(entryList.getEntries().length, 1);
+        const entry = entryList.getEntries()[0];
+        assert_equals(entry.id, 'my_id');
+        assert_equals(entry.identifier, 'my_identifier');
+        const element = document.getElementById('my_id');
+        element.id = 'other_id';
+        element.setAttribute('elementtiming', 'other_identifier');
+        assert_equals(entry.id, 'my_id');
+        assert_equals(entry.identifier, 'my_identifier');
+      })
+    );
+    observer.observe({type: 'element', buffered: true});
+  }, 'PerformanceElementTiming id and identifier do not change when Element changes.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/idlharness.window.js b/third_party/blink/web_tests/external/wpt/element-timing/idlharness.window.js
new file mode 100644
index 0000000..f0e5d06
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/idlharness.window.js
@@ -0,0 +1,16 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// https://wicg.github.io/element-timing/
+
+'use strict';
+
+idl_test(
+  ['element-timing'],
+  ['performance-timeline', 'dom'],
+  idl_array => {
+    idl_array.add_objects({
+      // PerformanceElementTiming: [ TODO ]
+    });
+  }
+);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
index 8933732..6328ee7 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
+++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
@@ -4,13 +4,22 @@
   assert_equals(entry.entryType, 'element');
   assert_equals(entry.url, expectedUrl);
   assert_equals(entry.identifier, expectedIdentifier);
-  assert_equals(entry.startTime, 0);
+  if (beforeRender != 0) {
+    // In this case, renderTime is not 0.
+    assert_equals(entry.startTime, entry.renderTime);
+  } else {
+    // In this case, renderTime is 0, so compare to loadTime.
+    assert_equals(entry.startTime, entry.loadTime);
+  }
   assert_equals(entry.duration, 0);
   assert_equals(entry.id, expectedID);
   assert_greater_than_equal(entry.renderTime, beforeRender);
   assert_greater_than_equal(performance.now(), entry.renderTime);
-  if (expectedElement !== null)
+  if (expectedElement !== null) {
     assert_equals(entry.element, expectedElement);
+    assert_equals(entry.identifier, expectedElement.elementTiming);
+    assert_equals(entry.id, expectedElement.id);
+  }
 }
 
 // Checks that this is an ElementTiming entry with url |expectedUrl|. It also
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-stores-entry.html b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-stores-entry.html
new file mode 100644
index 0000000..2fa2476
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-stores-entry.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p elementtiming='text'>Text</p>
+<script>
+  const observer = new PerformanceObserver(entryList => {
+    window.parent.triggerTest(entryList.getEntries()[0]);
+  });
+  observer.observe({type: 'element', buffered: true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html b/third_party/blink/web_tests/external/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
index d425996..e3e0778c9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
@@ -23,6 +23,7 @@
 <div id="null"></div>
 <div name="divwithname"></div>
 <div id="-0"></div>
+<div id="log"></div>
 <script>
 var anchors = document.querySelectorAll("a");
 var divs = document.querySelectorAll("div");
@@ -34,7 +35,7 @@
 }, "document.all is an HTMLAllCollection");
 
 test(function() {
-  assert_equals(document.all.length, 24);
+  assert_equals(document.all.length, 25);
 }, "length attribute");
 
 // indexed property getter
@@ -42,12 +43,12 @@
 test(function() {
   assert_equals(document.all[0], document.documentElement);
   assert_equals(document.all[-0], document.documentElement);
-  assert_equals(document.all[23], scripts[2]);
+  assert_equals(document.all[24], scripts[2]);
 }, "indexed property getter");
 
 test(function() {
   assert_equals(document.all[-1], undefined);
-  assert_equals(document.all[24], undefined);
+  assert_equals(document.all[25], undefined);
   assert_equals(document.all[42], undefined);
   assert_equals(document.all[43], undefined);
   assert_equals(document.all[4294967294], undefined);
@@ -86,8 +87,8 @@
 
 test(function() {
   assert_equals(document.all["0"], document.documentElement);
-  assert_equals(document.all["23"], document.scripts[2]);
-  assert_equals(document.all["24"], undefined);
+  assert_equals(document.all["24"], document.scripts[2]);
+  assert_equals(document.all["25"], undefined);
   assert_equals(document.all["42"], undefined);
   assert_equals(document.all["43"], undefined);
 }, "named property getter with \"array index property name\"");
@@ -187,16 +188,16 @@
 
 test(function() {
   assert_equals(document.all("0"), document.documentElement);
-  assert_equals(document.all("23"), document.scripts[2]);
-  assert_equals(document.all("24"), null);
+  assert_equals(document.all("24"), document.scripts[2]);
+  assert_equals(document.all("25"), null);
   assert_equals(document.all("42"), null);
   assert_equals(document.all("43"), null);
 }, "legacy caller with \"array index property name\"");
 
 test(function() {
   assert_equals(document.all(0), document.documentElement);
-  assert_equals(document.all(23), document.scripts[2]);
-  assert_equals(document.all(24), null);
+  assert_equals(document.all(24), document.scripts[2]);
+  assert_equals(document.all(25), null);
   assert_equals(document.all(42), null);
   assert_equals(document.all(43), null);
 }, "legacy caller with \"array index property name\" as number");
@@ -267,16 +268,16 @@
 
 test(function() {
   assert_equals(document.all.item("0"), document.documentElement);
-  assert_equals(document.all.item("23"), document.scripts[2]);
-  assert_equals(document.all.item("24"), null);
+  assert_equals(document.all.item("24"), document.scripts[2]);
+  assert_equals(document.all.item("25"), null);
   assert_equals(document.all.item("42"), null);
   assert_equals(document.all.item("43"), null);
 }, "item method with \"array index property name\"");
 
 test(function() {
   assert_equals(document.all.item(0), document.documentElement);
-  assert_equals(document.all.item(23), document.scripts[2]);
-  assert_equals(document.all.item(24), null);
+  assert_equals(document.all.item(24), document.scripts[2]);
+  assert_equals(document.all.item(25), null);
   assert_equals(document.all.item(42), null);
   assert_equals(document.all.item(43), null);
 }, "item method with \"array index property name\" as number");
@@ -329,6 +330,5 @@
   }
 }, "collections are new live HTMLCollection instances");
 </script>
-<div id="log"></div>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
new file mode 100644
index 0000000..dc3b886c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
@@ -0,0 +1,20 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Element Timing API (https://wicg.github.io/element-timing/)
+
+interface PerformanceElementTiming : PerformanceEntry {
+    readonly attribute DOMHighResTimeStamp renderTime;
+    readonly attribute DOMHighResTimeStamp loadTime;
+    readonly attribute DOMRectReadOnly intersectionRect;
+    readonly attribute DOMString identifier;
+    readonly attribute unsigned long naturalWidth;
+    readonly attribute unsigned long naturalHeight;
+    readonly attribute DOMString id;
+    readonly attribute Element? element;
+    readonly attribute DOMString url;
+};
+
+partial interface Element {
+    [CEReactions] attribute DOMString elementTiming;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl b/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl
new file mode 100644
index 0000000..f16a7ff8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl
@@ -0,0 +1,13 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Largest Contentful Paint (https://wicg.github.io/largest-contentful-paint/)
+
+interface LargestContentfulPaint : PerformanceEntry {
+    readonly attribute DOMHighResTimeStamp renderTime;
+    readonly attribute DOMHighResTimeStamp loadTime;
+    readonly attribute unsigned long size;
+    readonly attribute DOMString id;
+    readonly attribute DOMString url;
+    readonly attribute Element? element;
+};
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..d9362ea 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
@@ -23,8 +23,8 @@
                                              PerformanceObserver observer);
 [Constructor(PerformanceObserverCallback callback), Exposed=(Window,Worker)]
 interface PerformanceObserver {
-  void observe(optional PerformanceObserverInit options);
-  void disconnect();
+  void observe (optional PerformanceObserverInit options = {});
+  void disconnect ();
   PerformanceEntryList takeRecords();
   [SameObject] static readonly attribute FrozenArray<DOMString> supportedEntryTypes;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
index 4130803f..8b0f813 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
@@ -4,23 +4,23 @@
 // Source: User Timing Level 3 (https://w3c.github.io/user-timing/)
 
 dictionary PerformanceMarkOptions {
-            any detail;
-            DOMHighResTimeStamp startTime;
-        };
+    any detail;
+    DOMHighResTimeStamp startTime;
+};
 
-        dictionary PerformanceMeasureOptions {
-            any detail;
-            (DOMString or DOMHighResTimeStamp) start;
-            DOMHighResTimeStamp duration;
-            (DOMString or DOMHighResTimeStamp) end;
-        };
+dictionary PerformanceMeasureOptions {
+    any detail;
+    (DOMString or DOMHighResTimeStamp) start;
+    DOMHighResTimeStamp duration;
+    (DOMString or DOMHighResTimeStamp) end;
+};
 
-        partial interface Performance {
-            PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions = {});
-            void clearMarks(optional DOMString markName);
-            PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions = {}, optional DOMString endMark);
-            void clearMeasures(optional DOMString measureName);
-        };
+partial interface Performance {
+    PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions = {});
+    void clearMarks(optional DOMString markName);
+    PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions = {}, optional DOMString endMark);
+    void clearMeasures(optional DOMString measureName);
+};
 
 [Exposed=(Window,Worker),
  Constructor(DOMString markName, optional PerformanceMarkOptions markOptions)]
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl b/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
index e275aac9..ae1f3f8 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/web-share.idl
@@ -3,6 +3,10 @@
 // (https://github.com/tidoust/reffy-reports)
 // Source: Web Share API - Level 1 (https://wicg.github.io/web-share/)
 
+partial interface Navigator {
+  [SecureContext] Promise<void> share(optional ShareData data = {});
+};
+
 dictionary ShareData {
   USVString title;
   USVString text;
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/rtl-clipped-root.html b/third_party/blink/web_tests/external/wpt/intersection-observer/rtl-clipped-root.html
new file mode 100644
index 0000000..a30c6e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/rtl-clipped-root.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<head>
+  <meta name="viewport" content="width=device-width,initial-scale=1">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="./resources/intersection-observer-test-utils.js"></script>
+
+  <style>
+  pre, #log {
+    position: absolute;
+    top: 120px;
+    left: 0;
+  }
+  #root {
+    width: 350px;
+    height: 100px;
+    border: 1px solid black;
+    display: flex;
+    flex-direction: row;
+    overflow-x: auto;
+  }
+  #target-start, #target-end {
+    width: 100px;
+    height: 100px;
+    flex-shrink: 0;
+    background-color: green;
+    text-align: center;
+  }
+  #target-end {
+    margin-inline-start: 500px;
+  }
+  </style>
+</head>
+
+<div id="root">
+  <div id="target-start">start</div>
+  <div id="target-end">end</div>
+</div>
+
+<script>
+runTestCycle(function() {
+  let io = new IntersectionObserver(entries => {
+    entries.forEach(entry => {
+      if (entry.isIntersecting) {
+        entry.target.classList.add("intersecting");
+      } else {
+        entry.target.classList.remove("intersecting");
+      }
+    });
+  }, { root: document.getElementById("root") });
+  document.querySelectorAll("#root > div").forEach(element => {
+    io.observe(element);
+  });
+  runTestCycle(step0, "First rAF");
+}, "Explicit rtl root with overflow clipping");
+
+function step0() {
+  assert_true(
+    document.getElementById("target-start").classList.contains("intersecting"),
+    "Target at scroll start is intersecting");
+  assert_false(
+    document.getElementById("target-end").classList.contains("intersecting"),
+    "Target at scroll end is not intersecting");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/element-only-when-fully-active.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/element-only-when-fully-active.html
new file mode 100644
index 0000000..41bdc8f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/element-only-when-fully-active.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: element is only exposed for fully active documents.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="resources/iframe-stores-entry.html" id="ifr"></iframe>
+<script>
+  let t = async_test('Only expose element attribute for fully active documents');
+  window.triggerTest = t.step_func_done(entry => {
+    assert_not_equals(entry.element, null);
+    const iframe = document.getElementById('ifr');
+    iframe.remove();
+    assert_equals(entry.element, null);
+  });
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html
new file mode 100644
index 0000000..273fef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Largest Contentful Paint IDL tests</title>
+<link rel="help" href="https://wicg.github.io/largest-contentful-paint/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+<script>
+'use strict';
+
+idl_test(
+  ['largest-contentful-paint'],
+  ['performance-timeline', 'dom'],
+  async (idl_array, t) => {
+    idl_array.add_objects({
+      LargestContentfulPaint: ['lcp']
+    });
+
+    window.lcp = await new Promise((resolve, reject) => {
+      const observer = new PerformanceObserver(entryList => {
+        resolve(entryList.getEntries()[0]);
+      });
+      observer.observe({type: 'largest-contentful-paint', buffered: true});
+      t.step_timeout(() => reject('Timed out waiting for LargestContentfulPaint entry'), 3000);
+    });
+  }
+);
+</script>
+<!-- a contentful element to observe -->
+<img src=/images/green-100x50.png>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-after-untrusted-scroll.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-after-untrusted-scroll.html
new file mode 100644
index 0000000..abe753f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-after-untrusted-scroll.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: observe image.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  async_test(function (t) {
+    if (!window.LargestContentfulPaint) {
+      assert_unreached("LargestContentfulPaint is not implemented");
+    }
+    const 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, 'largest-contentful-paint');
+        assert_greater_than_equal(entry.renderTime, beforeRender,
+          'The rendering timestamp should occur after script starts running.');
+        assert_greater_than_equal(performance.now(), entry.renderTime,
+          'The rendering timestamp should occur before the entry is dispatched to the observer.');
+        assert_equals(entry.startTime, 0);
+        assert_equals(entry.duration, 0);
+        // blue.png is 133 x 106.
+        assert_equals(entry.size, 14098);
+        assert_equals(entry.id, 'image_id');
+        // 25 is the length of "largest-contentful-paint/".
+        const index = window.location.href.lastIndexOf('/') - 25;
+        const pathname = window.location.href.substring(0, index) + '/images/blue.png';
+        assert_equals(entry.url, pathname);
+        assert_greater_than(entry.loadTime, beforeRender,
+          'The load timestamp should occur after script starts running.');
+        assert_less_than(entry.loadTime, entry.renderTime,
+          'The load timestamp should occur before the render timestamp.')
+        assert_equals(entry.element, document.getElementById('image_id'));
+      })
+    );
+    observer.observe({type: 'largest-contentful-paint', buffered: true});
+  }, 'Same-origin image after a JS initiated scroll event is observable.');
+  document.body.dispatchEvent(new Event('scroll'));
+  const image = new Image();
+  image.id = 'image_id';
+  image.src = '/images/blue.png';
+  document.body.appendChild(image);
+</script>
+
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-entry.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-entry.html
new file mode 100644
index 0000000..cd600254
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-entry.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Text</p>
+<script>
+  const observer = new PerformanceObserver(entryList => {
+    window.parent.triggerTest(entryList.getEntries()[0]);
+  });
+  observer.observe({type: 'largest-contentful-paint', buffered: true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
index 6be38d5..5ecb66e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Verify fraction metrics for different sizes of numerator and denominator.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -33,6 +34,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       var mathAxis = getBox("axis").middle;
       // For stacks, nothing in the OpenType MATH specification seems to ensure
@@ -51,6 +54,8 @@
     }, "Fraction axis is aligned on the math axis");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 0; i < 10; i++) {
         assert_less_than(getBox("frac" + i + "num").bottom, getBox("frac" + i + "den").top, "numerator is above denominator");
         assert_less_than(getBox("frac" + i + "den").top - getBox("frac" + i + "num").bottom, 5, "The gap between numerator and denominator is not too large");
@@ -58,12 +63,16 @@
     }, "Vertical positions of numerator and denominator");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       for (var i = 0; i < 10; i++)
         assert_approx_equals(getBox("frac" + i + "num").center, getBox("frac" + i + "den").center, e, "numerator and denominator are horizontally centered");
     }, "Horizontal alignments of numerator and denominator");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 5;
       for (var i = 0; i < 10; i++) {
         var frac = getBox("frac" + i);
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
index 55404fa..b8bc4051 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Element mfrac correctly uses the fraction parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -68,6 +69,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 7000 * emToPx;
       var v2 = 1000 * emToPx;
       assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
@@ -75,54 +78,72 @@
     }, "AxisHeight");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 5000 * emToPx;
       assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
                            v1, epsilon, "mfrac: denominator gap");
     }, "DenominatorDisplayStyleGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 6000 * emToPx;
       assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
                            v1, epsilon, "mfrac: denominator shift");
     }, "DenominatorDisplayStyleShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 4000 * emToPx;
       assert_approx_equals(getBox("den0004").top - getBox("ref0004").bottom,
                            v1, epsilon, "mfrac: denominator gap");
     }, "DenominatorGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 3000 * emToPx;
       assert_approx_equals(getBox("den0005").top - getBox("ref0005").bottom,
                            v1, epsilon, "mfrac: denominator shift");
     }, "DenominatorShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 8000 * emToPx;
       assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
                            v1, epsilon, "mfrac: numerator gap");
     }, "NumeratorDisplayStyleGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 2000 * emToPx;
       assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
                            v1, epsilon, "mfrac: numerator shift");
     }, "NumeratorDisplayStyleShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 9000 * emToPx;
       assert_approx_equals(getBox("ref0008").top - getBox("num0008").bottom,
                            v1, epsilon, "mfrac: numerator gap");
     }, "NumeratorGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 11000 * emToPx;
       assert_approx_equals(getBox("ref0009").top - getBox("num0009").bottom,
                            v1, epsilon, "mfrac: numerator shift");
     }, "NumeratorShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 10000 * emToPx;
       assert_approx_equals(getBox("den0010").top - getBox("num0010").bottom,
                            v1, epsilon, "mfrac: rule thickness");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
index 63ab9776..ce2d299 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -56,42 +57,56 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
                            v, epsilon, "mfrac: axis height");
     }, "AxisHeight");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
                            v, epsilon, "mfrac: denominator shift");
     }, "BottomDisplayStyleShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 6000 * emToPx;
       assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
                            v, epsilon, "mfrac: denominator shift");
     }, "BottomShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 4000 * emToPx;
       assert_approx_equals(getBox("den0004").top - getBox("num0004").bottom,
                            v, epsilon, "mfrac: gap");
     }, "DisplayStyleGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 8000 * emToPx;
       assert_approx_equals(getBox("den0005").top - getBox("num0005").bottom,
                            v, epsilon, "mfrac: gap");
     }, "GapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
                            v, epsilon, "mfrac: numerator shift");
     }, "TopDisplayStyleShiftUp");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 9000 * emToPx;
       assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
                            v, epsilon, "mfrac: numerator shift");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
index 672d90d..1541b2d6 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
@@ -12,6 +12,7 @@
 <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#enclose-expression-inside-notation-menclose">
 <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#adjust-space-around-content-mpadded">
 <meta name="assert" content="Baseline for mrow-like elements is correct.">
+<script src="/mathml/support/feature-detection.js"></script>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script type="text/javascript">
@@ -23,6 +24,7 @@
           var x = document.getElementById("above" + tag).getBoundingClientRect();
           var y = document.getElementById("below" + tag).getBoundingClientRect();
           test(function() {
+              assert_true(MathMLFeatureDetection.has_mspace());
               assert_equals(x.bottom, y.top);
           }, "baseline alignment inside " + tag);
       });
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
index 75587d0..ee40561 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
@@ -13,6 +13,7 @@
 <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#adjust-space-around-content-mpadded">
 <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
 <meta name="assert" content="Operators can stretch inside mrow-like elements.">
+<script src="/mathml/support/feature-detection.js"></script>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
@@ -36,6 +37,7 @@
       ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded"].forEach((tag) => {
           var mo = document.getElementById("mo" + tag);
           test(function() {
+              assert_true(MathMLFeatureDetection.has_mspace());
               assert_greater_than_equal(mo.getBoundingClientRect().height, 100);
           }, "operator stretching inside " + tag);
       });
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html
new file mode 100644
index 0000000..78c5069
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Embellished operators</title>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#embellished-operators">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#definition-of-space-like-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#layout-of-mrow">
+<meta name="assert" content="Verify definition of embellished operators">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<style>
+  /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
+     from the measured/specified 0em and 1em. */
+  math, math * {
+      font: 25px/1 Ahem;
+  }
+  mn {
+      color: black;
+  }
+  .testedElement mo {
+      color: yellow !important;
+  }
+  .testedElement, .testedElement * {
+      color: blue !important;
+      background: blue !important;
+  }
+</style>
+<script>
+  function spaceBeforeElement(id) {
+      var element = document.getElementById(id);
+      var mnBefore = element.previousElementSibling;
+      return element.getBoundingClientRect().left - mnBefore.getBoundingClientRect().right;
+  }
+
+  function spaceBeforeCoreOperator(id) {
+      var element = document.getElementById(id);
+      var coreMo = element.getElementsByTagName("mo")[0];
+      return coreMo.getBoundingClientRect().left - element.getBoundingClientRect().left;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+      var epsilon = 1;
+      var emToPx = 25;
+
+      ["mrow", "mstyle", "mphantom", "mpadded"].forEach(tag => {
+          test(function() {
+              assert_true(MathMLFeatureDetection.has_operator_spacing());
+              assert_approx_equals(spaceBeforeElement(`${tag}-op`), 2 * emToPx, epsilon);
+              assert_approx_equals(spaceBeforeCoreOperator(`${tag}-op`), 0, epsilon);
+          }, `${tag} (embellished operator)`);
+
+          test(function() {
+              assert_true(MathMLFeatureDetection.has_operator_spacing());
+              assert_approx_equals(spaceBeforeElement(`${tag}-nonop`), 0, epsilon);
+              assert_approx_equals(spaceBeforeCoreOperator(`${tag}-nonop`), 2 * emToPx, epsilon);
+          }, `${tag} (not embellished operator)`);
+      });
+
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mrow id="mrow-op" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mtext class="space-like">X</mtext>
+      </mrow>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mrow id="mrow-nonop" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn> <!-- "mn" is not space-like -->
+      </mrow>
+      <mn>X</mn>
+    </math>
+  </p>
+  <!-- mstyle is an embellished operator if its children consist
+       of one embellished operator and zero or more space-like elements. -->
+  <p>
+    <math>
+      <mn>X</mn>
+      <mstyle id="mstyle-op" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+      </mstyle>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mstyle id="mstyle-nonop" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn> <!-- "mn" is not space-like -->
+      </mstyle>
+      <mn>X</mn>
+    </math>
+  </p>
+  <!-- mphantom is an embellished operator if its children consist
+       of one embellished operator and zero or more space-like elements. -->
+  <p>
+    <math>
+      <mn>X</mn>
+      <mphantom id="mphantom-op" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+      </mphantom>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mphantom id="mphantom-nonop" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn> <!-- "mn" is not space-like -->
+      </mphantom>
+      <mn>X</mn>
+    </math>
+  </p>
+  <!-- mpadded is an embellished operator if its children consist
+       of one embellished operator and zero or more space-like elements. -->
+  <p>
+    <math>
+      <mn>X</mn>
+      <mpadded id="mpadded-op" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+      </mpadded>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mpadded id="mpadded-nonop" class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn> <!-- "mn" is not space-like -->
+      </mpadded>
+      <mn>X</mn>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html
new file mode 100644
index 0000000..da343dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html
@@ -0,0 +1,286 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Embellished operators</title>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#embellished-operators">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#definition-of-space-like-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#layout-of-mrow">
+<meta name="assert" content="Verify definition of embellished operators">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<style>
+  /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
+     from the measured/specified 0em and 1em. */
+  math, math * {
+      font: 25px/1 Ahem;
+  }
+  mn {
+      color: black;
+  }
+  mtext.space-like {
+      color: lightblue !important;
+  }
+  .testedElement mo {
+      color: yellow !important;
+  }
+  .testedElement, .testedElement * {
+      color: blue !important;
+      background: blue !important;
+  }
+</style>
+<script>
+  function spaceBeforeElement(element) {
+      var mnBefore = element.previousElementSibling;
+      return element.getBoundingClientRect().left - mnBefore.getBoundingClientRect().right;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+      var epsilon = 1;
+      var emToPx = 25;
+
+      ["msub", "msup", "msubsup", "munder", "mover", "munderover",
+       "mmultiscripts", "mfrac", "maction", "semantics"].forEach(tag => {
+           test(function() {
+               assert_true(MathMLFeatureDetection.has_operator_spacing());
+               var element = document.getElementsByTagName(tag)[0];
+               assert_approx_equals(spaceBeforeElement(element), 2 * emToPx, epsilon);
+           }, `${tag} (embellished operator)`);
+
+           test(function() {
+               assert_true(MathMLFeatureDetection.has_operator_spacing());
+               var element = document.getElementsByTagName(tag)[1];
+               assert_approx_equals(spaceBeforeElement(element), 0, epsilon);
+           }, `${tag} (not embellished operator)`);
+      });
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+  <!-- <msub>, <msup>, <msubsup>, <munder>, <mover>, <munderover>,
+       <mmultiscripts>, <mfrac>, <semantics> or <maction> are embellished
+       operators if their first child exists and is an embellished operator -->
+  <p>
+    <math>
+      <mn>X</mn>
+      <msub class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </msub>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <msup class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </msup>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <msubsup class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+        <mn>X</mn>
+      </msubsup>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <munder class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </munder>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mover class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </mover>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <munderover class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </munderover>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mmultiscripts class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+        <mn>X</mn>
+        <mn>X</mn>
+        <mn>X</mn>
+      </mmultiscripts>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mfrac class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </mfrac>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <maction class="testedElement" actiontype="statusline">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>STATUS MESSAGE</mn>
+      </maction>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <semantics class="testedElement">
+        <mo lspace="2em" rspace="0em">X</mo>
+        <annotation>TEXT ANNOTATION</annotation>
+        <mn>X</mn>
+      </semantics>
+      <mn>X</mn>
+    </math>
+  </p>
+  <!-- <msub>, <msup>, <msubsup>, <munder>, <mover>, <munderover>,
+       <mmultiscripts>, <mfrac>, <semantics> or <maction> are not embellished
+       operators if their first child is not an embellished operator -->
+  <p>
+    <math>
+      <mn>X</mn>
+      <msub class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </msub>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <msup class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </msup>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <msubsup class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+      </msubsup>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <munder class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </munder>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mover class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </mover>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <munderover class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </munderover>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mmultiscripts class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+        <mn>X</mn>
+        <mn>X</mn>
+        <mn>X</mn>
+      </mmultiscripts>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <mfrac class="testedElement">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">X</mo>
+      </mfrac>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <maction class="testedElement" actiontype="statusline">
+        <mn>X</mn>
+        <mo lspace="2em" rspace="0em">STATUS MESSAGE</mo>
+      </maction>
+      <mn>X</mn>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mn>X</mn>
+      <semantics class="testedElement">
+        <mrow>
+          <mn>X</mn>
+          <mo lspace="2em" rspace="0em">X</mo>
+        </mrow>
+        <annotation>TEXT ANNOTATION</annotation>
+      </semantics>
+      <mn>X</mn>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
index 5ad0b73..d09d117 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements msqrt and mroot correctly use the radical parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -56,6 +57,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 25;
       var v2 = 1000 * emToPx;
       var radicalHeight = getBox("base001").height + v2;
@@ -65,6 +68,8 @@
     }, "RadicalDegreeBottomRaisePercent");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 7000 * emToPx;
       var v2 = 1000 * emToPx;
       assert_approx_equals(getBox("base0021").top - getBox("radical0021").top,
@@ -76,6 +81,8 @@
     }, "RadicalDisplayStyleVerticalGap");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 3000 * emToPx;
       var v2 = 1000 * emToPx;
       assert_approx_equals(getBox("base0031").top - getBox("radical0031").top,
@@ -86,7 +93,9 @@
                            "mroot: vertical gap");
     }, "RadicalExtraAscender");
 
-  test(function() {
+    test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       // Note: the size variants of U+221A in this font have width 1000.
       var v1 = 5000 * emToPx;
       var radicalSymbolWidth = 1000 * emToPx;
@@ -97,6 +106,8 @@
     }, "RadicalKernAfterDegree");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 4000 * emToPx;
       assert_approx_equals(getBox("index005").left - getBox("radical005").left,
                            v1, epsilon,
@@ -104,6 +115,8 @@
     }, "RadicalKernBeforeDegree");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 8000 * emToPx;
       assert_approx_equals(getBox("base0061").top - getBox("radical0061").top,
                            v, epsilon,
@@ -114,6 +127,8 @@
     }, "RadicalRuleThickness");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 6000 * emToPx;
       var v2 = 1000 * emToPx;
       assert_approx_equals(getBox("base0071").top - getBox("radical0071").top,
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
index 01a6b0e..e5321d6c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Basic metrics for elements msub, msup and msubsup.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -28,6 +29,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_less_than_equal(getBox("msubBase").right, getBox("msubSub").left, e, "msub: subscript is after base");
       assert_less_than_equal(getBox("msupBase").right, getBox("msupSup").left, e, "msup: superscript is after base");
@@ -43,6 +46,8 @@
     }, "Respective horizontal positions");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_approx_equals(getBox("msubBase").middle, getBox("baseline").bottom, e, "msub: base is placed on the baseline");
       assert_approx_equals(getBox("msupBase").middle, getBox("baseline").bottom, e, "msup: base is placed on the baseline");
@@ -50,6 +55,8 @@
     }, "Alignment of the base on the baseline");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       assert_approx_equals(getBox("msubSub").middle, getBox("msubBase").bottom, e, "msub: script is placed at the bottom of the base");
       assert_approx_equals(getBox("msupSup").middle, getBox("msupBase").top, e, "msup: script is placed at the top of the base");
@@ -58,6 +65,8 @@
     }, "Vertical position of scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       assert_approx_equals(getBox("msub").width, getBox("msubSub").right - getBox("msubBase").left, e, "msub: width is determined by the left/right sides of base/script (+ some space after script)");
       assert_approx_equals(getBox("msup").width, getBox("msupSup").right - getBox("msupBase").left, e, "msup: width is determined by the left/right sides of base/script (+ some space after script)");
@@ -65,6 +74,8 @@
     }, "Width of scripted elements");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_greater_than_equal(getBox("msub").height, getBox("msubBase").height, e, "msub: height is at least the one of the base");
       assert_greater_than_equal(getBox("msup").height, getBox("msupBase").height, e, "msup: height is at least the one of the base");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
index 2fd6963..1a5b80d3 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Basic metrics for the mmultiscript element.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -28,6 +29,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_less_than_equal(getBox("msubBase").right, getBox("msubSub").left, e, "subscript is after base");
       assert_less_than_equal(getBox("msupBase").right, getBox("msupSup").left, e, "superscript is after base");
@@ -54,6 +57,8 @@
     }, "Respective horizontal positions");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_approx_equals(getBox("msubBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
       assert_approx_equals(getBox("msupBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
@@ -64,6 +69,8 @@
     }, "Alignment of the base on the baseline");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       assert_approx_equals(getBox("msubSub").middle, getBox("msubBase").bottom, e, "script is placed at the bottom of the base");
       assert_approx_equals(getBox("msupSup").middle, getBox("msupBase").top, e, "script is placed at the top of the base");
@@ -77,6 +84,8 @@
     }, "Vertical position of scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       assert_approx_equals(getBox("msub").width, getBox("msubSub").right - getBox("msubBase").left, e, "width is determined by the left/right sides of base/script (+ some space after script)");
       assert_approx_equals(getBox("msup").width, getBox("msupSup").right - getBox("msupBase").left, e, "width is determined by the left/right sides of base/script (+ some space after script)");
@@ -88,6 +97,8 @@
     }, "Width of scripted elements");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       assert_greater_than_equal(getBox("msub").height, getBox("msubBase").height, e, "height is at least the one of the base");
       assert_greater_than_equal(getBox("msup").height, getBox("msupBase").height, e, "height is at least the one of the base");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
index 60df24a..5494bb0c9 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Basic metrics for the mmultiscript element with many scripts.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -28,12 +29,16 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       for (var i = 0; i < 5; i++)
         assert_approx_equals(getBox("multi" + i + "base").middle, getBox("baseline").bottom, e, "base " + i + "is placed on the baseline");
     }, "Alignment of the base on the baseline");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 5;
       assert_approx_equals(getBox("multi0").width, 30, e, "width of multi0");
       assert_approx_equals(getBox("multi0").height, 30, e, "height of multi0");
@@ -49,6 +54,8 @@
     }, "Dimensions of the scripted elements");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3;
       for (var i = 2; i <= 4; i++) {
         var base = getBox("multi" + i + "base");
@@ -66,6 +73,8 @@
     }, "Vertical positions of scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       for (var i = 2; i <= 4; i++) {
         var base = getBox("multi" + i + "base");
@@ -81,6 +90,8 @@
     }, "Horizontal alignment of scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 2; i <= 4; i++) {
         var base = getBox("multi" + i + "base");
         var firstPostScript = getBox("multi" + i + "postsub1");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
index 68867660..00bd454 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Verify metrics of scripted elements for bases of different heights.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -30,6 +31,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       sizeArray.forEach(function(size) {
          assert_approx_equals(getBox("msub" + size + "base").middle, getBox("baseline").bottom, e, "msub base " + size + "is placed on the baseline");
@@ -40,6 +43,8 @@
     }, "Alignment on the baseline for bases of different heights");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 5;
       sizeArray.forEach(function(size) {
          assert_approx_equals(getBox("msub" + size + "sub").middle, getBox("msub" + size + "base").bottom, e, "msub script " + size + "is placed at the top of of the base");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
index 2cc4e6d9..566b0133 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Verify metrics of scripted elements with tall scripts.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -30,6 +31,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
        assert_approx_equals(getBox("msubbase").middle, getBox("baseline").bottom, e, "msub base is placed on the baseline");
        assert_approx_equals(getBox("msupbase").middle, getBox("baseline").bottom, e, "msup base is placed on the baseline");
@@ -38,6 +41,8 @@
     }, "Alignment on the baseline with different and large script heights");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       assert_greater_than(getBox("msubsub").top, getBox("msubbase").top, "msub: subscript is below the top of the base");
       assert_less_than(getBox("msupsup").bottom, getBox("msupbase").bottom, "msup: supscript is above the bottom of the base");
       assert_greater_than(getBox("msubsupsub").top, getBox("msubsupbase").top, "msubsup: subscript is below the top of the base");
@@ -49,6 +54,8 @@
     }, "Tall subscripts/superscripts are not placed too high/low");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       assert_greater_than(getBox("msubsupsub").top, getBox("msubsupsup").bottom, "msubsup: subscript is below the superscript");
       assert_greater_than(getBox("multipresub").top, getBox("multipresup").bottom, "mmultiscripts: presubscript is below the presuperscript");
       assert_greater_than(getBox("multipostsub").top, getBox("multipostsup").bottom, "mmultiscripts: postsubscript is below the postsuperscript");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
index 7503ad16..a5f21ec5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the subscript and superscript parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -68,6 +69,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("ref001").left - getBox("sub001").right, v, epsilon, "msub: Space after subscript");
       assert_approx_equals(getBox("ref002").left - getBox("sup002").right, v, epsilon, "msup: Space after superscript");
@@ -78,6 +81,8 @@
     }, "SpaceAfterScript");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref101").bottom - getBox("sup102").bottom, v, epsilon, "msup: Superscript shift");
       assert_approx_equals(getBox("ref101").bottom - getBox("sup103").bottom, v, epsilon, "msubsup: Superscript shift");
@@ -87,6 +92,8 @@
     }, "SuperscriptShiftUp");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("ref201").bottom - getBox("sup202").bottom, v, epsilon, "msup: Superscript shift");
       assert_approx_equals(getBox("ref201").bottom - getBox("sup203").bottom, v, epsilon, "msubsup: Superscript shift");
@@ -96,6 +103,8 @@
     }, "SuperscriptShiftUpCramped");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 6000 * emToPx;
       assert_approx_equals(getBox("sub301").bottom - getBox("ref300").bottom, v, epsilon, "msup: Subscript shift");
       assert_approx_equals(getBox("sub302").bottom - getBox("ref300").bottom, v, epsilon, "msubsup: Subscript shift");
@@ -104,12 +113,16 @@
     }, "SubscriptShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 11000 * emToPx;
       assert_approx_equals(getBox("sub4011").top - getBox("sup4012").bottom, v, epsilon, "msubsup: SubSuperscript gap");
       assert_approx_equals(getBox("sub4021").top - getBox("sup4022").bottom, v, epsilon, "mmultiscripts: SubSuperscript gap");
     }, "SubSuperscriptGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 11000 * emToPx;
       var v2 = 3000 * emToPx;
       assert_approx_equals(getBox("sub501").top - getBox("sup501").bottom, v1, epsilon, "msubsup: SubSuperscript gap");
@@ -119,21 +132,29 @@
     }, "SuperscriptBottomMaxWithSubscript");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 4000 * emToPx;
       assert_approx_equals(getBox("ref600").bottom - getBox("sub601").top, v, epsilon, "msub: Subscript top");
     }, "SubscriptTopMax");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 8000 * emToPx;
       assert_approx_equals(getBox("ref700").bottom - getBox("sub701").bottom, v, epsilon, "msub: Superscript bottom");
     }, "SuperscriptBottomMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 9000 * emToPx;
       assert_approx_equals(getBox("sub801").bottom - getBox("base801").bottom, v, epsilon, "msub: Superscript drop");
     }, "SubscriptBaselineDrop");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 10000 * emToPx;
       assert_approx_equals(getBox("sup901").bottom - getBox("base901").top, v, epsilon, "msup: Superscript drop");
     }, "SuperscriptBaselineDrop");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
index 62aa7a9d..0abf0183 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the italic correction from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -42,6 +43,8 @@
   var epsilon = 1;
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 0;
       assert_approx_equals(getBox("base001").right - getBox("sub001").left, v, epsilon, "msub");
       assert_approx_equals(getBox("sup002").left, getBox("base002").right, epsilon, "msup");
@@ -50,6 +53,8 @@
       assert_approx_equals(getBox("sup005").left - getBox("sub005").left, 0, epsilon, "mmultiscripts prescripts");
     }, "Null Italic Correction");
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("base011").right - getBox("sub011").left, v, epsilon, "msub");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
index b178355..5b71870 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements munder, mover, munderover correctly .">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace, mo {
     font-size: 10px;
@@ -29,6 +30,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       for (var i = 0; i <= 3; i++) {
         assert_approx_equals(getBox("under" + i + "base").middle, getBox("baseline").bottom, e, "munder " + i + ": base is placed on the baseline");
@@ -40,6 +43,8 @@
     }, "Alignment of the base on the baseline");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       for (var i = 0; i <= 3; i++) {
         assert_approx_equals(getBox("under" + i + "under").center, getBox("under" + i + "base").center, e, "munder " + i + ": base and script are horizontally centered");
@@ -52,6 +57,8 @@
     }, "Horizontal alignments of base and scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 0; i <= 3; i++) {
         assert_greater_than_equal(getBox("under" + i + "under").top, getBox("under" + i + "base").bottom, "munder " + i + ": script is under base");
         assert_less_than_equal(getBox("over" + i + "over").bottom, getBox("over" + i + "base").top, "mover " + i + ": script is over base");
@@ -63,6 +70,8 @@
     }, "Relative vertical positions of base and scripts");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 1;
       for (var i = 0; i <= 3; i++) {
         assert_approx_equals(getBox("under" + i).width, Math.max(getBox("under" + i + "base").width, getBox("under" + i + "under").width), e, "munder " + i + ": width is determined by the maximum of width of base and script");
@@ -74,6 +83,8 @@
     }, "Width of scripted elements");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var e = 3.2;
       for (var i = 0; i <= 3; i++) {
         assert_approx_equals(getBox("under" + i).height, getBox("under" + i + "base").height + getBox("under" + i + "under").height + e, e, "munder " + i + ": height is determined by the sum of heights of base and script plus some spacing.");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
index d8a564a..26a1b39 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements munder, mover, munderover correctly use the limit parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace, mo {
     font-size: 10px;
@@ -44,6 +45,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("under00011").top - getBox("ref0001").bottom,
                            v, epsilon, "munder: under shift");
@@ -52,6 +55,8 @@
     }, "LowerLimitBaselineDropMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 11000 * emToPx;
       assert_approx_equals(getBox("under00021").top - getBox("ref0002").bottom,
                            v, epsilon, "munder: under gap");
@@ -60,6 +65,8 @@
     }, "LowerLimitGapMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("ref0003").top - getBox("over00031").bottom,
                            v, epsilon, "mover: over shift");
@@ -68,6 +75,8 @@
     }, "UpperLimitBaselineRiseMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref0004").top - getBox("over00041").bottom,
                            v, epsilon, "mover: over shift");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
index c10f77e..aed22235 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements munder, mover, munderover correctly use the stretch stack parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace, mo {
     font-size: 10px;
@@ -44,6 +45,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("under00011").top - getBox("ref0001").bottom,
                            v, epsilon, "munder: under shift");
@@ -52,6 +55,8 @@
     }, "StretchStackBottomShiftDown");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 11000 * emToPx;
       assert_approx_equals(getBox("under00021").top - getBox("ref0002").bottom,
                            v, epsilon, "munder: under gap");
@@ -60,6 +65,8 @@
     }, "StretchStackGapBelowMin");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("ref0003").top - getBox("over00031").bottom,
                            v, epsilon, "mover: over shift");
@@ -68,6 +75,8 @@
     }, "StretchStackTopShiftUp");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref0004").top - getBox("over00041").bottom,
                            v, epsilon, "mover: over shift");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
index 86562fd..6a1e51c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace, mo {
     font-size: 10px;
@@ -47,6 +48,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 1; i <= 4; i++) {
         for (var j = 1; j <= 6; j++) {
            var baseId = ("base00" + i) + j;
@@ -59,6 +62,8 @@
     }, "Baseline alignment");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 1; i <= 4; i++) {
         for (var j = 1; j <= 6; j++) {
            var baseId = ("base00" + i) + j;
@@ -72,6 +77,8 @@
     }, "Heights of bases");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("ref001").bottom - getBox("over0014").bottom,
                            shortBaseHeight, epsilon,
@@ -96,6 +103,8 @@
     }, "AccentBaseHeight, UnderbarExtraDescender");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref002").bottom - getBox("over0024").bottom,
                            shortBaseHeight, epsilon,
@@ -118,6 +127,8 @@
     }, "AccentBaseHeight, UnderbarVerticalGap");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("ref003").bottom - getBox("over0031").bottom,
                            shortBaseHeight, epsilon,
@@ -154,6 +165,8 @@
     }, "AccentBaseHeight, OverbarExtraAscender");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       v = 11000 * emToPx;
       assert_approx_equals(getBox("ref004").bottom - getBox("over0041").bottom,
                            shortBaseHeight + v, epsilon,
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
index f7fb389..e569c15 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace, mo {
     font-size: 10px;
@@ -47,6 +48,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 1; i <= 4; i++) {
         for (var j = 1; j <= 6; j++) {
            var baseId = ("base00" + i) + j;
@@ -59,6 +62,8 @@
     }, "Baseline alignment");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       for (var i = 1; i <= 4; i++) {
         for (var j = 1; j <= 6; j++) {
            var baseId = ("base00" + i) + j;
@@ -72,6 +77,8 @@
     }, "Heights of bases");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 5000 * emToPx;
       assert_approx_equals(getBox("ref001").bottom - getBox("over0014").bottom,
                            shortBaseHeight, epsilon,
@@ -96,6 +103,8 @@
     }, "AccentBaseHeight, UnderbarExtraDescender");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 7000 * emToPx;
       assert_approx_equals(getBox("ref002").bottom - getBox("over0024").bottom,
                            shortBaseHeight, epsilon,
@@ -118,6 +127,8 @@
     }, "AccentBaseHeight, UnderbarVerticalGap");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v = 3000 * emToPx;
       assert_approx_equals(getBox("ref003").bottom - getBox("over0031").bottom,
                            shortBaseHeight, epsilon,
@@ -154,6 +165,8 @@
     }, "AccentBaseHeight, OverbarExtraAscender");
 
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       v = 11000 * emToPx;
       assert_approx_equals(getBox("ref004").bottom - getBox("over0041").bottom,
                            shortBaseHeight + v, epsilon,
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-001.html
index d2db456..61375bcd 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-001.html
@@ -10,6 +10,7 @@
 <meta name="assert" content="Verify definition of space-like elements">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
      from the measured/specified 0em and 1em. */
@@ -48,51 +49,61 @@
       var emToPx = 25;
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mtext"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mtext"), emToPx, epsilon);
       }, "mtext is space-like");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mspace"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mspace"), emToPx, epsilon);
       }, "mspace is space-like");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mrow1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mrow1"), emToPx, epsilon);
       }, "space-like mrow");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mrow2"), 0, epsilon);
           assert_approx_equals(spaceAfter("mrow2"), 2 * emToPx, epsilon);
       }, "non-space-like mrow");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mstyle1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mstyle1"), emToPx, epsilon);
       }, "space-like mstyle");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mstyle2"), 0, epsilon);
           assert_approx_equals(spaceAfter("mstyle2"), 2 * emToPx, epsilon);
       }, "non-space-like mstyle");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mphantom1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mphantom1"), emToPx, epsilon);
       }, "space-like mphantom");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mphantom2"), 0, epsilon);
           assert_approx_equals(spaceAfter("mphantom2"), 2 * emToPx, epsilon);
       }, "non-space-like mphantom");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mpadded1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("mpadded1"), emToPx, epsilon);
       }, "space-like mpadded");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("mpadded2"), 0, epsilon);
           assert_approx_equals(spaceAfter("mpadded2"), 2 * emToPx, epsilon);
       }, "non-space-like mpadded");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-002.html
index 78f4c92..f5f4e5a8 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-002.html
@@ -10,6 +10,7 @@
 <meta name="assert" content="Verify definition of space-like elements">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
      from the measured/specified 0em and 1em. */
@@ -48,31 +49,37 @@
       var emToPx = 25;
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("maction1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("maction1"), emToPx, epsilon);
       }, "space-like maction");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("maction2"), 0, epsilon);
           assert_approx_equals(spaceAfter("maction2"), 2 * emToPx, epsilon);
       }, "non-space like maction (no first child)");
 
       test(function() {
           assert_approx_equals(spaceBefore("maction3"), 0, epsilon);
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceAfter("maction3"), 2 * emToPx, epsilon);
       }, "non-space like maction (first child not space-like)");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("semantics1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("semantics1"), emToPx, epsilon);
       }, "space-like semantics");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("semantics2"), 0, epsilon);
           assert_approx_equals(spaceAfter("semantics2"), 2 * emToPx, epsilon);
       }, "non-space like semantics (no first child)");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("semantics3"), 0, epsilon);
           assert_approx_equals(spaceAfter("semantics3"), 2 * emToPx, epsilon);
       }, "non-space like semantics (first child not space-like)");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-003.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-003.html
index 8831657a..6d3d007 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-003.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-003.html
@@ -10,6 +10,7 @@
 <meta name="assert" content="Verify definition of space-like elements">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
      from the measured/specified 0em and 1em. */
@@ -47,6 +48,7 @@
 
       Array.from(document.querySelectorAll(".testedElement")).forEach(el => {
           test(function() {
+              assert_true(MathMLFeatureDetection.has_operator_spacing());
               assert_approx_equals(spaceBefore(el), 0, epsilon);
               assert_approx_equals(spaceAfter(el), 2 * emToPx, epsilon);
           }, `${el.tagName} is not space-like`);
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-004.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-004.html
index 28a2871b..091a3ea0 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-004.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-like-004.html
@@ -10,6 +10,7 @@
 <meta name="assert" content="Verify definition of space-like elements">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   /* Default spacing of operator 'X' is 0.2777777777777778em so quite different
      from the measured/specified 0em and 1em. */
@@ -48,11 +49,13 @@
       var emToPx = 25;
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("complex1"), emToPx, epsilon);
           assert_approx_equals(spaceAfter("complex1"), emToPx, epsilon);
       }, "complex space-like subtree");
 
       test(function() {
+          assert_true(MathMLFeatureDetection.has_operator_spacing());
           assert_approx_equals(spaceBefore("complex2"), 0, epsilon);
           assert_approx_equals(spaceAfter("complex2"), 2 * emToPx, epsilon);
       }, "complex non-space-like subtree");
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
index feb2907..3eaf9c8f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
@@ -7,6 +7,7 @@
 <meta name="assert" content="Element mtable correctly uses the axis height parameter from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
 <style>
   math, mspace {
     font-size: 10px;
@@ -32,6 +33,8 @@
 
   function runTests() {
     test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+
       var v1 = 5000 * emToPx;
       var tableMiddle = (getBox("table").bottom + getBox("table").top) / 2;
       assert_approx_equals(getBox("baseline").bottom - tableMiddle,
diff --git a/third_party/blink/web_tests/external/wpt/mathml/support/feature-detection.js b/third_party/blink/web_tests/external/wpt/mathml/support/feature-detection.js
new file mode 100644
index 0000000..5beb78c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/support/feature-detection.js
@@ -0,0 +1,31 @@
+// This is a helper for MathML feature detection.
+// It is indented to be used to prevent false negative test results.
+
+var MathMLFeatureDetection = {
+    has_mspace: function() {
+        if (!this.hasOwnProperty("_has_mspace")) {
+            document.body.insertAdjacentHTML("beforeend", "<math><mspace></mspace><mspace width='20px'></mspace></math>");
+            var math = document.body.lastElementChild;
+            // The width attribute will add 20px per MathML and none if not supported.
+            this._has_mspace =
+                math.lastChild.getBoundingClientRect().width -
+                math.firstChild.getBoundingClientRect().width > 10;
+            document.body.removeChild(math);
+        }
+        return this._has_mspace;
+    },
+
+    has_operator_spacing: function() {
+        if (!this.hasOwnProperty("_has_operator_spacing")) {
+            document.body.insertAdjacentHTML("beforeend", "<math><mrow><mn>1</mn><mo lspace='0px' rspace='0px'>+</mo><mn>2</mn><mo lspace='8px' rspace='8px'>+</mo><mn>3</mn></mspace></mrow></math>");
+            var math = document.body.lastElementChild;
+            var mo = math.getElementsByTagName("mo");
+            // lspace/rspace will add 16px per MathML and none if not supported.
+            this._has_operator_spacing =
+                mo[1].getBoundingClientRect().width -
+                mo[0].getBoundingClientRect().width > 10;
+            document.body.removeChild(math);
+        }
+        return this._has_operator_spacing;
+    }
+};
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py b/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
index fe4305c4..ed978bc 100755
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/tools/generate.py
@@ -3,62 +3,69 @@
 import os
 import sys
 
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'common', 'security-features', 'tools'))
+sys.path.insert(
+    0,
+    os.path.join(
+        os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'common',
+        'security-features', 'tools'))
 import generate
 
 
 class MixedContentConfig(object):
-  def __init__(self):
-    self.selection_pattern = '%(subresource)s/' + \
-                             '%(opt_in_method)s/' + \
-                             '%(origin)s/' + \
-                             '%(context_nesting)s/' + \
-                             '%(redirection)s/'
+    def __init__(self):
+        self.selection_pattern = '%(subresource)s/' + \
+                                 '%(delivery_type)s/' + \
+                                 '%(delivery_value)s/' + \
+                                 '%(origin)s/' + \
+                                 'top-level/' + \
+                                 '%(redirection)s/'
 
-    self.test_file_path_pattern = self.selection_pattern + \
-                                  '%(spec_name)s/' + \
-                                  '%(name)s.%(source_scheme)s.html'
+        self.test_file_path_pattern = self.selection_pattern + \
+                                      '%(spec_name)s/' + \
+                                      '%(name)s.%(source_scheme)s.html'
 
-    self.test_description_template = '''opt_in_method: %(opt_in_method)s
+        self.test_description_template = '''delivery_type: %(delivery_type)s
+delivery_value: %(delivery_value)s
 origin: %(origin)s
 source_scheme: %(source_scheme)s
-context_nesting: %(context_nesting)s
+context_nesting: top-level
 redirection: %(redirection)s
 subresource: %(subresource)s
 expectation: %(expectation)s
 '''
 
-    self.test_page_title_template = 'Mixed-Content: %s'
+        self.test_page_title_template = 'Mixed-Content: %s'
 
-    self.helper_js = '/mixed-content/generic/mixed-content-test-case.js?pipe=sub'
+        self.helper_js = '/mixed-content/generic/mixed-content-test-case.js?pipe=sub'
 
-    # For debug target only.
-    self.sanity_checker_js = '/mixed-content/generic/sanity-checker.js'
-    self.spec_json_js = '/mixed-content/spec_json.js'
+        # For debug target only.
+        self.sanity_checker_js = '/mixed-content/generic/sanity-checker.js'
+        self.spec_json_js = '/mixed-content/spec_json.js'
 
-    self.test_case_name = 'MixedContentTestCase'
+        self.test_case_name = 'MixedContentTestCase'
 
-    script_directory = os.path.dirname(os.path.abspath(__file__))
-    self.spec_directory = os.path.abspath(os.path.join(script_directory, '..', '..'))
+        script_directory = os.path.dirname(os.path.abspath(__file__))
+        self.spec_directory = os.path.abspath(
+            os.path.join(script_directory, '..', '..'))
 
-  def handleDelivery(self, selection, spec):
-    opt_in_method = selection['opt_in_method']
+    def handleDelivery(self, selection, spec):
+        delivery_type = selection['delivery_type']
+        delivery_value = selection['delivery_value']
 
-    meta = ''
-    headers = []
+        meta = ''
+        headers = []
 
-    # TODO(kristijanburnik): Implement the opt-in-method here.
-    if opt_in_method == 'meta-csp':
-        meta = '<meta http-equiv="Content-Security-Policy" ' + \
-               'content="block-all-mixed-content">'
-    elif opt_in_method == 'http-csp':
-        headers.append("Content-Security-Policy: block-all-mixed-content")
-    elif opt_in_method == 'no-opt-in':
-        pass
-    else:
-        raise ValueError("Invalid opt_in_method %s" % opt_in_method)
+        if delivery_value is not None:
+            if delivery_type == 'meta':
+                meta = '<meta http-equiv="Content-Security-Policy" ' + \
+                       'content="block-all-mixed-content">'
+            elif delivery_type == 'http-rp':
+                headers.append(
+                    "Content-Security-Policy: block-all-mixed-content")
+            else:
+                raise ValueError("Invalid delivery_type %s" % delivery_type)
 
-    return {"meta": meta, "headers": headers}
+        return {"meta": meta, "headers": headers}
 
 
 if __name__ == '__main__':
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json b/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
index 06d381d..95dc2d3 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/spec.src.json
@@ -10,28 +10,34 @@
           "name": "opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": ["http-csp", "meta-csp"],
-          "context_nesting": "top-level",
+          "delivery_type": "*",
+          "delivery_value": "opt-in",
           "redirection": "*",
           "subresource": {
             "blockable": [],
             "optionally-blockable": "*"
           },
-          "origin": ["cross-origin-http", "same-host-http"],
+          "origin": [
+            "cross-http",
+            "same-http"
+          ],
           "expectation": "blocked"
         },
         {
           "name": "no-opt-in-allows",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": "no-opt-in",
-          "context_nesting": "top-level",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
           "subresource": {
             "blockable": [],
             "optionally-blockable": "*"
           },
-          "origin": ["cross-origin-http", "same-host-http"],
+          "origin": [
+            "cross-http",
+            "same-http"
+          ],
           "expectation": "allowed"
         }
       ]
@@ -46,42 +52,51 @@
           "name": "opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": ["http-csp", "meta-csp"],
-          "context_nesting": "top-level",
+          "delivery_type": "*",
+          "delivery_value": "opt-in",
           "redirection": "*",
           "subresource": {
             "blockable": "*",
             "optionally-blockable": []
           },
-          "origin": ["cross-origin-http", "same-host-http"],
+          "origin": [
+            "cross-http",
+            "same-http"
+          ],
           "expectation": "blocked"
         },
         {
           "name": "no-opt-in-blocks",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": "no-opt-in",
-          "context_nesting": "top-level",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
           "subresource": {
             "blockable": "*",
             "optionally-blockable": []
           },
-          "origin": ["cross-origin-http", "same-host-http"],
+          "origin": [
+            "cross-http",
+            "same-http"
+          ],
           "expectation": "blocked"
         },
         {
           "name": "ws-downgrade-blocks",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": ["no-opt-in", "http-csp", "meta-csp"],
-          "context_nesting": "top-level",
+          "delivery_type": "*",
+          "delivery_value": "*",
           "redirection": "*",
           "subresource": {
             "blockable": "websocket-request",
             "optionally-blockable": []
           },
-          "origin": ["cross-origin-ws", "same-host-ws"],
+          "origin": [
+            "cross-ws",
+            "same-ws"
+          ],
           "expectation": "blocked"
         }
       ]
@@ -96,41 +111,65 @@
           "name": "allowed",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": "*",
-          "context_nesting": "top-level",
-          "redirection": ["no-redirect", "keep-scheme-redirect"],
+          "delivery_type": "*",
+          "delivery_value": "*",
+          "redirection": [
+            "no-redirect",
+            "keep-scheme"
+          ],
           "subresource": {
             "blockable": "*",
             "optionally-blockable": "*"
           },
-          "origin": ["same-host-https"],
+          "origin": [
+            "same-https"
+          ],
           "expectation": "allowed"
         },
         {
           "name": "websocket-allowed",
           "expansion": "default",
           "source_scheme": "https",
-          "opt_in_method": "*",
-          "context_nesting": "top-level",
-          "redirection": ["no-redirect", "keep-scheme-redirect"],
+          "delivery_type": "*",
+          "delivery_value": "*",
+          "redirection": [
+            "no-redirect",
+            "keep-scheme"
+          ],
           "subresource": {
             "blockable": "websocket-request",
             "optionally-blockable": []
           },
-          "origin": ["same-host-wss"],
+          "origin": [
+            "same-wss"
+          ],
           "expectation": "allowed"
         }
       ]
     }
   ],
-
+  "delivery_key": "mixedContent",
   "excluded_tests": [
     {
+      "name": "Skip-redundant-no-opt-in",
+      "expansion": "*",
+      "source_scheme": "*",
+      "delivery_type": "http-rp",
+      "delivery_value": null,
+      "redirection": "*",
+      "subresource": {
+        "blockable": "*",
+        "optionally-blockable": "*"
+      },
+      "origin": "*",
+      "expectation": "*"
+    },
+    {
       "name": "Redundant-subresources",
       "expansion": "*",
       "source_scheme": "*",
-      "opt_in_method": "*",
-      "context_nesting": "*",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
       "subresource": {
         "blockable": [
@@ -145,8 +184,8 @@
       "name": "Skip-origins-not-applicable-to-websockets",
       "expansion": "*",
       "source_scheme": "*",
-      "opt_in_method": "*",
-      "context_nesting": "*",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
       "subresource": {
         "blockable": [
@@ -155,37 +194,23 @@
         "optionally-blockable": []
       },
       "origin": [
-        "same-host-https",
-        "same-host-http",
-        "cross-origin-https",
-        "cross-origin-http"
+        "same-https",
+        "same-http",
+        "cross-https",
+        "cross-http"
       ],
       "expectation": "*"
     },
     {
-      "name": "TODO-opt-in-method-img-cross-origin",
-      "expansion": "*",
-      "source_scheme": "*",
-      "opt_in_method": "img-crossorigin",
-      "context_nesting": "*",
-      "redirection": "*",
-      "subresource": {
-        "blockable": "*",
-        "optionally-blockable": "*"
-      },
-      "origin": "*",
-      "expectation": "*"
-    },
-    {
       "name": "Skip-redundant-for-opt-in-method",
       "expansion": "*",
       "source_scheme": "*",
-      "opt_in_method":  [
-        "meta-csp",
-        "img-crossorigin"
+      "delivery_type": "meta",
+      "delivery_value": "opt-in",
+      "redirection": [
+        "keep-scheme",
+        "swap-scheme"
       ],
-      "context_nesting": "*",
-      "redirection": ["keep-scheme-redirect", "swap-scheme-redirect"],
       "subresource": {
         "blockable": "*",
         "optionally-blockable": "*"
@@ -194,7 +219,6 @@
       "expectation": "*"
     }
   ],
-
   "test_expansion_schema": {
     "expansion": [
       "default",
@@ -204,30 +228,28 @@
       "http",
       "https"
     ],
-    "opt_in_method": [
-      "no-opt-in",
-      "http-csp",
-      "meta-csp",
-      "img-crossorigin"
+    "delivery_type": [
+      "http-rp",
+      "meta"
+    ],
+    "delivery_value": [
+      null,
+      "opt-in"
     ],
     "redirection": [
       "no-redirect",
-      "keep-scheme-redirect",
-      "swap-scheme-redirect"
-    ],
-    "context_nesting": [
-      "top-level",
-      "sub-level"
+      "keep-scheme",
+      "swap-scheme"
     ],
     "origin": [
-      "same-host-https",
-      "same-host-http",
-      "cross-origin-https",
-      "cross-origin-http",
-      "same-host-wss",
-      "same-host-ws",
-      "cross-origin-wss",
-      "cross-origin-ws"
+      "same-https",
+      "same-http",
+      "cross-https",
+      "cross-http",
+      "same-wss",
+      "same-ws",
+      "cross-wss",
+      "cross-ws"
     ],
     "subresource": {
       "blockable": [
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.js
deleted file mode 100644
index 1dbcb2b..0000000
--- a/third_party/blink/web_tests/external/wpt/native-file-system/NativeFileSystemWritableFileStream.tentative.https.window.js
+++ /dev/null
@@ -1,126 +0,0 @@
-// META: script=resources/test-helpers.js
-promise_test(async t => cleanupSandboxedFileSystem(),
-             'Cleanup to setup test environment');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'empty_blob');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, new Blob([]));
-
-  assert_equals(await getFileContents(handle), '');
-  assert_equals(await getFileSize(handle), 0);
-}, 'write() with an empty blob to an empty file');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'valid_blob');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, new Blob(['1234567890']));
-
-  assert_equals(await getFileContents(handle), '1234567890');
-  assert_equals(await getFileSize(handle), 10);
-}, 'write() a blob to an empty file');
-
-promise_test(async t => {
-    const handle = await createEmptyFile(t, 'blob_with_offset');
-    const stream = await handle.createWritable();
-
-    await stream.write(0, new Blob(['1234567890']));
-    await stream.write(4, new Blob(['abc']));
-
-    assert_equals(await getFileContents(handle), '1234abc890');
-    assert_equals(await getFileSize(handle), 10);
-}, 'write() called with a blob and a valid offset');
-
-promise_test(async t => {
-    const handle = await createEmptyFile(t, 'bad_offset');
-    const stream = await handle.createWritable();
-
-    await promise_rejects(t, 'InvalidStateError', stream.write(4, new Blob(['abc'])));
-
-    assert_equals(await getFileContents(handle), '');
-    assert_equals(await getFileSize(handle), 0);
-}, 'write() called with an invalid offset');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'empty_string');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, '');
-  assert_equals(await getFileContents(handle), '');
-  assert_equals(await getFileSize(handle), 0);
-}, 'write() with an empty string to an empty file');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'valid_utf8_string');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, 'foo🤘');
-  assert_equals(await getFileContents(handle), 'foo🤘');
-  assert_equals(await getFileSize(handle), 7);
-}, 'write() with a valid utf-8 string');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'string_with_unix_line_ending');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, 'foo\n');
-  assert_equals(await getFileContents(handle), 'foo\n');
-  assert_equals(await getFileSize(handle), 4);
-}, 'write() with a string with unix line ending preserved');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'string_with_windows_line_ending');
-  const stream = await handle.createWritable();
-
-  await stream.write(0, 'foo\r\n');
-  assert_equals(await getFileContents(handle), 'foo\r\n');
-  assert_equals(await getFileSize(handle), 5);
-}, 'write() with a string with windows line ending preserved');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'empty_array_buffer');
-  const stream = await handle.createWritable();
-
-  let buf = new ArrayBuffer(0);
-  await stream.write(0, buf);
-  assert_equals(await getFileContents(handle), '');
-  assert_equals(await getFileSize(handle), 0);
-}, 'write() with an empty array buffer to an empty file');
-
-promise_test(async t => {
-  const handle = await createEmptyFile(t, 'valid_string_typed_byte_array');
-  const stream = await handle.createWritable();
-
-  let buf = new ArrayBuffer(3);
-  let intView = new Uint8Array(buf);
-  intView[0] = 0x66;
-  intView[1] = 0x6f;
-  intView[2] = 0x6f;
-  await stream.write(0, buf);
-  assert_equals(await getFileContents(handle), 'foo');
-  assert_equals(await getFileSize(handle), 3);
-}, 'write() with a valid typed array buffer');
-
-promise_test(async t => {
-    const handle = await createEmptyFile(t, 'trunc_shrink');
-    const stream = await handle.createWritable();
-
-    await stream.write(0, new Blob(['1234567890']));
-    await stream.truncate(5);
-
-    assert_equals(await getFileContents(handle), '12345');
-    assert_equals(await getFileSize(handle), 5);
-}, 'truncate() to shrink a file');
-
-promise_test(async t => {
-    const handle = await createEmptyFile(t, 'trunc_grow');
-    const stream = await handle.createWritable();
-
-    await stream.write(0, new Blob(['abc']));
-    await stream.truncate(5);
-
-    assert_equals(await getFileContents(handle), 'abc\0\0');
-    assert_equals(await getFileSize(handle), 5);
-}, 'truncate() to grow a file');
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html
index 9d859f3a..b2251d6 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html
@@ -34,58 +34,54 @@
                 var target1 = innerframe.contentDocument.getElementById('target1');
                 innerframe.contentWindow.name = "innerframe";
 
+                on_event(document, "pointerlockchange", function(event) {
+                  if (document.pointerLockElement == target0) {
+                    on_event(target0, "pointermove", function (event) {
+                      test_pointermove.step(function() {
+                        assert_equals(event.view.name, "outerframe", "View attribute of pointermove should be the target frame.");
+                      }, "View attribute of pointermove should be the target frame.");
+                      document.exitPointerLock();
+
+                      on_event(target1, "click", function(event) {
+                        target1.requestPointerLock();
+                      });
+
+                      on_event(innerframe.contentDocument, "pointerlockchange", function(event) {
+                        if (innerframe.contentDocument.pointerLockElement == target1) {
+                          on_event(target1, "pointermove", function (event) {
+                            innerframe_pointermoveReceived = true;
+                            test_pointermove.step(function() {
+                              assert_equals(event.view.name, "innerframe", "View attribute of pointermove should be the target frame.");
+                            }, "View attribute of pointermove should be the target frame.");
+                            innerframe.contentDocument.exitPointerLock();
+                            test_pointermove.done();
+                          });
+                        }
+                      });
+                    });
+                  }
+                });
+
                 on_event(target0, "click", function(event) {
                   target0.requestPointerLock();
                 });
 
-                on_event(target1, "click", function(event) {
-                  target1.requestPointerLock();
-                });
+                var x = innerframe.getBoundingClientRect().x + target1.getBoundingClientRect().x;
+                var y = innerframe.getBoundingClientRect().y + target1.getBoundingClientRect().y;
+                // Inject mouse input
+                new test_driver.Actions()
+                               .pointerMove(5, 5, {origin: target0})
+                               .pointerDown()
+                               .pointerUp()
+                               .pointerMove(100, 300, {origin: target0})
+                               .pointerMove(x+10, y+10)
+                               .pointerDown()
+                               .pointerUp()
+                               .pointerMove(5, 5, {origin: target0})
+                               .send();
 
-                on_event(target0, "pointermove", function (event) {
-                  if (document.pointerLockElement == target0) {
-                    test_pointermove.step(function() {
-                      assert_equals(event.view.name, "outerframe", "View attribute of pointermove should be the target frame.");
-                    }, "View attribute of pointermove should be the target frame.");
-                    document.exitPointerLock();
-                    // Click the inner frame target to lock.
-                    clickInTarget("mouse", target1);
-                  }
-                });
-
-                on_event(target1, "pointermove", function (event) {
-                  if (innerframe.contentDocument.pointerLockElement == target1) {
-                    test_pointermove.step(function() {
-                      assert_equals(event.view.name, "innerframe", "View attribute of pointermove should be the target frame.");
-                    }, "View attribute of pointermove should be the target frame.");
-                    innerframe.contentDocument.exitPointerLock();
-                    test_pointermove.done();
-                  }
-                });
-
-                on_event(document, "pointerlockchange", function(event) {
-                  if (document.pointerLockElement == target0) {
-                    // Send moves in main frame target
-                    new test_driver.Actions()
-                                   .pointerMove(10, 30, {origin: target0})
-                                   .pointerMove(20, 10, {origin: target0})
-                                   .send();
-                  }
-                });
-
-                on_event(innerframe.contentDocument, "pointerlockchange", function(event) {
-                  if (innerframe.contentDocument.pointerLockElement == target1) {
-                    // Send moves in inner frame target
-                    new test_driver.Actions()
-                                   .pointerMove(10, 30, {origin: target1})
-                                   .pointerMove(20, 10, {origin: target1})
-                                   .send();
-                  }
-                });
-
-                // Click the outer frame target to lock.
-                clickInTarget("mouse", target0);
             }
+
         </script>
         <div id="complete-notice">
         </div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/README.md b/third_party/blink/web_tests/external/wpt/referrer-policy/README.md
index 303362d..498f7f2 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/README.md
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/README.md
@@ -30,6 +30,8 @@
 
 The spec can be validated by running ```./generic/tools/spec_validator.py```. This is specially important when you're making changes to  ```spec.src.json```. Make sure it's a valid JSON (no comments or trailing commas). The validator should be informative and very specific on any issues.
 
+The ```spec.src.json``` file can be formatted by running ```../common/security-features/tools/format_spec_src_json.py```.
+
 For details about the spec JSON, see **Overview of the spec JSON** below.
 
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
index b3e3340..98374982 100755
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/generate.py
@@ -3,65 +3,71 @@
 import os
 import sys
 
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'common', 'security-features', 'tools'))
+sys.path.insert(
+    0,
+    os.path.join(
+        os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'common',
+        'security-features', 'tools'))
 import generate
 
+
 class ReferrerPolicyConfig(object):
-  def __init__(self):
-    self.selection_pattern = '%(delivery_method)s/' + \
-                             '%(origin)s/' + \
-                             '%(source_protocol)s-%(target_protocol)s/' + \
-                             '%(subresource)s/' + \
-                             '%(redirection)s/'
+    def __init__(self):
+        self.selection_pattern = '%(delivery_type)s/' + \
+                                 '%(origin)s/' + \
+                                 '%(source_scheme)s/' + \
+                                 '%(subresource)s/' + \
+                                 '%(redirection)s/'
 
-    self.test_file_path_pattern = '%(spec_name)s/' + self.selection_pattern + \
-                                  '%(name)s.%(source_protocol)s.html'
+        self.test_file_path_pattern = '%(spec_name)s/' + self.selection_pattern + \
+                                      '%(name)s.%(source_scheme)s.html'
 
-    self.test_description_template = '''The referrer URL is %(referrer_url)s when a
-document served over %(source_protocol)s requires an %(target_protocol)s
-sub-resource via %(subresource)s using the %(delivery_method)s
+        self.test_description_template = '''The referrer URL is %(expectation)s when a
+document served over %(source_scheme)s requires a
+sub-resource via %(subresource)s using the %(delivery_type)s
 delivery method with %(redirection)s and when
 the target request is %(origin)s.'''
 
-    self.test_page_title_template = 'Referrer-Policy: %s'
+        self.test_page_title_template = 'Referrer-Policy: %s'
 
-    self.helper_js = '/referrer-policy/generic/referrer-policy-test-case.sub.js'
+        self.helper_js = '/referrer-policy/generic/referrer-policy-test-case.sub.js'
 
-    # For debug target only.
-    self.sanity_checker_js = '/referrer-policy/generic/sanity-checker.js'
-    self.spec_json_js = '/referrer-policy/spec_json.js'
+        # For debug target only.
+        self.sanity_checker_js = '/referrer-policy/generic/sanity-checker.js'
+        self.spec_json_js = '/referrer-policy/spec_json.js'
 
-    self.test_case_name = 'ReferrerPolicyTestCase'
+        self.test_case_name = 'ReferrerPolicyTestCase'
 
-    script_directory = os.path.dirname(os.path.abspath(__file__))
-    self.spec_directory = os.path.abspath(os.path.join(script_directory, '..', '..'))
+        script_directory = os.path.dirname(os.path.abspath(__file__))
+        self.spec_directory = os.path.abspath(
+            os.path.join(script_directory, '..', '..'))
 
-  def handleDelivery(self, selection, spec):
-    delivery_method = selection['delivery_method']
-    delivery_value = spec['referrer_policy']
+    def handleDelivery(self, selection, spec):
+        delivery_type = selection['delivery_type']
+        delivery_value = selection['delivery_value']
 
-    meta = ''
-    headers = []
-    if delivery_value != None:
-        if delivery_method == 'meta-referrer':
-            meta = \
-                '<meta name="referrer" content="%s">' % delivery_value
-        elif delivery_method == 'http-rp':
-            meta = \
-                "<!-- No meta: Referrer policy delivered via HTTP headers. -->"
-            headers.append('Referrer-Policy: ' + '%s' % delivery_value)
-            # TODO(kristijanburnik): Limit to WPT origins.
-            headers.append('Access-Control-Allow-Origin: *')
-        elif delivery_method == 'attr-referrer':
-            # attr-referrer is supported by the JS test wrapper.
-            pass
-        elif delivery_method == 'rel-noreferrer':
-            # rel=noreferrer is supported by the JS test wrapper.
-            pass
-        else:
-            raise ValueError('Not implemented delivery_method: ' \
-                              + delivery_method)
-    return {"meta": meta, "headers": headers}
+        meta = ''
+        headers = []
+        if delivery_value != None:
+            if delivery_type == 'meta':
+                meta = \
+                    '<meta name="referrer" content="%s">' % delivery_value
+            elif delivery_type == 'http-rp':
+                meta = \
+                    "<!-- No meta: Referrer policy delivered via HTTP headers. -->"
+                headers.append('Referrer-Policy: ' + '%s' % delivery_value)
+                # TODO(kristijanburnik): Limit to WPT origins.
+                headers.append('Access-Control-Allow-Origin: *')
+            elif delivery_type == 'attr':
+                # attr-referrer is supported by the JS test wrapper.
+                pass
+            elif delivery_type == 'rel-noref':
+                # rel=noreferrer is supported by the JS test wrapper.
+                pass
+            else:
+                raise ValueError('Not implemented delivery_type: ' \
+                                  + delivery_type)
+        return {"meta": meta, "headers": headers}
 
 
 if __name__ == '__main__':
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/spec_validator.py b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/spec_validator.py
deleted file mode 100755
index 70656db..0000000
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/tools/spec_validator.py
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
-
-import json, sys
-from common_paths import *
-
-def assert_non_empty_string(obj, field):
-    assert field in obj, 'Missing field "%s"' % field
-    assert isinstance(obj[field], basestring), \
-        'Field "%s" must be a string' % field
-    assert len(obj[field]) > 0, 'Field "%s" must not be empty' % field
-
-
-def assert_non_empty_list(obj, field):
-    assert isinstance(obj[field], list), \
-        '%s must be a list' % field
-    assert len(obj[field]) > 0, \
-        '%s list must not be empty' % field
-
-
-def assert_non_empty_dict(obj, field):
-    assert isinstance(obj[field], dict), \
-        '%s must be a dict' % field
-    assert len(obj[field]) > 0, \
-        '%s dict must not be empty' % field
-
-
-def assert_contains(obj, field):
-    assert field in obj, 'Must contain field "%s"' % field
-
-
-def assert_value_from(obj, field, items):
-   assert obj[field] in items, \
-        'Field "%s" must be from: %s' % (field, str(items))
-
-
-def assert_atom_or_list_items_from(obj, field, items):
-    if isinstance(obj[field], basestring) or isinstance(obj[field], int):
-        assert_value_from(obj, field, items)
-        return
-
-    assert isinstance(obj[field], list), '%s must be a list' % field
-    for allowed_value in obj[field]:
-        assert allowed_value != '*', "Wildcard is not supported for lists!"
-        assert allowed_value in items, \
-            'Field "%s" must be from: %s' % (field, str(items))
-
-
-def assert_contains_only_fields(obj, expected_fields):
-    for expected_field in expected_fields:
-        assert_contains(obj, expected_field)
-
-    for actual_field in obj:
-        assert actual_field in expected_fields, \
-                'Unexpected field "%s".' % actual_field
-
-
-def assert_value_unique_in(value, used_values):
-    assert value not in used_values, 'Duplicate value "%s"!' % str(value)
-    used_values[value] = True
-
-
-def assert_valid_artifact(exp_pattern, artifact_key, schema):
-    if isinstance(schema, list):
-        assert_atom_or_list_items_from(exp_pattern, artifact_key,
-                                       ["*"] + schema)
-        return
-
-    for sub_artifact_key, sub_schema in schema.iteritems():
-        assert_valid_artifact(exp_pattern[artifact_key], sub_artifact_key,
-                              sub_schema)
-
-def validate(spec_json, details):
-    """ Validates the json specification for generating tests. """
-
-    details['object'] = spec_json
-    assert_contains_only_fields(spec_json, ["specification",
-                                            "referrer_policy_schema",
-                                            "test_expansion_schema",
-                                            "excluded_tests"])
-    assert_non_empty_list(spec_json, "specification")
-    assert_non_empty_list(spec_json, "referrer_policy_schema")
-    assert_non_empty_dict(spec_json, "test_expansion_schema")
-    assert_non_empty_list(spec_json, "excluded_tests")
-
-    specification = spec_json['specification']
-    referrer_policy_schema = spec_json['referrer_policy_schema']
-    test_expansion_schema = spec_json['test_expansion_schema']
-    excluded_tests = spec_json['excluded_tests']
-
-    valid_test_expansion_fields = ['name'] + test_expansion_schema.keys()
-
-    # Validate each single spec.
-    for spec in specification:
-        details['object'] = spec
-
-        # Validate required fields for a single spec.
-        assert_contains_only_fields(spec, ['name',
-                                           'title',
-                                           'description',
-                                           'referrer_policy',
-                                           'specification_url',
-                                           'test_expansion'])
-        assert_non_empty_string(spec, 'name')
-        assert_non_empty_string(spec, 'title')
-        assert_non_empty_string(spec, 'description')
-        assert_non_empty_string(spec, 'specification_url')
-        assert_value_from(spec, 'referrer_policy', referrer_policy_schema)
-        assert_non_empty_list(spec, 'test_expansion')
-
-        # Validate spec's test expansion.
-        used_spec_names = {}
-
-        for spec_exp in spec['test_expansion']:
-            details['object'] = spec_exp
-            assert_non_empty_string(spec_exp, 'name')
-            # The name is unique in same expansion group.
-            assert_value_unique_in((spec_exp['expansion'], spec_exp['name']),
-                                   used_spec_names)
-            assert_contains_only_fields(spec_exp, valid_test_expansion_fields)
-
-            for artifact in test_expansion_schema:
-                details['test_expansion_field'] = artifact
-                assert_valid_artifact(spec_exp, artifact,
-                                      test_expansion_schema[artifact])
-                del details['test_expansion_field']
-
-    # Validate the test_expansion schema members.
-    details['object'] = test_expansion_schema
-    assert_contains_only_fields(test_expansion_schema, ['expansion',
-                                                        'delivery_method',
-                                                        'redirection',
-                                                        'origin',
-                                                        'source_protocol',
-                                                        'target_protocol',
-                                                        'subresource',
-                                                        'referrer_url'])
-    # Validate excluded tests.
-    details['object'] = excluded_tests
-    for excluded_test_expansion in excluded_tests:
-        assert_contains_only_fields(excluded_test_expansion,
-                                    valid_test_expansion_fields)
-        details['object'] = excluded_test_expansion
-        for artifact in test_expansion_schema:
-            details['test_expansion_field'] = artifact
-            assert_valid_artifact(
-                excluded_test_expansion,
-                artifact,
-                test_expansion_schema[artifact])
-            del details['test_expansion_field']
-
-    del details['object']
-
-
-def assert_valid_spec_json(spec_json):
-    error_details = {}
-    try:
-        validate(spec_json, error_details)
-    except AssertionError as err:
-        print('ERROR:', err.message)
-        print(json.dumps(error_details, indent=4))
-        sys.exit(1)
-
-
-def main():
-    spec_json = load_spec_json();
-    assert_valid_spec_json(spec_json)
-    print("Spec JSON is valid.")
-
-
-if __name__ == '__main__':
-    main()
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json b/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
index 5f0247b..f69c289 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/spec.src.json
@@ -5,51 +5,62 @@
       "title": "Referrer Policy is not explicitly defined",
       "description": "Check that referrer URL follows no-referrer-when-downgrade policy when no explicit Referrer Policy is set.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policies",
-      "referrer_policy": null,
       "test_expansion": [
         {
           "name": "insecure-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "upgrade-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "downgrade-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         },
         {
           "name": "secure-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": null,
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         }
       ]
     },
@@ -58,18 +69,17 @@
       "title": "Referrer Policy is set to 'no-referrer'",
       "description": "Check that sub-resource never gets the referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer",
-      "referrer_policy": "no-referrer",
       "test_expansion": [
         {
           "name": "generic",
           "expansion": "default",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "no-referrer",
           "redirection": "*",
           "origin": "*",
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         }
       ]
     },
@@ -78,51 +88,62 @@
       "title": "Referrer Policy is set to 'no-referrer-when-downgrade'",
       "description": "Check that non a priori insecure subresource gets the full Referrer URL. A priori insecure subresource gets no referrer information.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-no-referrer-when-downgrade",
-      "referrer_policy": "no-referrer-when-downgrade",
       "test_expansion": [
         {
           "name": "insecure-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "upgrade-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "downgrade-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         },
         {
           "name": "secure-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "no-referrer-when-downgrade",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         }
       ]
     },
@@ -131,18 +152,17 @@
       "title": "Referrer Policy is set to 'origin'",
       "description": "Check that all subresources in all casses get only the origin portion of the referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin",
-      "referrer_policy": "origin",
       "test_expansion": [
         {
           "name": "generic",
           "expansion": "default",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "origin",
           "redirection": "*",
           "origin": "*",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         }
       ]
     },
@@ -151,51 +171,56 @@
       "title": "Referrer Policy is set to 'same-origin'",
       "description": "Check that cross-origin subresources get no referrer information and same-origin get the stripped referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-same-origin",
-      "referrer_policy": "same-origin",
       "test_expansion": [
         {
           "name": "same-origin-insecure",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "same-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-http",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-origin-secure-default",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "same-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-https",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-origin-insecure",
           "expansion": "override",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
-          "redirection": "swap-origin-redirect",
-          "origin": "same-origin",
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "same-origin",
+          "redirection": "swap-origin",
+          "origin": [
+            "same-http",
+            "same-https"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         },
         {
           "name": "cross-origin",
           "expansion": "default",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "same-origin",
           "redirection": "*",
-          "origin": "cross-origin",
+          "origin": [
+            "cross-http",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         }
       ]
     },
@@ -204,73 +229,78 @@
       "title": "Referrer Policy is set to 'origin-when-cross-origin'",
       "description": "Check that cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-origin-when-cross-origin",
-      "referrer_policy": "origin-when-cross-origin",
       "test_expansion": [
         {
           "name": "same-origin-insecure",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-http",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-origin-secure-default",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-https",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-origin-upgrade",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-https",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "same-origin-downgrade",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-http",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "same-origin-insecure",
           "expansion": "override",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
-          "redirection": "swap-origin-redirect",
-          "origin": "same-origin",
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
+          "redirection": "swap-origin",
+          "origin": [
+            "same-http",
+            "same-https"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "cross-origin",
           "expansion": "default",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "origin-when-cross-origin",
           "redirection": "*",
-          "origin": "cross-origin",
+          "origin": [
+            "cross-http",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         }
       ]
     },
@@ -279,51 +309,62 @@
       "title": "Referrer Policy is set to 'strict-origin'",
       "description": "Check that non a priori insecure subresource gets only the origin portion of the referrer URL. A priori insecure subresource gets no referrer information.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin",
-      "referrer_policy": "strict-origin",
       "test_expansion": [
         {
           "name": "insecure-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "upgrade-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "downgrade-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         },
         {
           "name": "secure-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         }
       ]
     },
@@ -332,95 +373,100 @@
       "title": "Referrer Policy is set to 'strict-origin-when-cross-origin'",
       "description": "Check that a priori insecure subresource gets no referrer information. Otherwise, cross-origin subresources get the origin portion of the referrer URL and same-origin get the stripped referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin",
-      "referrer_policy": "strict-origin-when-cross-origin",
       "test_expansion": [
         {
           "name": "same-insecure",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-http",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-insecure",
           "expansion": "override",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
-          "redirection": "swap-origin-redirect",
-          "origin": "same-origin",
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
+          "redirection": "swap-origin",
+          "origin": "same-http",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "cross-insecure",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "cross-origin",
+          "origin": "cross-http",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "upgrade-protocol",
           "expansion": "default",
-          "source_protocol": "http",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "http",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-https",
+            "cross-https"
+          ],
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "downgrade-protocol",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "http",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "*",
+          "origin": [
+            "same-http",
+            "cross-http"
+          ],
           "subresource": "*",
-          "referrer_url": "omitted"
+          "expectation": "omitted"
         },
         {
           "name": "same-secure",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "same-origin",
+          "origin": "same-https",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         },
         {
           "name": "same-secure",
           "expansion": "override",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
-          "redirection": "swap-origin-redirect",
-          "origin": "same-origin",
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
+          "redirection": "swap-origin",
+          "origin": "same-https",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         },
         {
           "name": "cross-secure",
           "expansion": "default",
-          "source_protocol": "https",
-          "target_protocol": "https",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "https",
+          "delivery_type": "*",
+          "delivery_value": "strict-origin-when-cross-origin",
           "redirection": "*",
-          "origin": "cross-origin",
+          "origin": "cross-https",
           "subresource": "*",
-          "referrer_url": "origin"
+          "expectation": "origin"
         }
       ]
     },
@@ -429,199 +475,185 @@
       "title": "Referrer Policy is set to 'unsafe-url'",
       "description": "Check that all sub-resources get the stripped referrer URL.",
       "specification_url": "https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-unsafe-url",
-      "referrer_policy": "unsafe-url",
       "test_expansion": [
         {
           "name": "generic",
           "expansion": "default",
-          "source_protocol": "*",
-          "target_protocol": "*",
-          "delivery_method": ["http-rp", "meta-referrer", "attr-referrer"],
+          "source_scheme": "*",
+          "delivery_type": "*",
+          "delivery_value": "unsafe-url",
           "redirection": "*",
           "origin": "*",
           "subresource": "*",
-          "referrer_url": "stripped-referrer"
+          "expectation": "stripped-referrer"
         }
       ]
     }
   ],
-
-  "excluded_tests":[
+  "delivery_key": "referrerPolicy",
+  "excluded_tests": [
     {
       "name": "cross-origin-workers",
       "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
+      "source_scheme": "*",
       "redirection": "*",
-      "delivery_method": "*",
-      "origin": "cross-origin",
+      "delivery_type": "*",
+      "delivery_value": "*",
+      "origin": [
+        "cross-http",
+        "cross-https"
+      ],
       "subresource": [
         "worker-request",
         "module-worker",
         "shared-worker"
       ],
-      "referrer_url": "*"
+      "expectation": "*"
     },
     {
       "name": "upgraded-protocol-workers",
       "expansion": "*",
-      "source_protocol": "http",
-      "target_protocol": "https",
-      "delivery_method": "*",
+      "source_scheme": "http",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
-      "origin": "*",
+      "origin": [
+        "same-https",
+        "cross-https"
+      ],
       "subresource": [
         "worker-request",
         "module-worker",
         "shared-worker"
       ],
-      "referrer_url": "*"
+      "expectation": "*"
     },
     {
       "name": "mixed-content-insecure-subresources",
       "expansion": "*",
-      "source_protocol": "https",
-      "target_protocol": "http",
-      "delivery_method": "*",
+      "source_scheme": "https",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
-      "origin": "*",
+      "origin": [
+        "same-http",
+        "cross-http"
+      ],
       "subresource": "*",
-      "referrer_url": "*"
-    },
-    {
-      "name": "elements-not-supporting-attr-referrer",
-      "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
-      "delivery_method": ["attr-referrer"],
-      "redirection": "*",
-      "origin": "*",
-      "subresource": [
-        "xhr-request",
-        "worker-request",
-        "module-worker",
-        "shared-worker",
-        "fetch-request"
-      ],
-      "referrer_url": "*"
-    },
-    {
-      "name": "elements-not-supporting-rel-noreferrer",
-      "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
-      "delivery_method": ["rel-noreferrer"],
-      "redirection": "*",
-      "origin": "*",
-      "subresource": [
-        "iframe-tag",
-        "img-tag",
-        "script-tag",
-        "xhr-request",
-        "worker-request",
-        "module-worker",
-        "shared-worker",
-        "fetch-request",
-        "area-tag"
-      ],
-      "referrer_url": "*"
+      "expectation": "*"
     },
     {
       "name": "area-tag",
       "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
-      "delivery_method": "*",
+      "source_scheme": "*",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
       "origin": "*",
       "subresource": "area-tag",
-      "referrer_url": "*"
+      "expectation": "*"
     },
     {
       "name": "worker-requests-with-swap-origin-redirect",
       "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
-      "delivery_method": "*",
-      "redirection": "swap-origin-redirect",
+      "source_scheme": "*",
+      "delivery_type": "*",
+      "delivery_value": "*",
+      "redirection": "swap-origin",
       "origin": "*",
       "subresource": [
         "worker-request",
         "module-worker",
         "shared-worker"
       ],
-      "referrer_url": "*"
+      "expectation": "*"
     },
     {
       "name": "overhead-for-redirection",
       "expansion": "*",
-      "source_protocol": "*",
-      "target_protocol": "*",
-      "delivery_method": "*",
-      "redirection": ["keep-origin-redirect", "swap-origin-redirect"],
+      "source_scheme": "*",
+      "delivery_type": "*",
+      "delivery_value": "*",
+      "redirection": [
+        "keep-origin",
+        "swap-origin"
+      ],
       "origin": "*",
-      "subresource": ["a-tag", "area-tag"],
-      "referrer_url": "*"
+      "subresource": [
+        "a-tag",
+        "area-tag"
+      ],
+      "expectation": "*"
     },
     {
       "name": "source-https-unsupported-by-web-platform-tests-runners",
       "expansion": "*",
-      "source_protocol": "https",
-      "target_protocol": "*",
-      "delivery_method": "*",
+      "source_scheme": "https",
+      "delivery_type": "*",
+      "delivery_value": "*",
       "redirection": "*",
       "origin": "*",
       "subresource": "*",
-      "referrer_url": "*"
+      "expectation": "*"
+    },
+    {
+      "name": "<link rel=noreferrer>'s delivery_value should be no-referrer",
+      "expansion": "*",
+      "source_scheme": "*",
+      "delivery_type": "rel-noref",
+      "delivery_value": [
+        null,
+        "no-referrer-when-downgrade",
+        "same-origin",
+        "origin",
+        "origin-when-cross-origin",
+        "strict-origin",
+        "strict-origin-when-cross-origin",
+        "unsafe-url"
+      ],
+      "redirection": "*",
+      "origin": "*",
+      "subresource": "*",
+      "expectation": "*"
     }
   ],
-
-  "referrer_policy_schema": [
-    null,
-    "no-referrer",
-    "no-referrer-when-downgrade",
-    "same-origin",
-    "origin",
-    "origin-when-cross-origin",
-    "strict-origin",
-    "strict-origin-when-cross-origin",
-    "unsafe-url"
-  ],
-
   "test_expansion_schema": {
     "expansion": [
       "default",
       "override"
     ],
-
-    "delivery_method": [
+    "delivery_type": [
+      "attr",
+      "rel-noref",
       "http-rp",
-      "meta-referrer",
-      "attr-referrer",
-      "rel-noreferrer"
+      "meta"
     ],
-
-    "origin": [
+    "delivery_value": [
+      null,
+      "no-referrer",
+      "no-referrer-when-downgrade",
       "same-origin",
-      "cross-origin"
+      "origin",
+      "origin-when-cross-origin",
+      "strict-origin",
+      "strict-origin-when-cross-origin",
+      "unsafe-url"
     ],
-
-    "source_protocol": [
+    "origin": [
+      "same-http",
+      "same-https",
+      "cross-http",
+      "cross-https"
+    ],
+    "source_scheme": [
       "http",
       "https"
     ],
-
-    "target_protocol": [
-      "http",
-      "https"
-    ],
-
     "redirection": [
       "no-redirect",
-      "keep-origin-redirect",
-      "swap-origin-redirect"
+      "keep-origin",
+      "swap-origin"
     ],
-
     "subresource": [
       "iframe-tag",
       "img-tag",
@@ -634,8 +666,7 @@
       "shared-worker",
       "fetch-request"
     ],
-
-    "referrer_url": [
+    "expectation": [
       "omitted",
       "origin",
       "stripped-referrer"
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.html b/third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detected-boundingBox-read-only.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLCanvasElement.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLImageElement.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-HTMLVideoElement.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageBitmap.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData-detached.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData-detached.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData-detached.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData-detached.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-ImageData.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-getSupportedFormats.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.worker.js b/third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.https.worker.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.worker.js
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-on-worker.https.worker.js
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-options.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-options.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-options.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-options.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.html b/third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detection-security-test.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.html b/third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/detector-same-object.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any-expected.txt
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt
rename to third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any-expected.txt
index 3eeafa7..4e09e548 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 53 tests; 50 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 53 tests; 52 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS FaceDetector interface: existence and properties of interface object
 PASS FaceDetector interface object length
@@ -12,9 +12,7 @@
 PASS Stringification of faceDetector
 PASS FaceDetector interface: faceDetector must inherit property "detect(ImageBitmapSource)" with the proper type
 PASS FaceDetector interface: calling detect(ImageBitmapSource) on faceDetector with too few arguments must throw TypeError
-FAIL DetectedFace interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
-                new interface_object();
-            }" did not throw
+PASS DetectedFace interface: existence and properties of interface object
 PASS DetectedFace interface object length
 PASS DetectedFace interface object name
 PASS DetectedFace interface: existence and properties of interface prototype object
@@ -39,9 +37,7 @@
 PASS BarcodeDetector interface: barcodeDetector must inherit property "getSupportedFormats()" with the proper type
 PASS BarcodeDetector interface: barcodeDetector must inherit property "detect(ImageBitmapSource)" with the proper type
 PASS BarcodeDetector interface: calling detect(ImageBitmapSource) on barcodeDetector with too few arguments must throw TypeError
-FAIL DetectedBarcode interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
-                new interface_object();
-            }" did not throw
+PASS DetectedBarcode interface: existence and properties of interface object
 PASS DetectedBarcode interface object length
 PASS DetectedBarcode interface object name
 PASS DetectedBarcode interface: existence and properties of interface prototype object
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.js
rename to third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any.worker-expected.txt
similarity index 84%
rename from third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt
rename to third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any.worker-expected.txt
index 58b8ab3..e27f6fa 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/idlharness.https.any.worker-expected.txt
@@ -11,8 +11,8 @@
 PASS Stringification of faceDetector
 PASS FaceDetector interface: faceDetector must inherit property "detect(ImageBitmapSource)" with the proper type
 PASS FaceDetector interface: calling detect(ImageBitmapSource) on faceDetector with too few arguments must throw TypeError
-PASS DetectedFace interface: existence and properties of interface object
-FAIL DetectedFace must be primary interface of detectedFace assert_own_property: self does not have own property "DetectedFace" expected property "DetectedFace" missing
+FAIL DetectedFace interface: existence and properties of interface object assert_false: expected false got true
+PASS DetectedFace must be primary interface of detectedFace
 PASS Stringification of detectedFace
 FAIL DetectedFace interface: detectedFace must not have property "boundingBox" assert_false: expected false got true
 FAIL DetectedFace interface: detectedFace must not have property "landmarks" assert_false: expected false got true
@@ -29,8 +29,8 @@
 PASS BarcodeDetector interface: barcodeDetector must inherit property "getSupportedFormats()" with the proper type
 PASS BarcodeDetector interface: barcodeDetector must inherit property "detect(ImageBitmapSource)" with the proper type
 PASS BarcodeDetector interface: calling detect(ImageBitmapSource) on barcodeDetector with too few arguments must throw TypeError
-PASS DetectedBarcode interface: existence and properties of interface object
-FAIL DetectedBarcode must be primary interface of detectedBarcode assert_own_property: self does not have own property "DetectedBarcode" expected property "DetectedBarcode" missing
+FAIL DetectedBarcode interface: existence and properties of interface object assert_false: expected false got true
+PASS DetectedBarcode must be primary interface of detectedBarcode
 PASS Stringification of detectedBarcode
 FAIL DetectedBarcode interface: detectedBarcode must not have property "boundingBox" assert_false: expected false got true
 FAIL DetectedBarcode interface: detectedBarcode must not have property "rawValue" assert_false: expected false got true
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.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-cross-origin.sub.https.html
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.html b/third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.html
rename to third_party/blink/web_tests/external/wpt/shape-detection/shapedetection-empty-input.https.html
diff --git a/third_party/blink/web_tests/external/wpt/tools/requirements_flake8.txt b/third_party/blink/web_tests/external/wpt/tools/requirements_flake8.txt
index 480aacd4..d73e7c3d 100644
--- a/third_party/blink/web_tests/external/wpt/tools/requirements_flake8.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/requirements_flake8.txt
@@ -1,4 +1,4 @@
-flake8==3.7.7
+flake8==3.7.8
 pycodestyle==2.5.0
 pyflakes==2.1.1
 pep8-naming==0.8.2
diff --git a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
index 8f1d67cd..c1bf4bc0 100644
--- a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
@@ -1,3 +1,3 @@
-mypy==0.711
+mypy==0.720
 mypy-extensions==0.4.1
 typed-ast==1.4.0
diff --git a/third_party/blink/web_tests/external/wpt/tools/tox.ini b/third_party/blink/web_tests/external/wpt/tools/tox.ini
index ba6dc03..63fb8ed9 100644
--- a/third_party/blink/web_tests/external/wpt/tools/tox.ini
+++ b/third_party/blink/web_tests/external/wpt/tools/tox.ini
@@ -13,6 +13,7 @@
 
 passenv =
   HYPOTHESIS_PROFILE
+  PY_COLORS
 
 [testenv:py27-flake8]
 deps = -rrequirements_flake8.txt
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/run.py b/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
index 6acc5b8e..d5d7e7a2 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
@@ -661,7 +661,7 @@
 
 if __name__ == "__main__":
     import pdb
-    from tools import localpaths  # noqa: flake8
+    from tools import localpaths  # noqa: F401
     try:
         main()
     except Exception:
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/wpt.py b/third_party/blink/web_tests/external/wpt/tools/wpt/wpt.py
index 4130e1e..93301dd 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/wpt.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/wpt.py
@@ -4,7 +4,7 @@
 import os
 import sys
 
-from tools import localpaths  # noqa: flake8
+from tools import localpaths  # noqa: F401
 
 from six import iteritems
 from . import virtualenv
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
index e25eec3..d6c7a4f 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
@@ -2,7 +2,7 @@
 mozinfo==1.1.0
 mozlog==4.2.0
 mozdebug==0.1.1
-pillow==6.0.0
+pillow==6.1.0
 urllib3[secure]==1.25.3
 requests==2.22.0
 six==1.12.0
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/environment.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/environment.py
index 2493f1f..9244092 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/environment.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/environment.py
@@ -14,7 +14,7 @@
 repo_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir, os.pardir))
 
 sys.path.insert(0, repo_root)
-from tools import localpaths  # noqa: flake8
+from tools import localpaths  # noqa: F401
 
 from wptserve.handlers import StringHandler
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/update/update.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/update/update.py
index a0f4237..0bcaf82 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/update/update.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/update/update.py
@@ -10,7 +10,7 @@
 
 def setup_paths(sync_path):
     sys.path.insert(0, os.path.abspath(sync_path))
-    from tools import localpaths  # noqa: flake8
+    from tools import localpaths  # noqa: F401
 
 class LoadConfig(Step):
     """Step for loading configuration from the ini file and kwargs."""
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
index 3074895..8dda8b2 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html
@@ -6,20 +6,24 @@
 </head>
 <body>
   <script>
-  // CSP insists the "trusted-types: ..." directives are deliverd as headers
+  // CSP insists the "trusted-types: ..." directives are delivered as headers
   // (rather than as "<meta http-equiv" tags). This test assumes the following
   // headers are set in the .headers file:
   //
   //   Content-Security-Policy: trusted-types one
   //   Content-Security-Policy-Report-Only: trusted-types two; report-uri ...
   //   Content-Security-Policy: plugin-types bla/blubb
+  //   Content-Security-Policy: default-src * 'unsafe-inline'
   //
-  // The last rule is there so we can provoke a CSP violation report at will.
+  // The third rule is there so we can provoke a CSP violation report at will.
   // The intent is that in order to test that a violation has *not* been thrown
   // (and without resorting to abominations like timeouts), we force a *another*
-  // CSP violation (by violating the img-src rule) and when that event is
+  // CSP violation (by violating the plugin-types rule) and when that event is
   // processed we can we sure that an earlier event - if it indeed occurred -
   // must have already been processed.
+  //
+  // The last rule allows all scripting except 'unsafe-eval', so we can also
+  // test reporting of this case.
 
   const url = "" + document.location;
 
@@ -72,7 +76,6 @@
     });
   }
 
-
   promise_test(t => {
     let p = Promise.resolve()
         .then(promise_violation("trusted-types one"))
@@ -134,10 +137,89 @@
     return p;
   }, "Trusted Type violation report: assign trusted HTML to html; no report");
 
+  // Test the "sample" field of the event.
+  // TODO(vogelheim): The current set of tests allows for more variance in the
+  //     sample reports than the current spec draft does. Once the spec has
+  //     been finalized, we should clamp this down to check byte-for-byte
+  //     against the values mandated by the spec.
+
+  function expect_sample(s) { return e => {
+    assert_true(e.sample.includes(s),
+                `expected "${e.sample}" to include "${s}".`);
+    return e;
+  } }
+
+  promise_test(t => {
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types two"))
+        .then(expect_sample("Element.innerHTML"))
+        .then(expect_sample("abc"));
+    expect_throws(_ => { document.getElementById("div").innerHTML = "abc" });
+    return p;
+  }, "Trusted Type violation report: sample for .innerHTML assignment");
+
+  promise_test(t => {
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types two"))
+        .then(expect_sample("HTMLAnchorElement.href"));
+      expect_throws(_ => { document.getElementById("anchor").href = "" });
+    return p;
+  }, "Trusted Type violation report: sample for .href assignment");
+
+  promise_test(t => {
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types two"))
+        .then(expect_sample("HTMLElement.innerText"))
+        .then(expect_sample("2+2;"));
+    expect_throws(_ => document.getElementById("script").innerText = "2+2;");
+    return p;
+  }, "Trusted Type violation report: sample for script innerText assignment");
+
+  promise_test(t => {
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types one"))
+        .then(expect_sample("eval"))
+        .then(expect_sample("2+2"))
+        .then(promise_flush());
+    expect_throws(_ => eval("2+2"));
+    flush();
+    return p;
+  }, "Trusted Type violation report: sample for eval");
+
+  promise_test(t => {
+    // We expect the sample string to always contain the name, and at least the
+    // start of the value, but it should not be excessively long.
+    let p = Promise.resolve()
+        .then(promise_violation("trusted-types two"))
+        .then(expect_sample("HTMLElement.innerText"))
+        .then(expect_sample("abbb"))
+        .then(e => assert_less_than(e.sample.length, 150));
+    const value = "a" + "b".repeat(50000);
+    expect_throws(_ => document.getElementById("script").innerText = value);
+    return p;
+  }, "Trusted Type violation report: large values should be handled sanely.");
+
+  // Test reporting for Custom Elements (where supported). The report should
+  // refer to the DOM elements being modified, so that Custom Elements cannot
+  // "mask" the underlying DOM mechanism (for reporting).
+  if (customElements) {
+    class CustomLink extends HTMLAnchorElement {};
+    customElements.define("custom-link", CustomLink, { extends: "a" });
+
+    promise_test(t => {
+      let p = Promise.resolve()
+          .then(promise_violation("trusted-types one"))
+          .then(expect_sample("HTMLAnchorElement.href"))
+          .then(expect_sample("abc"));
+      expect_throws(_ => document.getElementById("customlink").href = "abc");
+      return p;
+    }, "Trusted Type violation report: sample for custom element assignment");
+  }
   </script>
 
   <!-- Some elements for the tests to act on. -->
   <a id="anchor" href="">anchor</a>
   <div id="div"></div>
-
+  <script id="script"></script>
+  <a id="customlink" is="custom-link" href="a"></a>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html.headers b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html.headers
index 8093b84..947a151c 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html.headers
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/trusted-types-reporting.tentative.https.html.headers
@@ -1,4 +1,5 @@
 Content-Security-Policy: trusted-types one
 Content-Security-Policy-Report-Only: trusted-types two; report-uri /content-security-policy/resources/dummy-report.php
 Content-Security-Policy: plugin-types bla/blubb
+Content-Security-Policy: default-src * 'unsafe-inline'
 
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing-expected.txt b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing-expected.txt
deleted file mode 100644
index 511d86a..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL MediaStreamAudioSourceNode captures the right track. assert_true: Other track seem to be routed to the AudioContext? expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html
index 43e5ac8..2e04ab6a 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html
@@ -57,6 +57,8 @@
 
       const twoTrackSource = ac.createMediaStreamSource(twoTrackMediaStream);
       const analyser = ac.createAnalyser();
+      // Don't do smoothing so that the frequency data changes quickly
+      analyser.smoothingTimeConstant = 0;
 
       twoTrackSource.connect(analyser);
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html
new file mode 100644
index 0000000..4dcce451
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="RTCPeerConnection-helper.js"></script>
+<script>
+"use strict";
+
+promise_test(async t => {
+  const pc1 = new RTCPeerConnection();
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc1.close());
+  t.add_cleanup(() => pc2.close());
+
+  pc1.addTransceiver("audio");
+
+  await pc1.setLocalDescription(await pc1.createOffer());
+  pc1.restartIce();
+  await pc2.setRemoteDescription(pc1.localDescription);
+  await pc2.setLocalDescription(await pc2.createAnswer());
+  await pc1.setRemoteDescription(pc2.localDescription);
+  // When the setRemoteDescription() promise above is resolved a task should be
+  // queued to fire the onnegotiationneeded event. Because of this, we should
+  // have time to hook up the event listener *after* awaiting the SRD promise.
+  await new Promise(r => pc1.onnegotiationneeded = r);
+}, "Negotiation needed when returning to stable does not fire too early");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
index 0ec35085..2c0c15c 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
@@ -1,16 +1,16 @@
 This is a testharness.js-based test.
-FAIL restartIce() does not trigger negotiation ahead of initial negotiation promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() has no effect on initial negotiation promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() fires negotiationneeded after initial negotiation promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() causes fresh ufrags promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() works in have-local-offer promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() works in initial have-local-offer promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() works in have-remote-offer promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() does nothing in initial have-remote-offer promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() survives remote offer promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() is satisfied by remote ICE restart promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() trumps {iceRestart: false} promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() survives rollback promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
-FAIL restartIce() survives remote offer containing partial restart promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
+FAIL restartIce() does not trigger negotiation ahead of initial negotiation assert_equals: No negotiationneeded event expected (undefined) undefined but got (object) object "[object Event]"
+PASS restartIce() has no effect on initial negotiation
+PASS restartIce() fires negotiationneeded after initial negotiation
+PASS restartIce() causes fresh ufrags
+PASS restartIce() works in have-local-offer
+PASS restartIce() works in initial have-local-offer
+PASS restartIce() works in have-remote-offer
+PASS restartIce() does nothing in initial have-remote-offer
+PASS restartIce() survives remote offer
+PASS restartIce() is satisfied by remote ICE restart
+PASS restartIce() trumps {iceRestart: false}
+FAIL restartIce() survives rollback promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
+FAIL restartIce() survives remote offer containing partial restart assert_false: Restarted 2 expected false got true
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https.html
index 6b66975f..c069736f 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-restartIce.https.html
@@ -39,6 +39,19 @@
   assert_equals(event, undefined, "No negotiationneeded event");
 }
 
+// In Chromium, assert_equals() produces test expectations with the values
+// compared. Because ufrags are different on each run, this would make Chromium
+// test expectations different on each run on tests that failed when comparing
+// ufrags. To work around this problem, assert_ufrags_equals() and
+// assert_ufrags_not_equals() should be preferred over assert_equals() and
+// assert_not_equals().
+function assert_ufrags_equals(x, y, description) {
+  assert_true(x === y, description);
+}
+function assert_ufrags_not_equals(x, y, description) {
+  assert_false(x === y, description);
+}
+
 promise_test(async t => {
   const pc1 = new RTCPeerConnection();
   const pc2 = new RTCPeerConnection();
@@ -90,21 +103,21 @@
   const [oldUfrag1] = getUfrags(pc1.localDescription);
   const [oldUfrag2] = getUfrags(pc2.localDescription);
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
-  assert_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "control 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "control 2");
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "control 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "control 2");
 
   pc1.restartIce();
   await new Promise(r => pc1.onnegotiationneeded = r);
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
-  assert_equals(getUfrags(pc1.localDescription)[0], newUfrag1, "Unchanged 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], newUfrag2, "Unchanged 2");
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], newUfrag1, "Unchanged 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], newUfrag2, "Unchanged 2");
 }, "restartIce() causes fresh ufrags");
 
 promise_test(async t => {
@@ -124,15 +137,25 @@
   pc1.restartIce();
   await pc2.setRemoteDescription(pc1.localDescription);
   await pc2.setLocalDescription(await pc2.createAnswer());
+  // Several tests in this file initializes the onnegotiationneeded listener
+  // before the setLocalDescription() or setRemoteDescription() that we expect
+  // to trigger negotiation needed. This allows Chrome to exercise these tests
+  // without timing out due to a bug that causes onnegotiationneeded to fire too
+  // early.
+  // TODO(https://crbug.com/985797): Once Chrome does not fire ONN too early,
+  // simply do "await new Promise(...)" instead of
+  // "await negotiationNeededPromise" here and in other tests in this file.
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setRemoteDescription(pc2.localDescription);
-  assert_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "Unchanged 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "Unchanged 2");
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "Unchanged 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "Unchanged 2");
+  await negotiationNeededPromise;
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() works in have-local-offer");
 
@@ -148,15 +171,17 @@
   pc1.restartIce();
   await pc2.setRemoteDescription(pc1.localDescription);
   await pc2.setLocalDescription(await pc2.createAnswer());
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setRemoteDescription(pc2.localDescription);
   const [oldUfrag1] = getUfrags(pc1.localDescription);
   const [oldUfrag2] = getUfrags(pc2.localDescription);
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  await negotiationNeededPromise;
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() works in initial have-local-offer");
 
@@ -177,15 +202,17 @@
   await pc1.setRemoteDescription(pc2.localDescription);
   pc1.restartIce();
   await pc2.setRemoteDescription(await pc1.createAnswer());
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setLocalDescription(pc2.remoteDescription); // End on pc1. No race
-  assert_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "Unchanged 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "Unchanged 2");
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "Unchanged 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "Unchanged 2");
+  await negotiationNeededPromise;
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() works in have-remote-offer");
 
@@ -219,15 +246,17 @@
 
   pc1.restartIce();
   await new Promise(r => pc1.onnegotiationneeded = r);
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await doSignalingHandshakeEndOnSecond(pc2, pc1);
-  assert_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "nothing yet 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "nothing yet 2");
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], oldUfrag1, "nothing yet 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], oldUfrag2, "nothing yet 2");
+  await negotiationNeededPromise;
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag2, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag2, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() survives remote offer");
 
@@ -250,13 +279,13 @@
   await doSignalingHandshakeEndOnSecond(pc2, pc1);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
-  assert_equals(getUfrags(pc1.localDescription)[0], newUfrag1, "Unchanged 1");
-  assert_equals(getUfrags(pc2.localDescription)[0], newUfrag2, "Unchanged 2");
+  assert_ufrags_equals(getUfrags(pc1.localDescription)[0], newUfrag1, "Unchanged 1");
+  assert_ufrags_equals(getUfrags(pc2.localDescription)[0], newUfrag2, "Unchanged 2");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() is satisfied by remote ICE restart");
 
@@ -281,8 +310,8 @@
   await pc1.setRemoteDescription(pc2.localDescription);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() trumps {iceRestart: false}");
 
@@ -302,13 +331,15 @@
   pc1.restartIce();
   await new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setLocalDescription(await pc1.createOffer());
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setLocalDescription({type: "rollback"});
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  await negotiationNeededPromise;
   await doSignalingHandshakeEndOnFirst(pc1, pc2);
   const [newUfrag1] = getUfrags(pc1.localDescription);
   const [newUfrag2] = getUfrags(pc2.localDescription);
-  assert_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
-  assert_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag1, "ufrag 1 changed");
+  assert_ufrags_not_equals(newUfrag1, oldUfrag2, "ufrag 2 changed");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() survives rollback");
 
@@ -340,21 +371,23 @@
     sdp = sdp.replace(getPwds({sdp})[0], oldPwds2[0]);
     const newUfrags2 = getUfrags({sdp});
     const newPwds2 = getPwds({sdp});
-    assert_equals(newUfrags2[0], oldUfrags2[0], "control ufrag match");
-    assert_equals(newPwds2[0], oldPwds2[0], "control pwd match");
-    assert_not_equals(newUfrags2[1], oldUfrags2[1], "control ufrag non-match");
-    assert_not_equals(newPwds2[1], oldPwds2[1], "control pwd non-match");
+    assert_ufrags_equals(newUfrags2[0], oldUfrags2[0], "control ufrag match");
+    assert_ufrags_equals(newPwds2[0], oldPwds2[0], "control pwd match");
+    assert_ufrags_not_equals(newUfrags2[1], oldUfrags2[1], "control ufrag non-match");
+    assert_ufrags_not_equals(newPwds2[1], oldPwds2[1], "control pwd non-match");
     await pc1.setRemoteDescription({type, sdp});
   }
+  const negotiationNeededPromise =
+      new Promise(r => pc1.onnegotiationneeded = r);
   await pc1.setLocalDescription(await pc1.createAnswer());
   const newUfrags1 = getUfrags(pc1.localDescription);
-  assert_equals(newUfrags1[0], oldUfrags1[0], "Unchanged 1");
-  assert_not_equals(newUfrags1[1], oldUfrags1[1], "Restarted 2");
-  await new Promise(r => pc1.onnegotiationneeded = r);
+  assert_ufrags_equals(newUfrags1[0], oldUfrags1[0], "Unchanged 1");
+  assert_ufrags_not_equals(newUfrags1[1], oldUfrags1[1], "Restarted 2");
+  await negotiationNeededPromise;
   await pc1.setLocalDescription(await pc1.createOffer());
   const newestUfrags1 = getUfrags(pc1.localDescription);
-  assert_not_equals(newestUfrags1[0], oldUfrag1[0], "Restarted 1");
-  assert_not_equals(newestUfrags1[1], oldUfrag1[1], "Restarted 2");
+  assert_ufrags_not_equals(newestUfrags1[0], oldUfrags1[0], "Restarted 1");
+  assert_ufrags_not_equals(newestUfrags1[1], oldUfrags1[1], "Restarted 2");
   await assertNoNegotiationNeeded(t, pc1);
 }, "restartIce() survives remote offer containing partial restart");
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 1f26f0d..2671b88 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 508 tests; 468 PASS, 40 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 508 tests; 470 PASS, 38 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 PASS Test driver for asyncInitTransports
@@ -33,7 +33,7 @@
 PASS RTCPeerConnection interface: attribute iceConnectionState
 PASS RTCPeerConnection interface: attribute connectionState
 FAIL RTCPeerConnection interface: attribute canTrickleIceCandidates assert_true: The prototype object must have a property "canTrickleIceCandidates" expected true got false
-FAIL RTCPeerConnection interface: operation restartIce() assert_own_property: interface prototype object missing non-static operation expected property "restartIce" missing
+PASS RTCPeerConnection interface: operation restartIce()
 FAIL RTCPeerConnection interface: operation getDefaultIceServers() assert_own_property: interface object missing static operation expected property "getDefaultIceServers" missing
 PASS RTCPeerConnection interface: operation getConfiguration()
 PASS RTCPeerConnection interface: operation setConfiguration(RTCConfiguration)
@@ -86,7 +86,7 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceConnectionState" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "connectionState" with the proper type
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "canTrickleIceCandidates" with the proper type assert_inherits: property "canTrickleIceCandidates" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type assert_inherits: property "restartIce" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getDefaultIceServers()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getConfiguration()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setConfiguration(RTCConfiguration)" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
index d75e5c7..022aca8c 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
+++ b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
@@ -123,7 +123,7 @@
   callback(window.XRCoordinateSystemEvent, 'XRCoordinateSystemEvent');
 }
 
-// Code for loading test api in chromium.
+// Code for loading test API in Chromium.
 let loadChromiumResources = Promise.resolve().then(() => {
   if (!('MojoInterfaceInterceptor' in self)) {
     // Do nothing on non-Chromium-based browsers or when the Mojo bindings are
@@ -131,26 +131,38 @@
     return;
   }
 
+  let chromiumResources = [
+    '/gen/layout_test_data/mojo/public/js/mojo_bindings.js',
+    '/gen/mojo/public/mojom/base/time.mojom.js',
+    '/gen/gpu/ipc/common/mailbox_holder.mojom.js',
+    '/gen/gpu/ipc/common/sync_token.mojom.js',
+    '/gen/ui/display/mojom/display.mojom.js',
+    '/gen/ui/gfx/geometry/mojo/geometry.mojom.js',
+    '/gen/ui/gfx/mojo/gpu_fence_handle.mojom.js',
+    '/gen/ui/gfx/mojo/transform.mojom.js',
+    '/gen/device/vr/public/mojom/vr_service.mojom.js',
+    '/resources/chromium/webxr-test.js',
+    '/resources/testdriver.js',
+    '/resources/testdriver-vendor.js',
+  ];
+
+  // This infrastructure is also used by Chromium-specific internal tests that
+  // may need additional resources (e.g. internal API extensions), this allows
+  // those tests to rely on this infrastructure while ensuring that no tests
+  // make it into public WPTs that rely on APIs outside of the webxr test API.
+  if (typeof(additionalChromiumResources) !== 'undefined') {
+    chromiumResources = chromiumResources.concat(additionalChromiumResources);
+  }
+
   let chain = Promise.resolve();
-  ['/gen/layout_test_data/mojo/public/js/mojo_bindings.js',
-   '/gen/mojo/public/mojom/base/time.mojom.js',
-   '/gen/gpu/ipc/common/mailbox_holder.mojom.js',
-   '/gen/gpu/ipc/common/sync_token.mojom.js',
-   '/gen/ui/display/mojom/display.mojom.js',
-   '/gen/ui/gfx/geometry/mojo/geometry.mojom.js',
-   '/gen/ui/gfx/mojo/gpu_fence_handle.mojom.js',
-   '/gen/ui/gfx/mojo/transform.mojom.js',
-   '/gen/device/vr/public/mojom/vr_service.mojom.js',
-   '/resources/chromium/webxr-test.js', '/resources/testdriver.js',
-   '/resources/testdriver-vendor.js',
-  ].forEach(path => {
-    let script = document.createElement('script');
-    script.src = path;
-    script.async = false;
-    chain = chain.then(() => new Promise(resolve => {
-                         script.onload = () => resolve();
-                       }));
-    document.head.appendChild(script);
+    chromiumResources.forEach(path => {
+      let script = document.createElement('script');
+      script.src = path;
+      script.async = false;
+      chain = chain.then(() => new Promise(resolve => {
+                           script.onload = () => resolve();
+                         }));
+      document.head.appendChild(script);
   });
 
   return chain;
diff --git a/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html b/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
index a8894fb..641c6761 100644
--- a/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
+++ b/third_party/blink/web_tests/fast/shapedetection/shapedetection-creation.html
@@ -3,12 +3,6 @@
 <script src=../../resources/testharnessreport.js></script>
 <script>
 
-// This test verifies that DetectedText can be created
-test(function() {
-  var detectedText = new DetectedText();
-  assert_true(detectedText instanceof DetectedText);
-}, 'DetectedText instance can be created.');
-
 // This test verifies that TextDetector can be created
 test(function() {
   var textDetector = new TextDetector();
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
index 7816dc4..2649c0c 100644
--- 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
@@ -111,4 +111,36 @@
 
 }, 'index.delete works on invalid ID.');
 
+contentIndexTest(async (t, index) => {
+  // Register an entry.
+  await index.add(createDescription({id: 'my-id'}));
+
+  // Simulate a user deleting it.
+  testRunner.simulateWebContentIndexDelete('my-id');
+
+  const swMessage = await waitForMessageFromServiceWorker();
+  assert_equals(swMessage, 'my-id');
+
+  // Make sure it is actually deleted.
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 0);
+
+}, 'Fire contentdelete event on user intiated content deletion.');
+
+contentIndexTest(async (t, index) => {
+  // Register an entry.
+  await index.add(createDescription({id: 'register-again'}));
+
+  // Simulate a user deleting it.
+  testRunner.simulateWebContentIndexDelete('register-again');
+
+  const swMessage = await waitForMessageFromServiceWorker();
+  assert_equals(swMessage, 'Failed to add description due to I/O error.');
+
+  // Make sure it is actually deleted and no new content was added.
+  const descriptions = await index.getDescriptions();
+  assert_equals(descriptions.length, 0);
+
+}, 'Content cannot be registered while contentdelete event is firing.');
+
 </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
index 500b0e06..5e1777d9 100644
--- a/third_party/blink/web_tests/http/tests/content_index/resources.js
+++ b/third_party/blink/web_tests/http/tests/content_index/resources.js
@@ -4,8 +4,8 @@
 
 'use strict';
 
-const swUrl = '/resources/empty-worker.js';
-const scope = '/resources/';
+const swUrl = 'resources/sw.js';
+const scope = 'resources/';
 
 async function expectTypeErrorWithMessage(promise, message) {
   try {
@@ -34,3 +34,14 @@
     return func(t, registration.index);
   }, description);
 }
+
+async function waitForMessageFromServiceWorker() {
+  return await new Promise(resolve => {
+    const listener = event => {
+      navigator.serviceWorker.removeEventListener('message', listener);
+      resolve(event.data);
+    };
+
+    navigator.serviceWorker.addEventListener('message', listener);
+  });
+}
diff --git a/third_party/blink/web_tests/http/tests/content_index/resources/sw.js b/third_party/blink/web_tests/http/tests/content_index/resources/sw.js
new file mode 100644
index 0000000..66d939bb3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/content_index/resources/sw.js
@@ -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.
+
+async function postMessageToWindow(msg) {
+  for (const client of await clients.matchAll({includeUncontrolled: true}))
+    client.postMessage(msg);
+}
+
+async function reregisterContent() {
+  try {
+    await self.registration.index.add({
+      id: 'new id',
+      title: 'same title',
+      description: 'same description',
+      category: 'article',
+      iconUrl: '/resources/square.png',
+      launchUrl: 'resources/',
+    });
+    await postMessageToWindow('Successfully registered');
+  } catch (e) {
+    await postMessageToWindow(e.message);
+  }
+}
+
+self.addEventListener('contentdelete', event => {
+  if (event.id === 'register-again') {
+    event.waitUntil(reregisterContent());
+    return;
+  }
+
+  event.waitUntil(postMessageToWindow(event.id));
+});
diff --git a/third_party/blink/web_tests/http/tests/cookie-store/delete-on-localhost.html b/third_party/blink/web_tests/http/tests/cookie-store/delete-on-localhost.html
new file mode 100644
index 0000000..f60fd92
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/cookie-store/delete-on-localhost.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- NOTE: On http://localhost, the Cookie Store API is intended to behave the
+same way as document.cookie. -->
+<title>Cookie Store API: Delete a cookie on http://localhost</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+promise_test(async t => {
+  await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+
+  await cookieStore.delete('cookie-name');
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie, null);
+}, 'cookieStore.delete(name) deletes an insecure cookie with the given name');
+
+promise_test(async t => {
+  await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+
+  await cookieStore.delete({ name: 'cookie-name' });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie, null);
+}, `cookieStore.delete(options) deletes an insecure cookie with the given name
+    option`);
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/cookie-store/set-on-localhost.html b/third_party/blink/web_tests/http/tests/cookie-store/set-on-localhost.html
new file mode 100644
index 0000000..d8fffd9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/cookie-store/set-on-localhost.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- NOTE: On http://localhost, the Cookie Store API is intended to behave the
+same way as document.cookie. -->
+<title>Cookie Store API: Set a cookie on http://localhost</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+promise_test(async t => {
+  await cookieStore.delete('cookie-name');
+
+  await cookieStore.set('cookie-name', 'cookie-value');
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.value, 'cookie-value');
+  assert_equals(cookie.secure, false);
+}, 'cookieStore.set(name, value) sets an insecure cookie');
+
+promise_test(async t => {
+  await cookieStore.delete('cookie-name');
+
+  await cookieStore.set({ name: 'cookie-name', value: 'cookie-value' });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.value, 'cookie-value');
+  assert_equals(cookie.secure, false);
+}, 'cookieStore.set(options) with no "secure" option sets an insecure cookie');
+
+promise_test(async t => {
+  await cookieStore.delete('cookie-name');
+
+  await cookieStore.set({
+    name: 'cookie-name', value: 'cookie-value', secure: false
+  });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.value, 'cookie-value');
+  assert_equals(cookie.secure, false);
+}, 'cookieStore.set(options) with "secure: false" sets an insecure cookie');
+
+promise_test(async t => {
+  await promise_rejects(t, new TypeError(),
+      cookieStore.set({
+        name: 'cookie-name', value: 'cookie-value', secure: true
+      }));
+}, 'cookieStore.set(options) with "secure: true" throws an exception');
+
+promise_test(async t => {
+  await cookieStore.delete('cookie-name');
+
+  await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.value, 'cookie-value');
+  assert_equals(cookie.secure, false);
+}, `cookieStore.set(name, value, options) with "secure: false" sets an insecure
+    cookie`);
+
+promise_test(async t => {
+  await cookieStore.delete('cookie-name');
+
+  await promise_rejects(t, new TypeError(),
+      cookieStore.set('cookie-name', 'cookie-value', { secure: true }));
+}, `cookieStore.set(name, value, options) with "secure: true" throws an
+    exception`);
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
index 9aef32f..459cca2 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
@@ -301,7 +301,7 @@
 bypass: notApplicable
 canonical: notApplicable
 color-contrast: notApplicable
-content-width: pass
+content-width: flaky
 critical-request-chains: notApplicable
 custom-controls-labels: manual
 custom-controls-roles: manual
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run.js b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run.js
index 16c06e2..165f135 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run.js
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run.js
@@ -32,6 +32,8 @@
     'uses-responsive-images',
     'uses-text-compression',
     'uses-webp-images',
+    // content shell issues
+    'content-width' // crbug.com/987722
   ];
 
   TestRunner.addResult('Tests that audits panel works.\n');
diff --git a/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript-expected.txt b/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript-expected.txt
index 222485f..dcfa09d0 100644
--- a/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript-expected.txt
@@ -1,5 +1,15 @@
 Tests that JavaScriptSourceSyntaxHighlighter detects the tokens.
 
+1_23.45_67: cm-js-number
+0xDEAD_c0de: cm-js-number
+0o123_456: cm-js-number
+0b1011_1101: cm-js-number
+123_456: cm-js-number
+.123_456e0_1: cm-js-number
+1E+12_34: cm-js-number
+123_456n: cm-js-number
+window._abc: cm-js-variable, *, cm-js-property
+window._for: cm-js-variable, *, cm-js-property
 return'foo';: cm-js-keyword, cm-js-string, *
 /\//g: cm-js-string-2
 //ig';: cm-js-comment
diff --git a/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript.js b/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript.js
index a508bf4d..cc2b29a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript.js
+++ b/third_party/blink/web_tests/http/tests/devtools/syntax-highlight-javascript.js
@@ -9,6 +9,16 @@
     return TestRunner.dumpSyntaxHighlight(str, 'text/javascript');
   }
 
+  dumpSyntaxHighlightJS('1_23.45_67');
+  dumpSyntaxHighlightJS('0xDEAD_c0de');
+  dumpSyntaxHighlightJS('0o123_456');
+  dumpSyntaxHighlightJS('0b1011_1101');
+  dumpSyntaxHighlightJS('123_456');
+  dumpSyntaxHighlightJS('.123_456e0_1');
+  dumpSyntaxHighlightJS('1E+12_34');
+  dumpSyntaxHighlightJS('123_456n');
+  dumpSyntaxHighlightJS('window._abc');
+  dumpSyntaxHighlightJS('window._for');
   dumpSyntaxHighlightJS('return\'foo\';');
   dumpSyntaxHighlightJS('/\\\//g');
   dumpSyntaxHighlightJS('//ig\';');
diff --git a/third_party/blink/web_tests/http/tests/misc/object-image-error-with-onload.html b/third_party/blink/web_tests/http/tests/misc/object-image-error-with-onload.html
index c02914ab..cf8082b 100644
--- a/third_party/blink/web_tests/http/tests/misc/object-image-error-with-onload.html
+++ b/third_party/blink/web_tests/http/tests/misc/object-image-error-with-onload.html
@@ -1,15 +1,22 @@
 <html>
-<script>
-    if (window.testRunner) {
-        testRunner.dumpAsText()
-    }
-</script>
 <body> 
 <p>
 Test for <i><a href="https://bugs.webkit.org/show_bug.cgi?id=19891">Bug https://bugs.webkit.org/show_bug.cgi?id=19891</a></i>
 Broken HTML object elements cause de-reference of pointer to freed memory
 </p>
-<object data="this.object.does.not.exist.gif" codetype="image/gif" height="10"></object>
+<object id=target codetype="image/gif" height="10"></object>
 Pass if there is no crash or ASSERT.
+
+<script>
+const target = document.getElementById("target");
+if (window.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+  target.onload = () => testRunner.notifyDone();
+}
+// Ensure to install an onload handler before setting data to avoid
+// cases where the event is fired before the handler is in place.
+target.data = "this.object.does.not.exist.gif";
+</script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 2595679..75c4cd4f 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -379,6 +379,24 @@
     method constructor
     method contains
     method item
+interface DetectedBarcode
+    attribute @@toStringTag
+    getter boundingBox
+    getter cornerPoints
+    getter format
+    getter rawValue
+    method constructor
+interface DetectedFace
+    attribute @@toStringTag
+    getter boundingBox
+    getter landmarks
+    method constructor
+interface DetectedText
+    attribute @@toStringTag
+    getter boundingBox
+    getter cornerPoints
+    getter rawValue
+    method constructor
 interface ErrorEvent : Event
     attribute @@toStringTag
     getter colno
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
new file mode 100644
index 0000000..4b8c234
--- /dev/null
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 656],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "VerticalScrollbar",
+          "rect": [353, 8, 15, 640],
+          "reason": "scroll control"
+        }
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow (sticky positioned) DIV id='sticky'",
+      "position": [8, 8],
+      "bounds": [345, 20]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html b/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html
new file mode 100644
index 0000000..0b8b7b24
--- /dev/null
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<div id="parent1">
+    <mat id="scroller">
+          <div id="sticky">This should stay sticky</div>
+          <mat id="spacer"></mat>
+    </mat>
+</div>
+
+<style>
+#parent1 {
+    height: 640px;
+    width: 360px;
+    perspective: 10px;
+}
+#scroller {
+    display: block;
+    height: 640px;
+    width: 360px;
+    overflow: hidden auto;
+}
+#sticky {
+    position: sticky;
+    top: 0px;
+}
+#spacer {
+    display: block;
+    height: 2000px;
+}
+</style>
+
+
+<script src="../../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  var scroller = document.getElementById("scroller");
+  // See crbug/983209: keyboard or mouse wheel (not just setting scroll
+  // offset) was required to see this bug.
+  scrollerMidX = scroller.offsetLeft + scroller.offsetWidth / 2;
+  scrollerMidY = scroller.offsetTop + scroller.offsetHeight / 2;
+  const pointerActions = [{
+      source: "touch",
+      actions: [
+        { name: "pointerDown", x: scrollerMidX, y: scrollerMidY },
+        { name: "pointerMove", x: scrollerMidX, y: scrollerMidY - 50 },
+        { name: 'pause', duration: 100},
+        { name: "pointerUp" },
+      ],
+    }];
+  if (window.chrome && chrome.gpuBenchmarking) {
+    chrome.gpuBenchmarking.pointerActionSequence(pointerActions, finishRepaintTest);
+  } else {
+    sticky.innerHTML = "This test requires gpuBenchmarking";
+    finishRepaintTest();
+  }
+}
+window.testIsAsync = true;
+onload = runRepaintTest;
+</script>
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
new file mode 100644
index 0000000..f9942e2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 656],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "VerticalScrollbar",
+          "rect": [353, 8, 15, 640],
+          "reason": "scroll control"
+        }
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow (sticky positioned) DIV id='sticky'",
+      "position": [8, 8],
+      "bounds": [345, 18]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
new file mode 100644
index 0000000..e45f5fa
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
@@ -0,0 +1,75 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 656],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow MAT id='scroller'",
+      "position": [8, 8],
+      "bounds": [360, 640]
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [8, 8],
+      "bounds": [345, 640],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [8, 8],
+      "bounds": [345, 2018],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [8, 8],
+      "bounds": [360, 640],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [353, 8],
+      "bounds": [15, 640],
+      "drawsContent": false,
+      "paintInvalidations": [
+        {
+          "object": "Vertical Scrollbar Layer",
+          "rect": [0, 0, 15, 640],
+          "reason": "full layer"
+        }
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow (sticky positioned) DIV id='sticky'",
+      "position": [8, -27],
+      "bounds": [345, 18]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -35, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/svg/animations/resources/svglength-additive-by-5.svg b/third_party/blink/web_tests/svg/animations/resources/svglength-additive-by-5.svg
deleted file mode 100644
index 365a2f8d..0000000
--- a/third_party/blink/web_tests/svg/animations/resources/svglength-additive-by-5.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-
-<!-- an1: Change width from 10 to 50 in 4s -->
-<!-- an2: Change width from 10 to 100 in 4s starting at 5s -->
-<rect width="10" height="100" fill="green">
-    <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="40" begin="0s" dur="4s"/>
-	<animate id="an2" attributeType="XML" attributeName="width" additive="sum" fill="freeze" by="90" begin="5s" dur="4s"/>
-</rect>
-
-</svg>
diff --git a/third_party/blink/web_tests/svg/animations/script-tests/svglength-additive-by-5.js b/third_party/blink/web_tests/svg/animations/script-tests/svglength-additive-by-5.js
deleted file mode 100644
index f54b32fc..0000000
--- a/third_party/blink/web_tests/svg/animations/script-tests/svglength-additive-by-5.js
+++ /dev/null
@@ -1,53 +0,0 @@
-description("This tests by-animations adding to previous underlying values");
-embedSVGTestCase("resources/svglength-additive-by-5.svg");
-
-// Setup animation test
-function sample1() {
-    shouldBeCloseEnough("rect.width.animVal.value", "10");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function sample2() {
-    shouldBeCloseEnough("rect.width.animVal.value", "30");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function sample3() {
-    shouldBeCloseEnough("rect.width.animVal.value", "50");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function sample4() {
-    shouldBeCloseEnough("rect.width.animVal.value", "10");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function sample5() {
-    shouldBeCloseEnough("rect.width.animVal.value", "55");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function sample6() {
-    shouldBeCloseEnough("rect.width.animVal.value", "100");
-    shouldBe("rect.width.baseVal.value", "10");
-}
-
-function executeTest() {
-    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
-
-    const expectedValues = [
-        // [animationId, time, sampleCallback]
-        ["an1", 0.0,   sample1],
-        ["an1", 2.0,   sample2],
-        ["an1", 3.999, sample3],
-        ["an1", 4.001, sample4],
-        ["an1", 7.0,   sample5],
-        ["an1", 9.0,   sample6],
-        ["an1", 60.0,  sample6]
-    ];
-
-    runAnimationTest(expectedValues);
-}
-
-window.animationStartsImmediately = true;
-var successfullyParsed = true;
diff --git a/third_party/blink/web_tests/svg/animations/svglength-additive-by-5-expected.txt b/third_party/blink/web_tests/svg/animations/svglength-additive-by-5-expected.txt
deleted file mode 100644
index 3bfde665..0000000
--- a/third_party/blink/web_tests/svg/animations/svglength-additive-by-5-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-SVG 1.1 dynamic animation tests
-
-This tests by-animations adding to previous underlying values
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS rect.width.animVal.value is 10
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 30
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 50
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 10
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 55
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 100
-PASS rect.width.baseVal.value is 10
-PASS rect.width.animVal.value is 100
-PASS rect.width.baseVal.value is 10
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/svg/animations/svglength-additive-by-5.html b/third_party/blink/web_tests/svg/animations/svglength-additive-by-5.html
index a65814d..b155c45 100644
--- a/third_party/blink/web_tests/svg/animations/svglength-additive-by-5.html
+++ b/third_party/blink/web_tests/svg/animations/svglength-additive-by-5.html
@@ -1,14 +1,65 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/SVGTestCase.js"></script>
-<script src="resources/SVGAnimationTestCase.js"></script>
-</head>
-<body onload="runSMILTest()">
-<h1>SVG 1.1 dynamic animation tests</h1>
-<p id="description"></p>
-<div id="console"></div>
-<script src="script-tests/svglength-additive-by-5.js"></script>
-</body>
+<title>Test by-animations adding to previous underlying values</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/SVGAnimationTestCase-testharness.js"></script>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<!-- an1: Change width from 10 to 50 in 4s -->
+<!-- an2: Change width from 10 to 100 in 4s starting at 5s -->
+<rect width="10" height="100" fill="green">
+  <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="40" begin="0s" dur="4s"/>
+	<animate id="an2" attributeType="XML" attributeName="width" additive="sum" fill="freeze" by="90" begin="5s" dur="4s"/>
+</rect>
+</svg>
+<script>
+var rootSVGElement = document.querySelector("svg");
+var rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+var epsilon = 0.15;
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.width.animVal.value, 10, epsilon, "1-1");
+    assert_equals(rect.width.baseVal.value, 10, "1-2");
+}
+
+function sample2() {
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon, "2-1");
+    assert_equals(rect.width.baseVal.value, 10, "2-2");
+}
+
+function sample3() {
+    assert_approx_equals(rect.width.animVal.value, 50, epsilon, "3-1");
+    assert_equals(rect.width.baseVal.value, 10, "3-2");
+}
+
+function sample4() {
+    assert_approx_equals(rect.width.animVal.value, 10, epsilon, "4-1");
+    assert_equals(rect.width.baseVal.value, 10, "4-2");
+}
+
+function sample5() {
+    assert_approx_equals(rect.width.animVal.value, 55, epsilon, "5-1");
+    assert_equals(rect.width.baseVal.value, 10, "5-2");
+}
+
+function sample6() {
+    console.log(rootSVGElement.getCurrentTime() + " - 6");
+    assert_approx_equals(rect.width.animVal.value, 100, epsilon, "6-1");
+    assert_equals(rect.width.baseVal.value, 10, "6-2");
+}
+
+smil_async_test(t => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,   sample1],
+        ["an1", 2.0,   sample2],
+        ["an1", 3.999, sample3],
+        ["an1", 4.001, sample4],
+        ["an1", 7.0,   sample5],
+        ["an1", 9.0,   sample6],
+        ["an1", 60.0,  sample6]
+    ];
+    runAnimationTest(t, expectedValues);
+});
+</script>
 </html>
diff --git a/third_party/blink/web_tests/virtual/cache-storage-parallel/external/wpt/service-workers/README.txt b/third_party/blink/web_tests/virtual/cache-storage-parallel/external/wpt/service-workers/README.txt
new file mode 100644
index 0000000..8e43317
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/cache-storage-parallel/external/wpt/service-workers/README.txt
@@ -0,0 +1,4 @@
+This suite runs the ServiceWorker and CacheStorage tests with the
+CacheStorageParallelOps feature enabled and set to 64 maximum shared
+operations.  This makes cache_storage execute read operations in
+parallel.
diff --git a/third_party/blink/web_tests/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt b/third_party/blink/web_tests/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
new file mode 100644
index 0000000..10168a5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/compositor_threaded_scrollbar_scrolling/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
@@ -0,0 +1,75 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 656],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow MAT id='scroller'",
+      "position": [8, 8],
+      "bounds": [360, 640]
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [8, 8],
+      "bounds": [345, 640],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [8, 8],
+      "bounds": [345, 2020],
+      "transform": 1
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [8, 8],
+      "bounds": [360, 640],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [353, 8],
+      "bounds": [15, 640],
+      "drawsContent": false,
+      "paintInvalidations": [
+        {
+          "object": "Vertical Scrollbar Layer",
+          "rect": [0, 0, 15, 640],
+          "reason": "full layer"
+        }
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow (sticky positioned) DIV id='sticky'",
+      "position": [8, -27],
+      "bounds": [345, 20]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -35, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
new file mode 100644
index 0000000..caab7be
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/disable-blink-gen-property-trees/paint/invalidation/scroll/sticky/invalidate-after-composited-scroll-with-sticky-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 656],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "VerticalScrollbar",
+          "rect": [353, 8, 15, 640],
+          "reason": "scroll control"
+        }
+      ]
+    },
+    {
+      "name": "Ancestor Clipping Layer",
+      "position": [8, 8],
+      "bounds": [345, 640],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutNGBlockFlow (sticky positioned) DIV id='sticky'",
+      "position": [8, 8],
+      "bounds": [345, 20]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index cd80f071..4954018 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -59,6 +59,7 @@
     property dir
     property dispatchEvent
     property draggable
+    property elementTiming
     property enterKeyHint
     property firstChild
     property firstElementChild
@@ -1165,6 +1166,7 @@
     property createShadowRoot
     property dataset
     property dispatchEvent
+    property elementTiming
     property firstChild
     property firstElementChild
     property focus
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 62b0c9a..480db6b 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -1608,6 +1608,7 @@
     getter clientLeft
     getter clientTop
     getter clientWidth
+    getter elementTiming
     getter firstElementChild
     getter id
     getter innerHTML
@@ -1689,6 +1690,7 @@
     method webkitRequestFullscreen
     setter classList
     setter className
+    setter elementTiming
     setter id
     setter innerHTML
     setter onbeforecopy
@@ -3785,6 +3787,23 @@
     getter target
     method constructor
     setter target
+interface LargestContentfulPaint : PerformanceEntry
+    attribute @@toStringTag
+    getter element
+    getter id
+    getter loadTime
+    getter renderTime
+    getter size
+    getter url
+    method constructor
+    method toJSON
+interface LayoutShift : PerformanceEntry
+    attribute @@toStringTag
+    getter hadRecentInput
+    getter lastInputTime
+    getter value
+    method constructor
+    method toJSON
 interface LinearAccelerationSensor : Accelerometer
     attribute @@toStringTag
     method constructor
@@ -5108,6 +5127,7 @@
     method getTransceivers
     method removeStream
     method removeTrack
+    method restartIce
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
@@ -8723,6 +8743,7 @@
     method getTransceivers
     method removeStream
     method removeTrack
+    method restartIce
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt
new file mode 100644
index 0000000..c759bcb1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Negotiation needed when returning to stable does not fire too early promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
index 4f8c807f..b44cc1f 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL restartIce() does not trigger negotiation ahead of initial negotiation promise_test: Unhandled rejection with value: object "TypeError: pc1.restartIce is not a function"
+FAIL restartIce() does not trigger negotiation ahead of initial negotiation assert_equals: No negotiationneeded event expected (undefined) undefined but got (object) object "[object Event]"
 FAIL restartIce() has no effect on initial negotiation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL restartIce() fires negotiationneeded after initial negotiation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL restartIce() causes fresh ufrags promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index 52843dc..55ead67 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 508 tests; 399 PASS, 109 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 508 tests; 401 PASS, 107 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: Error: assert_true: Expect pc.sctp to be instance of RTCSctpTransport expected true got false Reached unreachable code
@@ -33,7 +33,7 @@
 PASS RTCPeerConnection interface: attribute iceConnectionState
 PASS RTCPeerConnection interface: attribute connectionState
 FAIL RTCPeerConnection interface: attribute canTrickleIceCandidates assert_true: The prototype object must have a property "canTrickleIceCandidates" expected true got false
-FAIL RTCPeerConnection interface: operation restartIce() assert_own_property: interface prototype object missing non-static operation expected property "restartIce" missing
+PASS RTCPeerConnection interface: operation restartIce()
 FAIL RTCPeerConnection interface: operation getDefaultIceServers() assert_own_property: interface object missing static operation expected property "getDefaultIceServers" missing
 PASS RTCPeerConnection interface: operation getConfiguration()
 PASS RTCPeerConnection interface: operation setConfiguration(RTCConfiguration)
@@ -86,7 +86,7 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceConnectionState" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "connectionState" with the proper type
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "canTrickleIceCandidates" with the proper type assert_inherits: property "canTrickleIceCandidates" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type assert_inherits: property "restartIce" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getDefaultIceServers()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getConfiguration()" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setConfiguration(RTCConfiguration)" with the proper type
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html b/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html
index 912f14a..75b1c03 100644
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html
+++ b/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html
@@ -5,7 +5,7 @@
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
 <script> let legacy_vr_test = true; </script>
 <script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
+<script src="../wpt_internal/webxr/resources/xr-internal-device-mocking.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index b7b764be..3d94e252 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -107,6 +107,7 @@
     property dispatchEvent
     property displayLock
     property draggable
+    property elementTiming
     property enterKeyHint
     property firstChild
     property firstElementChild
@@ -1294,6 +1295,7 @@
     property dataset
     property dispatchEvent
     property displayLock
+    property elementTiming
     property firstChild
     property firstElementChild
     property focus
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 1a3665a..19883b5 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
@@ -341,6 +341,24 @@
 [Worker]     attribute PERSISTENT
 [Worker]     attribute TEMPORARY
 [Worker]     method constructor
+[Worker] interface DetectedBarcode
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter cornerPoints
+[Worker]     getter format
+[Worker]     getter rawValue
+[Worker]     method constructor
+[Worker] interface DetectedFace
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter landmarks
+[Worker]     method constructor
+[Worker] interface DetectedText
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter cornerPoints
+[Worker]     getter rawValue
+[Worker]     method constructor
 [Worker] interface ErrorEvent : Event
 [Worker]     attribute @@toStringTag
 [Worker]     getter colno
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 1554f97..025c14d 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -1502,20 +1502,17 @@
     getter format
     getter rawValue
     method constructor
-    method toJSON
 interface DetectedFace
     attribute @@toStringTag
     getter boundingBox
     getter landmarks
     method constructor
-    method toJSON
 interface DetectedText
     attribute @@toStringTag
     getter boundingBox
     getter cornerPoints
     getter rawValue
     method constructor
-    method toJSON
 interface DeviceMotionEvent : Event
     attribute @@toStringTag
     getter acceleration
@@ -1993,6 +1990,7 @@
     getter computedName
     getter computedRole
     getter displayLock
+    getter elementTiming
     getter firstElementChild
     getter id
     getter innerHTML
@@ -2121,6 +2119,7 @@
     setter ariaValueText
     setter classList
     setter className
+    setter elementTiming
     setter id
     setter innerHTML
     setter invisible
@@ -5929,6 +5928,7 @@
     method getTransceivers
     method removeStream
     method removeTrack
+    method restartIce
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
@@ -10884,6 +10884,7 @@
     method getTransceivers
     method removeStream
     method removeTrack
+    method restartIce
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
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 134ab1e9..cd3b593 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
@@ -336,6 +336,24 @@
 [Worker]     method constructor
 [Worker]     method contains
 [Worker]     method item
+[Worker] interface DetectedBarcode
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter cornerPoints
+[Worker]     getter format
+[Worker]     getter rawValue
+[Worker]     method constructor
+[Worker] interface DetectedFace
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter landmarks
+[Worker]     method constructor
+[Worker] interface DetectedText
+[Worker]     attribute @@toStringTag
+[Worker]     getter boundingBox
+[Worker]     getter cornerPoints
+[Worker]     getter rawValue
+[Worker]     method constructor
 [Worker] interface ErrorEvent : Event
 [Worker]     attribute @@toStringTag
 [Worker]     getter colno
diff --git a/third_party/blink/web_tests/wpt_internal/README b/third_party/blink/web_tests/wpt_internal/README
index fe2ef6e8..9537fc8a 100644
--- a/third_party/blink/web_tests/wpt_internal/README
+++ b/third_party/blink/web_tests/wpt_internal/README
@@ -2,6 +2,10 @@
 features (except `wpt lint`) are enabled in this directory, including wptserve.
 This directory is mapped to wpt_internal/ on wptserve.
 
+When including additional scripts from tests within this folder, the "root"
+folder is "external/wpt" and paths should be relative to that. (E.g. "/resources
+/testharness.js" will reference external/wpt/resources/testharness.js).
+
 (*) "Internal" in the sense that tests are not synchronized with the WPT
 upstream (https://github.com/web-platform-tests/wpt) or other browser vendors.
 
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-on-positioned-element.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-on-positioned-element.html
new file mode 100644
index 0000000..d8c8542
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-on-positioned-element.html
@@ -0,0 +1,44 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: acquire, commit</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">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+
+  position: absolute;
+  top: 0px;
+  left: 0px;
+}
+</style>
+
+<div id="log"></div>
+<div id="container"></div>
+
+<script>
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  takeScreenshot();
+}
+
+function runTest() {
+  const container = document.getElementById("container");
+  container.displayLock.acquire({ timeout: Infinity }).then(() => {
+    const child = document.createElement("div");
+    document.body.appendChild(child);
+
+    finishTest("PASS");
+  });
+}
+
+window.onload = runTest;
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/README.md b/third_party/blink/web_tests/wpt_internal/webxr/README.md
new file mode 100644
index 0000000..80fdd20
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/README.md
@@ -0,0 +1,19 @@
+# WebXR Internal Tests
+In keeping with the requirements for the wpt\_internal tests, these tests
+validate behavior that is not yet in a stable version of the spec (e.g. AR),
+test Chrome specific constants, or test things specific to the blink/mojom
+implementations.
+
+In order to reference xr-internal-device-mocking.js (or other additional files
+that may be required for an internal test), the additionalChromiumResources
+variable/array should be set before including webxr\_util.js, as webxr\_util.js
+begins loading resources immediately.
+
+To port a test from wpt\_internal to external/wpt, the "/webxr/resources/..."
+paths will need to be converted to "resources/..." paths, and any reliance on
+internal APIs (i.e. any usage of additionalChromiumResources) should be removed.
+Those should be the only changes required other than those required by wpt lint.
+
+For more details, please reference the wpt_internal [README] [WPT Readme].
+
+[WPT Readme]: https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/web_tests/wpt_internal/README
diff --git a/third_party/blink/web_tests/xr/ar_hittest.html b/third_party/blink/web_tests/wpt_internal/webxr/ar_hittest.https.html
similarity index 68%
rename from third_party/blink/web_tests/xr/ar_hittest.html
rename to third_party/blink/web_tests/wpt_internal/webxr/ar_hittest.https.html
index 8225619..75dac3ac 100644
--- a/third_party/blink/web_tests/xr/ar_hittest.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/ar_hittest.https.html
@@ -1,15 +1,10 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<script src="/webxr/resources/webxr_test_asserts.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/events_deviceconnect.https.html b/third_party/blink/web_tests/wpt_internal/webxr/events_deviceconnect.https.html
new file mode 100644
index 0000000..861a067
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/events_deviceconnect.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<canvas />
+<script>
+xr_promise_test("Test devicechange fires when devices are connected.",
+  (t) => {
+  let eventWatcher = new EventWatcher(t, navigator.xr, ["devicechange"]);
+
+  // The event should fire when a listener is added (which EventWatcher does)
+  // even if the devices are not explicity queried with navigator.xr.requestDevice().
+  // Note: This behaviour is chrome specific, the spec does not explicitly
+  // state when devicechange events should occur.
+
+  let promise = eventWatcher.wait_for(["devicechange"]);
+
+  // Timeout here is required because addEventListener sends a call to mojo to
+  // register the listener on the backend, which doesn't go through until the
+  // javascript pauses.
+  t.step_timeout(() => {
+    navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE);
+  }, 100);
+
+  return promise;
+
+});
+
+</script>
diff --git a/third_party/blink/web_tests/xr/navigator_xr_blocked_by_getVRDisplays.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html
similarity index 73%
rename from third_party/blink/web_tests/xr/navigator_xr_blocked_by_getVRDisplays.html
rename to third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html
index c64754f..4f0ee6c7 100644
--- a/third_party/blink/web_tests/xr/navigator_xr_blocked_by_getVRDisplays.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 // This behavior is not in the webxr spec, so this is an internal-only test.
 promise_test((t) => navigator.getVRDisplays().then(() => {
diff --git a/third_party/blink/web_tests/xr/navigator_xr_blocks_getVRDisplays.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html
similarity index 76%
rename from third_party/blink/web_tests/xr/navigator_xr_blocks_getVRDisplays.html
rename to third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html
index 14fe210..1c712a8b 100644
--- a/third_party/blink/web_tests/xr/navigator_xr_blocks_getVRDisplays.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 // This behavior is not in the webxr spec, so this is an internal-only test.
 promise_test((t) => navigator.xr.requestSession('immersive-vr').catch(() => {
diff --git a/third_party/blink/web_tests/xr/navigator_xr_detached.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
similarity index 85%
rename from third_party/blink/web_tests/xr/navigator_xr_detached.html
rename to third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
index dd8fc5e..78a78438 100644
--- a/third_party/blink/web_tests/xr/navigator_xr_detached.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
@@ -2,8 +2,8 @@
 <html>
 <head>
 <title>Detached use of navigator.xr</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 </head>
 <body>
 <iframe sandbox="allow-same-origin" id="subframe"></iframe>
diff --git a/third_party/blink/web_tests/xr/navigator_xr_early_detached.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_early_detached.https.html
similarity index 84%
rename from third_party/blink/web_tests/xr/navigator_xr_early_detached.html
rename to third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_early_detached.https.html
index 2d60c9c3..d4ecb91d 100644
--- a/third_party/blink/web_tests/xr/navigator_xr_early_detached.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_early_detached.https.html
@@ -2,8 +2,8 @@
 <html>
 <head>
 <title>Detached use of navigator.xr()</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 </head>
 <body>
 <iframe sandbox="allow-same-origin" id="subframe"></iframe>
diff --git a/third_party/blink/web_tests/xr/render_state_vertical_fov_inline.html b/third_party/blink/web_tests/wpt_internal/webxr/render_state_vertical_fov_inline.https.html
similarity index 78%
rename from third_party/blink/web_tests/xr/render_state_vertical_fov_inline.html
rename to third_party/blink/web_tests/wpt_internal/webxr/render_state_vertical_fov_inline.https.html
index 79e9097..08d2d9c 100644
--- a/third_party/blink/web_tests/xr/render_state_vertical_fov_inline.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/render_state_vertical_fov_inline.https.html
@@ -1,14 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js b/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
similarity index 100%
rename from third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js
rename to third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
diff --git a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html b/third_party/blink/web_tests/wpt_internal/webxr/xrBoundedReferenceSpace_updates.https.html
similarity index 81%
rename from third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
rename to third_party/blink/web_tests/wpt_internal/webxr/xrBoundedReferenceSpace_updates.https.html
index d65ab5bc..8660345 100644
--- a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrBoundedReferenceSpace_updates.https.html
@@ -1,15 +1,10 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<script src="/webxr/resources/webxr_test_asserts.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/xr/xrFrame_getPose.html b/third_party/blink/web_tests/wpt_internal/webxr/xrFrame_getPose.https.html
similarity index 64%
rename from third_party/blink/web_tests/xr/xrFrame_getPose.html
rename to third_party/blink/web_tests/wpt_internal/webxr/xrFrame_getPose.https.html
index 7d2f8c3e..fef47a2 100644
--- a/third_party/blink/web_tests/xr/xrFrame_getPose.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrFrame_getPose.https.html
@@ -1,14 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https.html b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https.html
new file mode 100644
index 0000000..5c3acf7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_immersive.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<canvas />
+
+<script>
+// This test manipulates mojo directly to simulate some behavior, and as such is
+// chromium specific.
+let testName = "Immersive session ends if data provider disconnects.";
+let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;
+
+let testFunction = function(session, fakeDeviceController, t) {
+  return new Promise((resolve) => {
+    session.addEventListener('end', () => resolve());
+
+    // Request an animation frame to ensure that everything has time to get
+    // initialized/connected and avoid a race-condition failure with the data
+    // binding not being able to get closed.
+    session.requestAnimationFrame(() => {
+      fakeDeviceController.closeDataProvider();
+      session.requestAnimationFrame(() => {});
+    });
+  });
+}
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-vr');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https.html b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https.html
new file mode 100644
index 0000000..88c26d0
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_dataProviderDisconnect_inline.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<canvas />
+
+<script>
+// This test manipulates mojo directly to simulate some behavior, and as such is
+// chromium specific.
+let testName = "Inline session ends if magic window data provider disconnects.";
+let fakeDeviceInitParams = VALID_NON_IMMERSIVE_DEVICE;
+
+let testFunction = function(session, fakeDeviceController, t) {
+  return new Promise((resolve) => {
+    session.addEventListener('end', () => resolve());
+
+    // Request an animation frame to ensure that everything has time to get
+    // initialized/connected and avoid a race-condition failure with the data
+    // binding not being able to get closed.
+    session.requestAnimationFrame(() => {
+      fakeDeviceController.closeDataProvider();
+      session.requestAnimationFrame(() => {});
+    });
+  });
+}
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'inline');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrSession_deviceDisconnect.https.html b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_deviceDisconnect.https.html
new file mode 100644
index 0000000..a24332e
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_deviceDisconnect.https.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<canvas />
+
+<script>
+
+// This test manipulates mojo directly to simulate some behavior, and as such is
+// chromium specific.
+let testName = "Outstanding promises resolve appropriately if device disconencts";
+
+let testFunction = function(t) {
+  // Expose the ability to get get the VRService and close the device on that VRService.
+  ChromeXRTest.prototype.getService = function() {
+    return this.mockVRService_;
+  }
+
+  MockVRService.prototype.closeDevice = function() {
+    this.deviceBinding_.close();
+    this.devicePtr_ = null;
+  }
+
+  // Override the default implementations of requestSession and supportsSession
+  // from XRDevice so that we can choose to either return an answer immediately or
+  // return a promise that will never resolve to guarantee we have an outstanding
+  // promise on device disconnect.
+  MockVRService.prototype.requestSession = function(sessionOptions, was_activation) {
+    return new Promise((resolve,reject) => { });
+  }
+
+  let immediatelyResolveSupportsSession = true;
+  MockVRService.prototype.supportsSession = function(sessionOptions) {
+    if (immediatelyResolveSupportsSession) {
+      return Promise.resolve({ supportsSession: true });
+    }
+
+    return new Promise((resolve, reject) => { });
+  }
+
+  // Override the default requestDevice implementation so that we can fail if it's
+  // called when we don't expect it to be called (typically this would be because
+  // we aren't planning to force another device closure and would leave any
+  // outstanding promises unresolved, and thus cause the test to timeout), and so
+  // that we can store the device binding so that we can force it to be closed.
+  let failIfRequestDeviceCalled = false;
+  MockVRService.prototype.requestDevice = function() {
+    if (failIfRequestDeviceCalled) {
+      assert_unreached("requestDevice shouldn't be called at this time");
+    }
+
+    if (!this.devicePtr_) {
+      this.devicePtr_ = new device.mojom.XRDevicePtr();
+      this.deviceBinding_ = new mojo.Binding(
+          device.mojom.XRDevice, this, mojo.makeRequest(this.devicePtr_));
+    }
+
+    return Promise.resolve({device: this.devicePtr_});
+  }
+
+  // Convenience methods to turn a resolve/reject into an appropraite string
+  // which Promise.All can check to tell us which methods failed.
+  // If we just let the promises assert, then Promise.all would only fail on the
+  // first assert.
+  let successMessage = "PASS";
+  let failMessageStart = "FAIL: "
+  function WrapResolve(promise, name, errorMsg) {
+    return promise.then(() => {
+      return successMessage;
+    }, () => {
+      return failMessageStart + name + ": " + errorMsg;
+    });
+  }
+
+  function WrapReject(promise, name, errorMsg, errorType) {
+    return promise.then(() => {
+      return failMessageStart + name + ": " + errorMsg;
+    }, (err) => {
+      if (err.name === errorType) {
+        return successMessage;
+      }
+
+      return failMessageStart + name + ": expected: " + errorType + " but got: " + err.name;
+    });
+  }
+
+  let validateDeviceDisconnectPromise = function() {
+    // Ensure that the state is properly set-up for our helper functions so that
+    // we can be called multiple times.
+    failIfRequestDeviceCalled = false;
+    immediatelyResolveSupportsSession = true;
+
+    // Make a supports session call so that we can ensure that the underlying code
+    // has gotten a devicePtr set up.  Note that inline, since it's always
+    // guaranteed doesn't ensure that the device is set up, where-as a call to see
+    // if we support immersive does require a device
+    return navigator.xr.supportsSession('immersive-vr').then(() => {
+
+      // Cause supportsSession to stop returning and make future calls "hang"/
+      immediatelyResolveSupportsSession = false;
+
+      // We don't expect a new device to be requested, and if it is we aren't
+      // going to close it during this test, so any of our mocked calls will cause
+      //  a timeout.
+      failIfRequestDeviceCalled = true;
+      let promises = [];
+
+      // Note that inline session requests still call out through the device.
+      promises.push(WrapResolve(navigator.xr.requestSession('inline'), "Request Inline",
+        "Inline should always be available"));
+      promises.push(WrapReject(navigator.xr.requestSession('immersive-vr'), "Request Immersive",
+        "Immersive should be rejected once device is disconnected", "NotSupportedError"));
+      promises.push(WrapReject(navigator.xr.supportsSession('immersive-vr'), "Supports Immersive",
+        "Immersive should not be supported once device is disconnected", "NotSupportedError"));
+
+      // Force the device disconnect, which should cause the promises to resolve.
+      navigator.xr.test.getService().closeDevice();
+
+      // Call this after we close the device, because we don't expect this to rely
+      // on (or request) the presence of a device.
+      promises.push(WrapResolve(navigator.xr.supportsSession('inline'), "Supports Inline",
+        "Inline support should be available without calling to a device"));
+
+      return Promise.all(promises).then((results) => {
+        let error_messages = [];
+        for (let i = 0; i < results.length; i++) {
+          if (results[i] !== successMessage) {
+            error_messages.push(results[i]);
+          }
+        }
+
+        if (error_messages.length !== 0) {
+          assert_unreached(error_messages);
+        }
+      })
+    });
+  }
+
+  return new Promise((resolve) => {
+    // Give a user activation so that immersive requets don't get a security error.
+    navigator.xr.test.simulateUserActivation(() => { resolve(); });
+  })
+  .then(() => {
+    return validateDeviceDisconnectPromise()
+  })
+  .then(() => {
+
+    // Validate that even after disconnecting and resolving the promises,
+    // we can still request a new device, and that it will resolve any promises
+    // on it's disconnect.
+    return validateDeviceDisconnectPromise();
+  });
+}
+
+xr_promise_test(testName, testFunction);
+
+</script>
diff --git a/third_party/blink/web_tests/xr/xrSession_environmentBlendMode.html b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentBlendMode.https.html
similarity index 68%
rename from third_party/blink/web_tests/xr/xrSession_environmentBlendMode.html
rename to third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentBlendMode.https.html
index d58c3ce..170931b 100644
--- a/third_party/blink/web_tests/xr/xrSession_environmentBlendMode.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentBlendMode.https.html
@@ -1,13 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https.html b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https.html
new file mode 100644
index 0000000..3092c068
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrSession_environmentProviderDisconnect.https.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
+<canvas />
+
+<script>
+// This test manipulates mojo directly to simulate some behavior, and as such is
+// chromium specific.
+let testName = "Outstanding promises get rejected if environmentProvider disconencts";
+
+let fakeDeviceInitParams = { supportsImmersive: true,
+                             views: VALID_VIEWS,
+                             supportsEnvironmentIntegration: true };
+
+let refSpace = undefined;
+
+let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0});
+
+let testFunction = function(session, fakeDeviceController, t) {
+  // Override the xr-internal-device-mock for requestHitTest so that we can
+  // also return a promise that never resolves or rejects.
+  // This is so that we can simulate a disconnect while the mojo call is still
+  // outstanding.
+  let immediatelyResolveHitTest = true;
+  MockRuntime.prototype.requestHitTest = function(Ray) {
+    if (immediatelyResolveHitTest) {
+      var hit = new device.mojom.XRHitResult();
+      hit.hitMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+      hit_results = {results: [hit]};
+      return Promise.resolve(hit_results);
+    }
+
+    return new Promise((resolve,reject) => { });
+  }
+
+  return session.requestReferenceSpace('local')
+    .then((referenceSpace) => {
+      refSpace = referenceSpace;
+
+      // Request a first hit test to ensure that all of the mojo connections are
+      // up and running.
+      return session.requestHitTest(ray, refSpace);
+    })
+    .then(() => {
+      immediatelyResolveHitTest = false;
+      let hitTestPromise = session.requestHitTest(ray, refSpace);
+      fakeDeviceController.closeEnvironmentIntegrationProvider();
+      return hitTestPromise;
+    })
+    .then(() => {
+        assert_unreached("HitTestPromise should not resolve");
+      },
+      (err) => {
+        assert_equals(err.name, "InvalidStateError");
+      });
+};
+
+xr_session_promise_test(
+  testName, testFunction, fakeDeviceInitParams, 'immersive-ar');
+
+</script>
diff --git a/third_party/blink/web_tests/xr/xrWebGLLayer_dirty_framebuffer.html b/third_party/blink/web_tests/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https.html
similarity index 73%
rename from third_party/blink/web_tests/xr/xrWebGLLayer_dirty_framebuffer.html
rename to third_party/blink/web_tests/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https.html
index 58e646fe..3c898c02 100644
--- a/third_party/blink/web_tests/xr/xrWebGLLayer_dirty_framebuffer.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrWebGLLayer_dirty_framebuffer.https.html
@@ -1,14 +1,9 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>let additionalChromiumResources = ["resources/xr-internal-device-mocking.js"];</script>
+<script src="/webxr/resources/webxr_util.js"></script>
+<script src="/webxr/resources/webxr_test_constants.js"></script>
 <canvas />
 
 <script>
@@ -50,7 +45,7 @@
     gl.clear(gl.COLOR_BUFFER_BIT);
 
     // After the function returns ensure the frame was submitted.
-    window.setTimeout(() => {
+    t.step_timeout(() => {
       assert_equals(fakeDeviceController.getSubmitFrameCount(), 1);
       assert_equals(fakeDeviceController.getMissingFrameCount(), 2);
       // Finished test.
diff --git a/third_party/blink/web_tests/xr/xr_view_projection_detached.html b/third_party/blink/web_tests/wpt_internal/webxr/xr_view_projection_detached.https.html
similarity index 69%
rename from third_party/blink/web_tests/xr/xr_view_projection_detached.html
rename to third_party/blink/web_tests/wpt_internal/webxr/xr_view_projection_detached.https.html
index 9c6922b..02993cc3 100644
--- a/third_party/blink/web_tests/xr/xr_view_projection_detached.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xr_view_projection_detached.https.html
@@ -1,13 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <canvas />
 
 <script>
diff --git a/third_party/blink/web_tests/xr/README.md b/third_party/blink/web_tests/xr/README.md
deleted file mode 100644
index 702300d2..0000000
--- a/third_party/blink/web_tests/xr/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# XR
-
-This directory contains tests that cannot be external/wpt/webxr tests due to one
-one of the following reasons:
-
-1) The behavior they test is chrome-specific in some way
-(e.g. intentionally non-compliant, specific decisions allowed by the spec, non-specced interactions, etc.)
-
-2) They test behavior and interactions with or exposed by the mojom layer that are not suitable to be shared.
-
-3) They rely on test methods that are not present in the webxr-test-api
-
-4) They test behavior that is not yet in the core spec.
-
-In the case of number 4 a crbug should be filed to update and move the test to be external once the spec has been updated.
-
-## XR/Resources
-The resources folder contains the xr device mocking extensions that enable some internal tests.
-Due to pathing limitations, the xr_session_promise_test implementation is also cloned into xr-test-utils
-
-Where possible, ensure that any updates to xr-test-utils can be mirrored into external/wpt/webxr/webxr_utils.
-Any helper methods should also generally be added to the external resources, to ease the transition of temporarily-internal tests to be external.
-
diff --git a/third_party/blink/web_tests/xr/events_deviceconnect.html b/third_party/blink/web_tests/xr/events_deviceconnect.html
deleted file mode 100644
index 69b5960..0000000
--- a/third_party/blink/web_tests/xr/events_deviceconnect.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<canvas />
-<script>
-promise_test((t) => {
-  let eventWatcher = new EventWatcher(t, navigator.xr, ["devicechange"]);
-
-  // The event should fire when a listener is added (which EventWatcher does)
-  // even if the devices are not explicity queried with navigator.xr.requestDevice().
-  // Note: This behaviour is chrome specific, the spec does not explicitly
-  // state when devicechange events should occur.
-
-  let promise = eventWatcher.wait_for(["devicechange"]);
-
-  // Timeout here is required because addEventListener sends a call to mojo to
-  // register the listener on the backend, which doesn't go through until the
-  // javascript pauses.
-  setTimeout(() => {
-    navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE);
-  }, 0);
-
-  return promise;
-
-}, "Test devicechange fires when devices are connected.");
-
-</script>
diff --git a/third_party/blink/web_tests/xr/resources/xr-test-utils.js b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
deleted file mode 100644
index 46026e7..0000000
--- a/third_party/blink/web_tests/xr/resources/xr-test-utils.js
+++ /dev/null
@@ -1,77 +0,0 @@
-// func: A function that takes a session and optionally a test object and
-// performs tests. If func returns a promise, test will only pass if the promise
-// resolves.
-function xr_session_promise_test(
-    name, func, deviceOptions, sessionMode, sessionInit, properties) {
-  let testSession = null;
-  let sessionObjects = {};
-
-  const webglCanvas = document.getElementsByTagName('canvas')[0];
-  // We can't use assert_true here because it causes the wpt testharness to treat
-  // this as a test page and not as a test.
-  if (!webglCanvas) {
-    promise_test(async (t) => {
-      Promise.reject('xr_session_promise_test requires a canvas on the page!');
-    }, name, properties);
-  }
-  let gl = webglCanvas.getContext('webgl', {alpha: false, antialias: false});
-  sessionObjects.gl = gl;
-  promise_test((t) => {
-    let fakeDeviceController;
-
-    // Ensure that any pending sessions are ended and devices are
-    // disconnected when done. This needs to use a cleanup function to
-    // ensure proper sequencing. If this were done in a .then() for the
-    // success case, a test that expected failure would already be marked
-    // done at the time that runs, and the shutdown would interfere with
-    // the next test which may have started already.
-    t.add_cleanup(async() => {
-      if (testSession) {
-        // TODO(bajones): Throwing an error when a session is
-        // already ended is not defined by the spec. This
-        // should be defined or removed.
-        await testSession.end().catch(() => {});
-      }
-
-      await navigator.xr.test.disconnectAllDevices();
-    });
-
-    return navigator.xr.test.simulateDeviceConnection(deviceOptions)
-        .then((controller) => {
-          fakeDeviceController = controller;
-          if (gl) {
-            return gl.makeXRCompatible().then(
-                () => Promise.resolve());
-          } else {
-            return Promise.resolve();
-          }
-        })
-        .then(() => new Promise((resolve, reject) => {
-            // Perform the session request in a user gesture.
-            navigator.xr.test.simulateUserActivation(() => {
-              navigator.xr.requestSession(sessionMode, sessionInit || {})
-                  .then((session) => {
-                    testSession = session;
-                    session.mode = sessionMode;
-                    let glLayer = new XRWebGLLayer(session, gl, {
-                      compositionDisabled: session.mode == 'inline'
-                    });
-                    sessionObjects.glLayer = glLayer;
-                    // Session must have a baseLayer or frame requests
-                    // will be ignored.
-                    session.updateRenderState({
-                        baseLayer: glLayer
-                    });
-                    resolve(func(session, fakeDeviceController, t, sessionObjects));
-                  })
-                  .catch((err) => {
-                    reject('Session with params ' +
-                      JSON.stringify(sessionMode) +
-                        ' was rejected on device ' +
-                        JSON.stringify(fakeDeviceInit) +
-                        ' with error: ' + err);
-                  });
-            });
-        }));
-  }, name, properties);
-}
diff --git a/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_immersive.html b/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_immersive.html
deleted file mode 100644
index 67e1a1e..0000000
--- a/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_immersive.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas />
-
-<script>
-// This test manipulates mojo directly to simulate some behavior, and as such is
-// chromium specific.
-let testName = "Immersive session ends if data provider disconnects.";
-let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;
-
-let testFunction = function(session, fakeDeviceController, t) {
-  return new Promise((resolve) => {
-    session.addEventListener('end', () => resolve());
-
-    // Request an animation frame to ensure that everything has time to get
-    // initialized/connected and avoid a race-condition failure with the data
-    // binding not being able to get closed.
-    session.requestAnimationFrame(() => {
-      fakeDeviceController.closeDataProvider();
-      session.requestAnimationFrame(() => {});
-    });
-  });
-}
-
-xr_session_promise_test(
-  testName, testFunction, fakeDeviceInitParams, 'immersive-vr');
-
-</script>
diff --git a/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_inline.html b/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_inline.html
deleted file mode 100644
index 92d0b384..0000000
--- a/third_party/blink/web_tests/xr/xrSession_dataProviderDisconnect_inline.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas />
-
-<script>
-// This test manipulates mojo directly to simulate some behavior, and as such is
-// chromium specific.
-let testName = "Inline session ends if magic window data provider disconnects.";
-let fakeDeviceInitParams = VALID_NON_IMMERSIVE_DEVICE;
-
-let testFunction = function(session, fakeDeviceController, t) {
-  return new Promise((resolve) => {
-    session.addEventListener('end', () => resolve());
-
-    // Request an animation frame to ensure that everything has time to get
-    // initialized/connected and avoid a race-condition failure with the data
-    // binding not being able to get closed.
-    session.requestAnimationFrame(() => {
-      fakeDeviceController.closeDataProvider();
-      session.requestAnimationFrame(() => {});
-    });
-  });
-}
-
-xr_session_promise_test(
-  testName, testFunction, fakeDeviceInitParams, 'inline');
-
-</script>
diff --git a/third_party/blink/web_tests/xr/xrSession_deviceDisconnect.html b/third_party/blink/web_tests/xr/xrSession_deviceDisconnect.html
deleted file mode 100644
index bdd4b38..0000000
--- a/third_party/blink/web_tests/xr/xrSession_deviceDisconnect.html
+++ /dev/null
@@ -1,164 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas />
-
-<script>
-
-// This test manipulates mojo directly to simulate some behavior, and as such is
-// chromium specific.
-let testName = "Outstanding promises resolve appropriately if device disconencts";
-
-// Expose the ability to get get the VRService and close the device on that VRService.
-ChromeXRTest.prototype.getService = function() {
-  return this.mockVRService_;
-}
-
-MockVRService.prototype.closeDevice = function() {
-  this.deviceBinding_.close();
-  this.devicePtr_ = null;
-}
-
-// Override the default implementations of requestSession and supportsSession
-// from XRDevice so that we can choose to either return an answer immediately or
-// return a promise that will never resolve to guarantee we have an outstanding
-// promise on device disconnect.
-MockVRService.prototype.requestSession = function(sessionOptions, was_activation) {
-  return new Promise((resolve,reject) => { });
-}
-
-let immediatelyResolveSupportsSession = true;
-MockVRService.prototype.supportsSession = function(sessionOptions) {
-  if (immediatelyResolveSupportsSession) {
-    return Promise.resolve({ supportsSession: true });
-  }
-
-  return new Promise((resolve, reject) => { });
-}
-
-// Override the default requestDevice implementation so that we can fail if it's
-// called when we don't expect it to be called (typically this would be because
-// we aren't planning to force another device closure and would leave any
-// outstanding promises unresolved, and thus cause the test to timeout), and so
-// that we can store the device binding so that we can force it to be closed.
-let failIfRequestDeviceCalled = false;
-MockVRService.prototype.requestDevice = function() {
-  if (failIfRequestDeviceCalled) {
-    assert_unreached("requestDevice shouldn't be called at this time");
-  }
-
-  if (!this.devicePtr_) {
-    this.devicePtr_ = new device.mojom.XRDevicePtr();
-    this.deviceBinding_ = new mojo.Binding(
-        device.mojom.XRDevice, this, mojo.makeRequest(this.devicePtr_));
-  }
-
-  return Promise.resolve({device: this.devicePtr_});
-}
-
-// Convenience methods to turn a resolve/reject into an appropraite string
-// which Promise.All can check to tell us which methods failed.
-// If we just let the promises assert, then Promise.all would only fail on the
-// first assert.
-let successMessage = "PASS";
-let failMessageStart = "FAIL: "
-function WrapResolve(promise, name, errorMsg) {
-  return promise.then(() => {
-    return successMessage;
-  }, () => {
-    return failMessageStart + name + ": " + errorMsg;
-  });
-}
-
-function WrapReject(promise, name, errorMsg, errorType) {
-  return promise.then(() => {
-    return failMessageStart + name + ": " + errorMsg;
-  }, (err) => {
-    if (err.name === errorType) {
-      return successMessage;
-    }
-
-    return failMessageStart + name + ": expected: " + errorType + " but got: " + err.name;
-  });
-}
-
-let validateDeviceDisconnectPromise = function() {
-  // Ensure that the state is properly set-up for our helper functions so that
-  // we can be called multiple times.
-  failIfRequestDeviceCalled = false;
-  immediatelyResolveSupportsSession = true;
-
-  // Make a supports session call so that we can ensure that the underlying code
-  // has gotten a devicePtr set up.  Note that inline, since it's always
-  // guaranteed doesn't ensure that the device is set up, where-as a call to see
-  // if we support immersive does require a device
-  return navigator.xr.supportsSession('immersive-vr').then(() => {
-
-    // Cause supportsSession to stop returning and make future calls "hang"/
-    immediatelyResolveSupportsSession = false;
-
-    // We don't expect a new device to be requested, and if it is we aren't
-    // going to close it during this test, so any of our mocked calls will cause
-    //  a timeout.
-    failIfRequestDeviceCalled = true;
-    let promises = [];
-
-    // Note that inline session requests still call out through the device.
-    promises.push(WrapResolve(navigator.xr.requestSession('inline'), "Request Inline",
-      "Inline should always be available"));
-    promises.push(WrapReject(navigator.xr.requestSession('immersive-vr'), "Request Immersive",
-      "Immersive should be rejected once device is disconnected", "NotSupportedError"));
-    promises.push(WrapReject(navigator.xr.supportsSession('immersive-vr'), "Supports Immersive",
-      "Immersive should not be supported once device is disconnected", "NotSupportedError"));
-
-    // Force the device disconnect, which should cause the promises to resolve.
-    navigator.xr.test.getService().closeDevice();
-
-    // Call this after we close the device, because we don't expect this to rely
-    // on (or request) the presence of a device.
-    promises.push(WrapResolve(navigator.xr.supportsSession('inline'), "Supports Inline",
-      "Inline support should be available without calling to a device"));
-
-    return Promise.all(promises).then((results) => {
-      let error_messages = [];
-      for (let i = 0; i < results.length; i++) {
-        if (results[i] !== successMessage) {
-          error_messages.push(results[i]);
-        }
-      }
-
-      if (error_messages.length !== 0) {
-        assert_unreached(error_messages);
-      }
-    })
-  });
-}
-
-let testFunction = function(t) {
-  return new Promise((resolve) => {
-    // Give a user activation so that immersive requets don't get a security error.
-    navigator.xr.test.simulateUserActivation(() => { resolve(); });
-  })
-  .then(() => {
-    return validateDeviceDisconnectPromise()
-  })
-  .then(() => {
-
-    // Validate that even after disconnecting and resolving the promises,
-    // we can still request a new device, and that it will resolve any promises
-    // on it's disconnect.
-    return validateDeviceDisconnectPromise();
-  });
-}
-
-promise_test(testFunction, testName);
-
-</script>
diff --git a/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html b/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html
deleted file mode 100644
index ae9ca72..0000000
--- a/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html
+++ /dev/null
@@ -1,70 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/testdriver.js"></script>
-<script src="../resources/testdriver-vendor.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="../xr/resources/xr-internal-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas />
-
-<script>
-// This test manipulates mojo directly to simulate some behavior, and as such is
-// chromium specific.
-let testName = "Outstanding promises get rejected if environmentProvider disconencts";
-
-let fakeDeviceInitParams = { supportsImmersive: true,
-                             views: VALID_VIEWS,
-                             supportsEnvironmentIntegration: true };
-
-let refSpace = undefined;
-
-let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0});
-
-// Override the xr-internal-device-mock for requestHitTest so that we can
-// also return a promise that never resolves or rejects.
-// This is so that we can simulate a disconnect while the mojo call is still
-// outstanding.
-let immediatelyResolveHitTest = true;
-MockRuntime.prototype.requestHitTest = function(Ray) {
-  if (immediatelyResolveHitTest) {
-    var hit = new device.mojom.XRHitResult();
-    hit.hitMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
-    hit_results = {results: [hit]};
-    return Promise.resolve(hit_results);
-  }
-
-  return new Promise((resolve,reject) => { });
-}
-
-let testFunction = function(session, fakeDeviceController, t) {
-  return session.requestReferenceSpace('local')
-    .then((referenceSpace) => {
-      refSpace = referenceSpace;
-
-      // Request a first hit test to ensure that all of the mojo connections are
-      // up and running.
-      return session.requestHitTest(ray, refSpace);
-    })
-    .then(() => {
-      immediatelyResolveHitTest = false;
-      let hitTestPromise = session.requestHitTest(ray, refSpace);
-      fakeDeviceController.closeEnvironmentIntegrationProvider();
-      return hitTestPromise;
-    })
-    .then(() => {
-        assert_unreached("HitTestPromise should not resolve");
-      },
-      (err) => {
-        assert_equals(err.name, "InvalidStateError");
-      });
-};
-
-xr_session_promise_test(
-  testName, testFunction, fakeDeviceInitParams, 'immersive-ar');
-
-</script>
diff --git a/third_party/libcxx-pretty-printers/README.chromium b/third_party/libcxx-pretty-printers/README.chromium
index 332511d..30c2d47 100644
--- a/third_party/libcxx-pretty-printers/README.chromium
+++ b/third_party/libcxx-pretty-printers/README.chromium
@@ -1,6 +1,6 @@
 Name: libcxx-pretty-printers
 URL: https://github.com/koutheir/libcxx-pretty-printers
-Version: 5c40997ea6850a11321907b4651954bcfa6a21c7
+Version: 9eb1bc3070a6560e388803af657b60015cfb440c
 License: GPL v3
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/libcxx-pretty-printers/printers.py b/third_party/libcxx-pretty-printers/printers.py
index 7f06a759d..5d68550 100644
--- a/third_party/libcxx-pretty-printers/printers.py
+++ b/third_party/libcxx-pretty-printers/printers.py
@@ -148,7 +148,7 @@
             len = sl['__size_']
             ptr = sl['__data_']
 
-        return ''.join(chr(ptr[i]) for i in range(len))
+        return u''.join(unichr(ptr[i]) for i in range(len))
 
     def display_hint(self):
         return 'string'
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 4088419..2cf027d 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Monday July 15 2019
+Date: Tuesday July 23 2019
 Branch: master
-Commit: bb407a27b2e32f89f0e9eeee2bcd0aa9d5cfea3f
+Commit: 18d309c12734d2f06d54ad1716e512153a13ab9d
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
index abd9cbd..9c314978 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
@@ -1347,6 +1347,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
index abd9cbd..9c314978 100644
--- a/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
@@ -1347,6 +1347,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
index 0f5ae3e..95df12a 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
@@ -1741,6 +1741,13 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
index 24f335a..71eb333 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
@@ -4047,6 +4047,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
index abd9cbd..9c314978 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
@@ -1347,6 +1347,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
index dc7b59b..792d50f 100644
--- a/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
@@ -870,6 +870,13 @@
                        uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_c
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
index 24f335a..71eb333 100644
--- a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
@@ -4047,6 +4047,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
index abd9cbd..9c314978 100644
--- a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
@@ -1347,6 +1347,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
index 7e5eb2cf..8ba4d880 100644
--- a/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
@@ -3155,6 +3155,13 @@
                        uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_c
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
index 472c624d..096e3a86 100644
--- a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
@@ -5856,6 +5856,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7843,6 +7859,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
index b406c88..63a0f75 100644
--- a/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
@@ -870,6 +870,13 @@
                        uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_c
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
index b406c88..63a0f75 100644
--- a/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
@@ -870,6 +870,13 @@
                        uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_c
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
index 7021eaa..ed632331 100644
--- a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
@@ -5933,6 +5933,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7923,6 +7939,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
index 472c624d..096e3a86 100644
--- a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
@@ -5856,6 +5856,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7843,6 +7859,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
index 7021eaa..ed632331 100644
--- a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
@@ -5933,6 +5933,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7923,6 +7939,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
index 7e5eb2cf..8ba4d880 100644
--- a/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
@@ -3155,6 +3155,13 @@
                        uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_c
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 225da6e..e481f5e 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -1,8 +1,8 @@
 // This file is generated. Do not edit.
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  8
-#define VERSION_PATCH  0
-#define VERSION_EXTRA  "601-gbb407a27b2"
+#define VERSION_PATCH  1
+#define VERSION_EXTRA  "53-g18d309c127"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.8.0-601-gbb407a27b2"
-#define VERSION_STRING      " v1.8.0-601-gbb407a27b2"
+#define VERSION_STRING_NOSP "v1.8.1-53-g18d309c127"
+#define VERSION_STRING      " v1.8.1-53-g18d309c127"
diff --git a/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
index 24f335a..71eb333 100644
--- a/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
@@ -4047,6 +4047,13 @@
                           uint32_t* sad_array);
 #define vpx_sad32x32x4d vpx_sad32x32x4d_neon
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+#define vpx_sad32x32x8 vpx_sad32x32x8_c
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
diff --git a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
index 472c624d..096e3a86 100644
--- a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
@@ -5856,6 +5856,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7843,6 +7859,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
index 7021eaa..ed632331 100644
--- a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
@@ -5933,6 +5933,22 @@
                                     int ref_stride,
                                     uint32_t* sad_array);
 
+void vpx_sad32x32x8_c(const uint8_t* src_ptr,
+                      int src_stride,
+                      const uint8_t* ref_ptr,
+                      int ref_stride,
+                      uint32_t* sad_array);
+void vpx_sad32x32x8_avx2(const uint8_t* src_ptr,
+                         int src_stride,
+                         const uint8_t* ref_ptr,
+                         int ref_stride,
+                         uint32_t* sad_array);
+RTCD_EXTERN void (*vpx_sad32x32x8)(const uint8_t* src_ptr,
+                                   int src_stride,
+                                   const uint8_t* ref_ptr,
+                                   int ref_stride,
+                                   uint32_t* sad_array);
+
 unsigned int vpx_sad32x64_c(const uint8_t* src_ptr,
                             int src_stride,
                             const uint8_t* ref_ptr,
@@ -7923,6 +7939,9 @@
   vpx_sad32x32x4d = vpx_sad32x32x4d_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x32x4d = vpx_sad32x32x4d_avx2;
+  vpx_sad32x32x8 = vpx_sad32x32x8_c;
+  if (flags & HAS_AVX2)
+    vpx_sad32x32x8 = vpx_sad32x32x8_avx2;
   vpx_sad32x64 = vpx_sad32x64_sse2;
   if (flags & HAS_AVX2)
     vpx_sad32x64 = vpx_sad32x64_avx2;
diff --git a/third_party/netty4/OWNERS b/third_party/netty4/OWNERS
index fb59c70..d314095 100644
--- a/third_party/netty4/OWNERS
+++ b/third_party/netty4/OWNERS
@@ -1 +1,3 @@
 file://net/OWNERS
+# COMPONENT: Internals>Network>Library
+# TEAM: net-dev@chromium.org
diff --git a/third_party/tcmalloc/chromium/src/base/basictypes.h b/third_party/tcmalloc/chromium/src/base/basictypes.h
index 24a6a8a6..3bf59f4e 100644
--- a/third_party/tcmalloc/chromium/src/base/basictypes.h
+++ b/third_party/tcmalloc/chromium/src/base/basictypes.h
@@ -200,7 +200,8 @@
 # define ATTRIBUTE_UNUSED
 #endif
 
-#if defined(HAVE___ATTRIBUTE__) && defined(HAVE_TLS)
+#if defined(HAVE___ATTRIBUTE__) && defined(HAVE_TLS) && \
+    !(defined(__GNUC__) && !defined(__clang__) && defined(__arm__))
 #define ATTR_INITIAL_EXEC __attribute__ ((tls_model ("initial-exec")))
 #else
 #define ATTR_INITIAL_EXEC
diff --git a/third_party/tcmalloc/chromium/src/heap-checker.cc b/third_party/tcmalloc/chromium/src/heap-checker.cc
index 8e71f582..918194f2 100755
--- a/third_party/tcmalloc/chromium/src/heap-checker.cc
+++ b/third_party/tcmalloc/chromium/src/heap-checker.cc
@@ -445,7 +445,8 @@
 // the cost you can't dlopen this library.  But dlopen on heap-checker
 // doesn't work anyway -- it must run before main -- so this is a good
 // trade-off.
-# ifdef HAVE___ATTRIBUTE__
+# if defined(HAVE___ATTRIBUTE__) && \
+    !(defined(__GNUC__) && !defined(__clang__) && defined(__arm__))
    __attribute__ ((tls_model ("initial-exec")))
 # endif
     ;
diff --git a/third_party/tcmalloc/gperftools-2.0/chromium/src/heap-checker.cc b/third_party/tcmalloc/gperftools-2.0/chromium/src/heap-checker.cc
index 1400c8e..3da27ff 100644
--- a/third_party/tcmalloc/gperftools-2.0/chromium/src/heap-checker.cc
+++ b/third_party/tcmalloc/gperftools-2.0/chromium/src/heap-checker.cc
@@ -448,7 +448,8 @@
 // the cost you can't dlopen this library.  But dlopen on heap-checker
 // doesn't work anyway -- it must run before main -- so this is a good
 // trade-off.
-# ifdef HAVE___ATTRIBUTE__
+#if defined(HAVE___ATTRIBUTE__) && \
+    !(defined(__GNUC__) && !defined(__clang__) && defined(__arm__))
    __attribute__ ((tls_model ("initial-exec")))
 # endif
     ;
diff --git a/third_party/tcmalloc/gperftools-2.0/chromium/src/thread_cache.h b/third_party/tcmalloc/gperftools-2.0/chromium/src/thread_cache.h
index ba2f873..0397842 100644
--- a/third_party/tcmalloc/gperftools-2.0/chromium/src/thread_cache.h
+++ b/third_party/tcmalloc/gperftools-2.0/chromium/src/thread_cache.h
@@ -272,7 +272,7 @@
   // gcc has a problem with this tls model on arm.
   // See https://bugs.chromium.org/p/chromium/issues/detail?id=650137
 #if defined(HAVE___ATTRIBUTE__) && !defined(PGO_GENERATE) && \
-    !(!defined(__clang__) && defined(OS_CHROMEOS) && defined(__arm__))
+    !(defined(__GNUC__) && !defined(__clang__) && defined(__arm__))
    __attribute__ ((tls_model ("initial-exec")))
 # endif
    ;
diff --git a/third_party/webxr_test_pages/webxr-samples/room-scale.html b/third_party/webxr_test_pages/webxr-samples/room-scale.html
index f06dae7..b44e365 100644
--- a/third_party/webxr_test_pages/webxr-samples/room-scale.html
+++ b/third_party/webxr_test_pages/webxr-samples/room-scale.html
@@ -69,8 +69,7 @@
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveRefSpace = null;
-      let xrNonImmersiveRefSpace = null;
+      let refSpaces = {};
 
       // WebGL scene globals.
       let gl = null;
@@ -127,13 +126,22 @@
         onResize();
 
         renderer = new Renderer(gl);
-
         scene.setRenderer(renderer);
       }
 
+      function updateBoundsRenderer(refSpace) {
+        if (!boundsRenderer) {
+          boundsRenderer = new BoundsRenderer(refSpace);
+          scene.addNode(boundsRenderer);
+        } else {
+          boundsRenderer.boundedRefSpace = refSpace;
+        }
+      }
+
       function onSessionStarted(session) {
         if (!session.mode)
           session.mode = 'inline';
+        session.ended = false;
         session.addEventListener('end', onSessionEnded);
 
         initGL();
@@ -147,13 +155,8 @@
         // Attempt to get a 'bounded' reference space, which will align the
         // user's physical floor with Y=0 and provide boundaries that indicate
         // where the user can safely walk.
-        session.requestReferenceSpace({ type: 'bounded' }).then((refSpace) => {
-          if (!boundsRenderer) {
-            boundsRenderer = new BoundsRenderer(refSpace);
-            scene.addNode(boundsRenderer);
-          } else {
-            boundsRenderer.boundedRefSpace = refSpace;
-          }
+        session.requestReferenceSpace('bounded-floor').then((refSpace) => {
+          updateBoundsRenderer(refSpace);
           return refSpace;
         }).catch(() => {
           // If a bounded reference space isn't supported, fall back to a
@@ -185,16 +188,31 @@
             }
           });
         }).then((refSpace) => {
-          if (session.mode.startsWith('immersive')) {
-            xrImmersiveRefSpace = refSpace;
-          } else {
-            xrNonImmersiveRefSpace = refSpace;
-          }
-
+          refSpaces[session.mode] = refSpace;
           session.requestAnimationFrame(onXRFrame);
+          if (session.mode.startsWith('immersive') && !boundsRenderer) {
+            pollBounds(session);
+          }
         });
       }
 
+      function sleep(ms) {
+        return new Promise(resolve => setTimeout(resolve, ms));
+      }
+
+      function pollBounds(session) {
+        if (session.mode.startsWith('immersive') && !session.ended) {
+          session.requestReferenceSpace('bounded-floor').then((space) => {
+            updateBoundsRenderer(space);
+            refSpaces[session.mode] = space;
+            console.log(space.boundsGeometry);
+          }).catch(() => {
+            console.log("Failed to get bounded reference space. Trying again in 1 second.");
+            sleep(1000).then(() => pollBounds(session));
+          });
+        }
+      }
+
       function onEndSession(session) {
         session.end();
       }
@@ -203,14 +221,12 @@
         if (event.session.mode.startsWith('immersive')) {
           xrButton.setSession(null);
         }
+        event.session.ended = true;
       }
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let refSpace = session.mode.startsWith('immersive') ?
-                         xrImmersiveRefSpace :
-                         xrNonImmersiveRefSpace;
-        let pose = frame.getViewerPose(refSpace);
+        let pose = frame.getViewerPose(refSpaces[session.mode]);
 
         scene.startFrame();
 
diff --git a/third_party/win_build_output/mc/chrome/credential_provider/eventlog/gcp_eventlog_messages.h b/third_party/win_build_output/mc/chrome/credential_provider/eventlog/gcp_eventlog_messages.h
index 6a45791..7f1f35b 100644
--- a/third_party/win_build_output/mc/chrome/credential_provider/eventlog/gcp_eventlog_messages.h
+++ b/third_party/win_build_output/mc/chrome/credential_provider/eventlog/gcp_eventlog_messages.h
@@ -47,13 +47,13 @@
 

 

 //

-// MessageId: GCP_CATEGORY

+// MessageId: GCPW_CATEGORY

 //

 // MessageText:

 //

-// GCP Events

+// GCPW Events

 //

-#define GCP_CATEGORY                     ((WORD)0x00000001L)

+#define GCPW_CATEGORY                     ((WORD)0x00000001L)

 

 //

 // MessageId: MSG_LOG_MESSAGE

diff --git a/third_party/zlib/patches/0000-build.patch b/third_party/zlib/patches/0000-build.patch
index c27111d..b7c5996 100644
--- a/third_party/zlib/patches/0000-build.patch
+++ b/third_party/zlib/patches/0000-build.patch
@@ -149,9 +149,17 @@
  #ifndef ZCONF_H
  #define ZCONF_H
  
++/*
++ * This library is also built as a part of AOSP, which does not need to include
++ * chromeconf.h. This config does not want chromeconf.h, so it can set this
++ * macro to opt out. While this works today, there's no guarantee that building
++ * zlib outside of Chromium keeps working in the future.
++ */
++#if !defined(CHROMIUM_ZLIB_NO_CHROMECONF)
 +/* This include does prefixing as below, but with an updated set of names.  Also
 + * sets up export macros in component builds. */
 +#include "chromeconf.h"
++#endif
 +
  /*
   * If you *really* need a unique prefix for all types and library functions,
diff --git a/third_party/zlib/zconf.h b/third_party/zlib/zconf.h
index 1664387..2d1d03f 100644
--- a/third_party/zlib/zconf.h
+++ b/third_party/zlib/zconf.h
@@ -10,9 +10,11 @@
 
 /*
  * This library is also built as a part of AOSP, which does not need to include
- * chromeconf.h. In that case, neither of these macros will be defined.
+ * chromeconf.h. This config does not want chromeconf.h, so it can set this
+ * macro to opt out. While this works today, there's no guarantee that building
+ * zlib outside of Chromium keeps working in the future.
  */
-#if defined(CHROMIUM_BUILD) || defined(GOOGLE_CHROME_BUILD)
+#if !defined(CHROMIUM_ZLIB_NO_CHROMECONF)
 /* This include does prefixing as below, but with an updated set of names.  Also
  * sets up export macros in component builds. */
 #include "chromeconf.h"
diff --git a/tools/OWNERS b/tools/OWNERS
index d771315..2f045af 100644
--- a/tools/OWNERS
+++ b/tools/OWNERS
@@ -54,3 +54,6 @@
 per-file sort-headers.py=satorux@chromium.org
 per-file sort-sources.py=satorux@chromium.org
 per-file yes_no.py=satorux@chromium.org
+
+per-file download_cros_provided_profile.py=gbiv@chromium.org
+per-file download_cros_provided_profile.py=tcwang@chromium.org
diff --git a/tools/cfi/blacklist.txt b/tools/cfi/blacklist.txt
index c2199d1..1adb46f5 100644
--- a/tools/cfi/blacklist.txt
+++ b/tools/cfi/blacklist.txt
@@ -159,8 +159,6 @@
 src:*ui/gl/gl_bindings_autogen_*
 
 src:*components/os_crypt/*
-src:*chrome/browser/password_manager/native_backend_gnome_x.cc
-src:*chrome/browser/password_manager/native_backend_libsecret*
 
 src:*content/browser/accessibility/browser_accessibility_auralinux.cc
 src:*ui/accessibility/platform/ax_platform_node_auralinux.cc
diff --git a/tools/chrome_proxy/webdriver/fallback.py b/tools/chrome_proxy/webdriver/fallback.py
index c50d266..cf61ae51 100644
--- a/tools/chrome_proxy/webdriver/fallback.py
+++ b/tools/chrome_proxy/webdriver/fallback.py
@@ -35,8 +35,8 @@
       # Verify that DataReductionProxy.ProbeURL histogram has one entry in
       # FAILED_PROXY_DISABLED, which is bucket=1.
       histogram = test_driver.GetBrowserHistogram('DataReductionProxy.ProbeURL')
-      self.assertEqual(histogram['count'], 1)
-      self.assertEqual(histogram['buckets'][0]['low'], 1)
+      self.assertGreaterEqual(histogram['count'], 1)
+      self.assertGreaterEqual(histogram['buckets'][0]['low'], 1)
       for response in responses:
           self.assertHasProxyHeaders(response)
           # TODO(rajendrant): Fix the correct protocol received.
diff --git a/tools/download_cros_provided_profile.py b/tools/download_cros_provided_profile.py
new file mode 100755
index 0000000..45755d2
--- /dev/null
+++ b/tools/download_cros_provided_profile.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This script is used to update our local profiles (AFDO or orderfiles)
+
+This uses profiles of Chrome, or orderfiles for linking, provided by our
+friends from Chrome OS. Though the profiles are available externally,
+the bucket they sit in is otherwise unreadable by non-Googlers. Gsutil
+usage with this bucket is therefore quite awkward: you can't do anything
+but `cp` certain files with an external account, and you can't even do
+that if you're not yet authenticated.
+
+No authentication is necessary if you pull these profiles directly over
+https.
+"""
+
+import argparse
+import contextlib
+import os
+import subprocess
+import sys
+import urllib2
+
+GS_HTTP_URL = 'https://storage.googleapis.com'
+
+
+def ReadUpToDateProfileName(newest_profile_name_path):
+  with open(newest_profile_name_path) as f:
+    return f.read().strip()
+
+
+def ReadLocalProfileName(local_profile_name_path):
+  try:
+    with open(local_profile_name_path) as f:
+      return f.read().strip()
+  except IOError:
+    # Assume it either didn't exist, or we couldn't read it. In either case, we
+    # should probably grab a new profile (and, in doing so, make this file sane
+    # again)
+    return None
+
+
+def WriteLocalProfileName(name, local_profile_name_path):
+  with open(local_profile_name_path, 'w') as f:
+    f.write(name)
+
+
+def CheckCallOrExit(cmd):
+  proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  stdout, stderr = proc.communicate()
+  exit_code = proc.wait()
+  if not exit_code:
+    return
+
+  complaint_lines = [
+      '## %s failed with exit code %d' % (cmd[0], exit_code),
+      '## Full command: %s' % cmd,
+      '## Stdout:\n' + stdout,
+      '## Stderr:\n' + stderr,
+  ]
+  print >>sys.stderr, '\n'.join(complaint_lines)
+  sys.exit(1)
+
+
+def RetrieveProfile(desired_profile_name, out_path, gs_url_base):
+  # vpython is > python 2.7.9, so we can expect urllib to validate HTTPS certs
+  # properly.
+  ext = os.path.splitext(desired_profile_name)[1]
+  compressed_path = out_path + ext
+  gs_prefix = 'gs://'
+  if not desired_profile_name.startswith(gs_prefix):
+    gs_url = os.path.join(GS_HTTP_URL, gs_url_base, desired_profile_name)
+  else:
+    gs_url = os.path.join(GS_HTTP_URL, desired_profile_name[len(gs_prefix):])
+
+  with contextlib.closing(urllib2.urlopen(gs_url)) as u:
+    with open(compressed_path, 'wb') as f:
+      while True:
+        buf = u.read(4096)
+        if not buf:
+          break
+        f.write(buf)
+
+  if ext == '.bz2':
+    # NOTE: we can't use Python's bzip module, since it doesn't support
+    # multi-stream bzip files. It will silently succeed and give us a garbage
+    # profile.
+    # bzip2 removes the compressed file on success.
+    CheckCallOrExit(['bzip2', '-d', compressed_path])
+  elif ext == '.xz':
+    # ...And we can't use the `lzma` module, since it was introduced in python3.
+    # xz removes the compressed file on success.
+    CheckCallOrExit(['xz', '-d', compressed_path])
+  else:
+    # Wait until after downloading the file to check the file extension, so the
+    # user has something usable locally if the file extension is unrecognized.
+    raise ValueError(
+        'Only bz2 and xz extensions are supported; "%s" is not' % ext)
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      'Downloads profile/orderfile provided by Chrome OS')
+
+  parser.add_argument(
+      '--newest_state',
+      required=True,
+      help='Path to the file with name of the newest profile. '
+           'We use this file to track the name of the newest profile '
+           'we should pull'
+  )
+  parser.add_argument(
+      '--local_state',
+      required=True,
+      help='Path of the file storing name of the local profile. '
+           'We use this file to track the most recent profile we\'ve '
+           'successfully pulled.'
+  )
+  parser.add_argument(
+      '--gs_url_base',
+      required=True,
+      help='The base GS URL to search for the profile.'
+  )
+  parser.add_argument(
+      '--output_name',
+      required=True,
+      help='Output name of the downloaded and uncompressed profile.'
+  )
+  parser.add_argument(
+      '-f', '--force',
+      action='store_true',
+      help='Fetch a profile even if the local one is current'
+  )
+  args = parser.parse_args()
+
+  up_to_date_profile = ReadUpToDateProfileName(args.newest_state)
+  if not args.force:
+    local_profile_name = ReadLocalProfileName(args.local_state)
+    # In a perfect world, the local profile should always exist if we
+    # successfully read local_profile_name. If it's gone, though, the user
+    # probably removed it as a way to get us to download it again.
+    if local_profile_name == up_to_date_profile \
+        and os.path.exists(args.output_name):
+      return 0
+
+  new_tmpfile = args.output_name + '.new'
+  RetrieveProfile(up_to_date_profile, new_tmpfile, args.gs_url_base)
+  os.rename(new_tmpfile, args.output_name)
+  WriteLocalProfileName(up_to_date_profile, args.local_state)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
index 78c5610..116a83b7 100644
--- a/tools/gdb/gdb_chrome.py
+++ b/tools/gdb/gdb_chrome.py
@@ -162,9 +162,14 @@
   typename = 'base::WeakPtr'
 
   def ptr(self):
-    if bool(
-        gdb.parse_and_eval(
-            '(*(%s*)(%s)).ref_.IsValid()' % (self.val.type, self.val.address))):
+    # Check that the pointer is valid. The invalidated flag is stored at
+    # val.ref_.flag_.ptr_->invalidated_.flag_.__a_.__a_value. This is a gdb
+    # implementation of base::WeakReference::IsValid(). This is necessary
+    # because calling gdb.parse_and_eval('(*(%s*)(%s)).ref_.IsValid()' %
+    # (self.val.type, self.val.address))) does not work in all cases.
+    ptr = self.val['ref_']['flag_']['ptr_']
+    if (ptr and
+        not ptr.dereference()['invalidated_']['flag_']['__a_']['__a_value']):
       return self.val['ptr_']
     return gdb.Value(0).cast(self.val['ptr_'].type)
 
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index a5120181..ba3af5a 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1296,7 +1296,10 @@
       # If we're testing a CrOS simplechrome build, assume we need to prepare a
       # DUT for testing. So prepend the command to run with the test wrapper.
       if is_simplechrome:
-        cmdline = [os.path.join('bin', 'cros_test_wrapper')]
+        cmdline = [
+            os.path.join('bin', 'cros_test_wrapper'),
+            '--logs-dir=${ISOLATED_OUTDIR}',
+        ]
       cmdline += [
           '../../testing/test_env.py',
           '../../' + self.ToSrcRelPath(isolate_map[target]['script'])
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 0be04c7..40bbfa2 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -4007,16 +4007,19 @@
 </action>
 
 <action name="ChromeColors_MenuCancel">
+  <obsolete>Deprecated as of 7/2019.</obsolete>
   <owner>gayane@chromium.org</owner>
   <description>Applied changes where canceled from Colors menu.</description>
 </action>
 
 <action name="ChromeColors_MenuDone">
+  <obsolete>Deprecated as of 7/2019.</obsolete>
   <owner>gayane@chromium.org</owner>
   <description>Applied changes where confirmed from Colors menu.</description>
 </action>
 
 <action name="ChromeColors_TabClosed">
+  <obsolete>Deprecated as of 7/2019.</obsolete>
   <owner>gayane@chromium.org</owner>
   <description>Tab with open Colors menu was closed.</description>
 </action>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2681efd4..996ee16 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2168,6 +2168,91 @@
   <int value="8" label="kLaunchTypeUnspecified"/>
 </enum>
 
+<enum name="AppListNonAppImpressionCategory">
+  <int value="0" label="APPS"/>
+  <int value="1" label="SEARCH_RESULTS"/>
+  <int value="2" label="START"/>
+  <int value="3" label="CUSTOM_LAUNCHER_PAGE_DEPRECATED"/>
+</enum>
+
+<enum name="AppListNonAppImpressionDeviceMode">
+  <int value="0" label="UNKNOWN"/>
+  <int value="1" label="FILE"/>
+  <int value="2" label="HISTORY"/>
+  <int value="3" label="NAV_SUGGEST"/>
+  <int value="4" label="SEARCH"/>
+  <int value="5" label="BOOKMARK"/>
+  <int value="6" label="DOCUMENT"/>
+  <int value="7" label="OMNIBOX_DEPRECATED"/>
+  <int value="8" label="OMNIBOX_GENERIC"/>
+</enum>
+
+<enum name="AppListNonAppImpressionFileExtension">
+  <int value="0" label=".3ga"/>
+  <int value="1" label=".3gp"/>
+  <int value="2" label=".aac"/>
+  <int value="3" label=".alac"/>
+  <int value="4" label=".asf"/>
+  <int value="5" label=".avi"/>
+  <int value="6" label=".bmp"/>
+  <int value="7" label=".csv"/>
+  <int value="8" label=".doc"/>
+  <int value="9" label=".docx"/>
+  <int value="10" label=".flac"/>
+  <int value="11" label=".gif"/>
+  <int value="12" label=".jpeg"/>
+  <int value="13" label=".jpg"/>
+  <int value="14" label=".log"/>
+  <int value="15" label=".m3u"/>
+  <int value="16" label=".m3u8"/>
+  <int value="17" label=".m4a"/>
+  <int value="18" label=".m4v"/>
+  <int value="19" label=".mid"/>
+  <int value="20" label=".mkv"/>
+  <int value="21" label=".mov"/>
+  <int value="22" label=".mp3"/>
+  <int value="23" label=".mp4"/>
+  <int value="24" label=".mpg"/>
+  <int value="25" label=".odf"/>
+  <int value="26" label=".odp"/>
+  <int value="27" label=".ods"/>
+  <int value="28" label=".odt"/>
+  <int value="29" label=".oga"/>
+  <int value="30" label=".ogg"/>
+  <int value="31" label=".ogv"/>
+  <int value="32" label=".pdf"/>
+  <int value="33" label=".png"/>
+  <int value="34" label=".ppt"/>
+  <int value="35" label=".pptx"/>
+  <int value="36" label=".ra"/>
+  <int value="37" label=".ram"/>
+  <int value="38" label=".rar"/>
+  <int value="39" label=".rm"/>
+  <int value="40" label=".rtf"/>
+  <int value="41" label=".wav"/>
+  <int value="42" label=".webm"/>
+  <int value="43" label=".webp"/>
+  <int value="44" label=".wma"/>
+  <int value="45" label=".wmv"/>
+  <int value="46" label=".xls"/>
+  <int value="47" label=".xlsx"/>
+  <int value="48" label=".crdownload"/>
+  <int value="49" label=".crx"/>
+  <int value="50" label=".dmg"/>
+  <int value="51" label=".exe"/>
+  <int value="52" label=".html"/>
+  <int value="53" label=".htm"/>
+  <int value="54" label=".jar"/>
+  <int value="55" label=".ps"/>
+  <int value="56" label=".torrent"/>
+  <int value="57" label=".txt"/>
+  <int value="58" label=".zip"/>
+  <int value="59" label=".mhtml"/>
+  <int value="60" label=".gdoc"/>
+  <int value="61" label=".gsheet"/>
+  <int value="62" label=".gslides"/>
+</enum>
+
 <enum name="AppListOmniboxResult">
   <int value="0" label="QUERY_SUGGESTION"/>
   <int value="1" label="ZERO_STATE_SUGEESTION"/>
@@ -5630,6 +5715,11 @@
   <int value="1" label="Checked"/>
 </enum>
 
+<enum name="BooleanChromeColorsChangesConfirmed">
+  <int value="0" label="Reverted"/>
+  <int value="1" label="Confirmed"/>
+</enum>
+
 <enum name="BooleanCleared">
   <int value="0" label="Not cleared"/>
   <int value="1" label="Cleared"/>
@@ -7426,6 +7516,12 @@
   <int value="19" label="RASPBERRY"/>
 </enum>
 
+<enum name="ChromeColorsRevertReason">
+  <int value="0" label="User action"/>
+  <int value="1" label="Search provider change"/>
+  <int value="2" label="Tab closed"/>
+</enum>
+
 <enum name="ChromeDownloadCountType">
   <int value="0" label="Initiated by Navigation (Obsolete)"/>
   <int value="1" label="Initiated by Context Menu (Obsolete)"/>
@@ -20048,6 +20144,8 @@
   <int value="1360" label="ACTION_SETBADGEBACKGROUNDCOLOR"/>
   <int value="1361" label="AUTOTESTPRIVATE_SETARCAPPWINDOWSTATE"/>
   <int value="1362" label="ACCESSIBILITY_PRIVATE_OPENSETTINGSSUBPAGE"/>
+  <int value="1363" label="ACTION_ENABLE"/>
+  <int value="1364" label="ACTION_DISABLE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -23693,7 +23791,7 @@
   <int value="2649" label="OpenerNavigationDownloadCrossOrigin"/>
   <int value="2650" label="V8RegExpMatchIsTrueishOnNonJSRegExp"/>
   <int value="2651" label="V8RegExpMatchIsFalseishOnJSRegExp"/>
-  <int value="2652" label="DownloadInAdFrameWithUserGesture"/>
+  <int value="2652" label="OBSOLETE_DownloadInAdFrameWithUserGesture"/>
   <int value="2653" label="DownloadInAdFrameWithoutUserGesture"/>
   <int value="2654" label="NavigatorAppVersion"/>
   <int value="2655" label="NavigatorDoNotTrack"/>
@@ -24013,9 +24111,7 @@
   <int value="2946" label="WebkitMarginAfterCollapseSeparate"/>
   <int value="2947"
       label="WebkitMarginAfterCollapseSeparateMaybeDoesSomething"/>
-  <int value="2948" label="CredentialManagerCreateWithUVM"/>
   <int value="2949" label="CredentialManagerGetWithUVM"/>
-  <int value="2950" label="CredentialManagerCreateSuccessWithUVM"/>
   <int value="2951" label="CredentialManagerGetSuccessWithUVM"/>
   <int value="2952" label="DiscardInputEventToMovingIframe"/>
   <int value="2953" label="SignedExchangeSubresourcePrefetch"/>
@@ -24040,6 +24136,10 @@
   <int value="2971" label="V8PointerEvent_GetPredictedEvents_Method"/>
   <int value="2972" label="ScrollSnapOnViewportBreaks"/>
   <int value="2973" label="ScrollPaddingOnViewportBreaks"/>
+  <int value="2974" label="DownloadInAdFrame"/>
+  <int value="2975" label="DownloadInSandbox"/>
+  <int value="2976" label="DownloadWithoutUserGesture"/>
+  <int value="2977" label="AutoplayDynamicDelegation"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -24159,6 +24259,10 @@
   <int value="2" label="Fetch failure"/>
 </enum>
 
+<!-- Keep elements in sync with components/feed/core/feed_logging_metrics.cc and
+ third_party/feed/src/src/main/java/com/google/android/libraries/feed/api/host/logging/InternalFeedError.java
+ -->
+
 <enum name="FeedInternalError">
   <int value="0" label="SWITCH_TO_EPHEMERAL"/>
   <int value="1" label="NO_URL_FOR_OPEN"/>
@@ -32304,6 +32408,52 @@
       label="Del Fail -MaxPathCheck Fail - MoveFileEx Fail - CopyDelDir Fail"/>
 </enum>
 
+<enum name="KerberosConfigErrorCode">
+  <int value="0" label="Valid"/>
+  <int value="1" label="Section nested in group"/>
+  <int value="2" label="Section syntax"/>
+  <int value="3" label="Expected opening curly brace"/>
+  <int value="4" label="Extra curly brace"/>
+  <int value="5" label="Relation syntax"/>
+  <int value="6" label="Key not supported"/>
+  <int value="7" label="Section not supported"/>
+  <int value="8" label="Krb5 failed to parse"/>
+</enum>
+
+<enum name="KerberosErrorType">
+  <int value="0" label="Success"/>
+  <int value="1" label="Unspecified error"/>
+  <int value="2" label="Unspecified D-Bus error"/>
+  <int value="3" label="General network problem"/>
+  <int value="4" label="Unspecified KRB5 error."/>
+  <int value="5" label="Bad principal"/>
+  <int value="6" label="Bad password"/>
+  <int value="7" label="Password expired"/>
+  <int value="8" label="Password rejected while trying to change it"/>
+  <int value="9" label="Kerberos credentials cache not found"/>
+  <int value="10" label="Kerberos ticket expired while renewing credentials"/>
+  <int value="11"
+      label="Key Distribution Center does not support enforced encryption
+             types"/>
+  <int value="12" label="Failed to contact Key Distribution Center"/>
+  <int value="13" label="Parsing D-Bus request message failed"/>
+  <int value="14" label="Local IO operation failed"/>
+  <int value="15" label="Account with given principal name does not exist"/>
+  <int value="16"
+      label="Adding account failed; account with given principal name already
+             exists"/>
+  <int value="17" label="Asynchronous operation in progress"/>
+  <int value="18" label="Badly formatted principal name"/>
+  <int value="19" label="Badly formatted Kerberos configuration"/>
+  <int value="20" label="Failed to run untrusted code in container"/>
+  <int value="21" label="Kerberos feature disabled"/>
+</enum>
+
+<enum name="KerberosUserType">
+  <int value="0" label="Managed"/>
+  <int value="1" label="Unmanaged"/>
+</enum>
+
 <enum name="KeyboardControlEvent">
   <int value="0" label="Keyboard was shown."/>
   <int value="1" label="Keyboard was automatically hidden."/>
@@ -33756,6 +33906,7 @@
   <int value="-2118893353" label="WebRtcRemoteEventLog:enabled"/>
   <int value="-2117621241" label="ExperimentalProductivityFeatures:enabled"/>
   <int value="-2117201726" label="disable-gpu-rasterization"/>
+  <int value="-2117169027" label="UpdateHoverAtBeginFrame:enabled"/>
   <int value="-2114831248" label="disable-new-ntp"/>
   <int value="-2113783491" label="ArcFilePickerExperiment:enabled"/>
   <int value="-2113705745"
@@ -34469,6 +34620,8 @@
   <int value="-1201183153" label="enable-centered-app-list"/>
   <int value="-1199159971"
       label="ContextualSuggestionsIPHReverseScroll:enabled"/>
+  <int value="-1198304634"
+      label="ProcessSharingWithDefaultSiteInstances:disabled"/>
   <int value="-1197245070" label="CookieDeprecationMessages:disabled"/>
   <int value="-1197035323" label="ZeroSuggestRedirectToChrome:disabled"/>
   <int value="-1195194959" label="XGEOVisibleNetworks:disabled"/>
@@ -34530,6 +34683,7 @@
   <int value="-1112782121" label="AndroidSigninPromos:disabled"/>
   <int value="-1107762575" label="enable-data-reduction-proxy-config-client"/>
   <int value="-1107103335" label="FsNosymfollow:enabled"/>
+  <int value="-1105637876" label="FilteringScrollPrediction:enabled"/>
   <int value="-1103099187" label="NotificationStackingBarRedesign:disabled"/>
   <int value="-1102212525" label="enable-tcp-fastopen"/>
   <int value="-1099618411" label="UpdatedCellularActivationUi:enabled"/>
@@ -34657,6 +34811,8 @@
   <int value="-932474660" label="VirtualDesks:enabled"/>
   <int value="-932261921"
       label="enable-defer-all-script-without-optimization-hints"/>
+  <int value="-932164609"
+      label="ProcessSharingWithDefaultSiteInstances:enabled"/>
   <int value="-929944930"
       label="AutofillRationalizeRepeatedServerPredictions:disabled"/>
   <int value="-928138978" label="IPH_DemoMode:disabled"/>
@@ -35130,6 +35286,7 @@
   <int value="-250822813" label="PwaImprovedSplashScreen:enabled"/>
   <int value="-250721831" label="AndroidAutofillAccessibility:disabled"/>
   <int value="-250543540" label="DeferAllScript:enabled"/>
+  <int value="-249415830" label="FilteringScrollPrediction:disabled"/>
   <int value="-248223420" label="AutofillKeyboardAccessory:disabled"/>
   <int value="-243323793" label="OmniboxOnDeviceHeadProvider:enabled"/>
   <int value="-241353344" label="MidiManagerWinrt:disabled"/>
@@ -36727,6 +36884,7 @@
   <int value="2071229145" label="BloatedRendererDetection:enabled"/>
   <int value="2071340353" label="progress-bar-completion"/>
   <int value="2071461362" label="disable-credit-card-scan"/>
+  <int value="2072057275" label="UpdateHoverAtBeginFrame:disabled"/>
   <int value="2072231406" label="SingleProcessMash:disabled"/>
   <int value="2073113207" label="SensorContentSetting:disabled"/>
   <int value="2075207488" label="AutomaticPasswordGeneration:disabled"/>
@@ -39623,6 +39781,7 @@
   <int value="13" label="Chrome Search Extension"/>
   <int value="14" label="Chrome Content Extension"/>
   <int value="15" label="Chrome Share Extension"/>
+  <int value="16" label="Chrome"/>
 </enum>
 
 <enum name="MobileSessionShutdownType">
@@ -40231,7 +40390,9 @@
   <int value="5" label="Opener x-origin"/>
   <int value="7" label="Sandbox no gesture"/>
   <int value="8" label="Ad frame no gesture"/>
-  <int value="9" label="Ad frame gesture"/>
+  <int value="10" label="Ad frame"/>
+  <int value="11" label="Sandbox"/>
+  <int value="12" label="No gesture"/>
 </enum>
 
 <enum name="NavigationInterceptResult">
@@ -53362,12 +53523,20 @@
   </int>
 </enum>
 
+<enum name="SharingClickToCallDialogType">
+  <int value="0" label="Dialog with devices and maybe apps"/>
+  <int value="1" label="Dialog without devices but with an app"/>
+  <int value="2" label="Educational dialog without devices or apps"/>
+  <int value="3" label="Error dialog"/>
+</enum>
+
 <enum name="SharingDeviceRegistrationResult">
   <int value="0" label="Operation is successful"/>
   <int value="1" label="Failed with Sync not ready"/>
   <int value="2" label="Failed with encryption related error"/>
   <int value="3" label="Failed with FCM transient error"/>
   <int value="4" label="Failed with FCM fatal error"/>
+  <int value="5" label="Device has not been registered"/>
 </enum>
 
 <enum name="SharingMessageType">
@@ -58442,6 +58611,15 @@
       label="CROS Cr50 0.2.2 aka 0.4.4 Flags 0x10(pre-pvt)"/>
 </enum>
 
+<enum name="TracingFinalizationDisallowedReason">
+  <int value="0" label="Incognito profile launched"/>
+  <int value="1" label="Default profile not loaded"/>
+  <int value="2" label="Crash metrics not loaded"/>
+  <int value="3" label="Last session crashed"/>
+  <int value="4" label="Metrics reporting disabled"/>
+  <int value="5" label="Recently uploaded another trace"/>
+</enum>
+
 <enum name="TrackedPreference">
   <int value="0" label="kShowHomeButton"/>
   <int value="1" label="kHomePageIsNewTabPage"/>
@@ -62912,6 +63090,361 @@
   <int value="1" label="Cannot open last used profile"/>
 </enum>
 
+<enum name="WindowsPatchLevel">
+<!-- These can be generated by visiting https://bit.ly/2JQr6rB -->
+
+  <int value="0" label="Invalid Patch Level"/>
+  <int value="498163541" label="Windows 7 Service Pack 1 (Build 7601.24405)"/>
+  <int value="602947584" label="Windows 8 (Build 9200.16384)"/>
+  <int value="629164923" label="Windows 8.1 (Build 9600.19323)"/>
+  <int value="671090283" label="Windows 10 1507 (Build 10240.1643)"/>
+  <int value="671090292" label="Windows 10 1507 (Build 10240.1652)"/>
+  <int value="671090299" label="Windows 10 1507 (Build 10240.1659)"/>
+  <int value="671090359" label="Windows 10 1507 (Build 10240.1719)"/>
+  <int value="671090417" label="Windows 10 1507 (Build 10240.1777)"/>
+  <int value="671105045" label="Windows 10 1507 (Build 10240.16405)"/>
+  <int value="671105053" label="Windows 10 1507 (Build 10240.16413)"/>
+  <int value="671105073" label="Windows 10 1507 (Build 10240.16433)"/>
+  <int value="671105085" label="Windows 10 1507 (Build 10240.16445)"/>
+  <int value="671105103" label="Windows 10 1507 (Build 10240.16463)"/>
+  <int value="671105127" label="Windows 10 1507 (Build 10240.16487)"/>
+  <int value="671105189" label="Windows 10 1507 (Build 10240.16549)"/>
+  <int value="671105206" label="Windows 10 1507 (Build 10240.16566)"/>
+  <int value="671105241" label="Windows 10 1507 (Build 10240.16601)"/>
+  <int value="671105284" label="Windows 10 1507 (Build 10240.16644)"/>
+  <int value="671105323" label="Windows 10 1507 (Build 10240.16683)"/>
+  <int value="671105365" label="Windows 10 1507 (Build 10240.16725)"/>
+  <int value="671105411" label="Windows 10 1507 (Build 10240.16771)"/>
+  <int value="671105494" label="Windows 10 1507 (Build 10240.16854)"/>
+  <int value="671105582" label="Windows 10 1507 (Build 10240.16942)"/>
+  <int value="671105664" label="Windows 10 1507 (Build 10240.17024)"/>
+  <int value="671105711" label="Windows 10 1507 (Build 10240.17071)"/>
+  <int value="671105753" label="Windows 10 1507 (Build 10240.17113)"/>
+  <int value="671105786" label="Windows 10 1507 (Build 10240.17146)"/>
+  <int value="671105842" label="Windows 10 1507 (Build 10240.17202)"/>
+  <int value="671105876" label="Windows 10 1507 (Build 10240.17236)"/>
+  <int value="671105959" label="Windows 10 1507 (Build 10240.17319)"/>
+  <int value="671105994" label="Windows 10 1507 (Build 10240.17354)"/>
+  <int value="671106034" label="Windows 10 1507 (Build 10240.17394)"/>
+  <int value="671106083" label="Windows 10 1507 (Build 10240.17443)"/>
+  <int value="671106086" label="Windows 10 1507 (Build 10240.17446)"/>
+  <int value="671106128" label="Windows 10 1507 (Build 10240.17488)"/>
+  <int value="671106173" label="Windows 10 1507 (Build 10240.17533)"/>
+  <int value="671106249" label="Windows 10 1507 (Build 10240.17609)"/>
+  <int value="671106283" label="Windows 10 1507 (Build 10240.17643)"/>
+  <int value="671106313" label="Windows 10 1507 (Build 10240.17673)"/>
+  <int value="671106349" label="Windows 10 1507 (Build 10240.17709)"/>
+  <int value="671106378" label="Windows 10 1507 (Build 10240.17738)"/>
+  <int value="671106381" label="Windows 10 1507 (Build 10240.17741)"/>
+  <int value="671106437" label="Windows 10 1507 (Build 10240.17797)"/>
+  <int value="671106471" label="Windows 10 1507 (Build 10240.17831)"/>
+  <int value="671106501" label="Windows 10 1507 (Build 10240.17861)"/>
+  <int value="671106529" label="Windows 10 1507 (Build 10240.17889)"/>
+  <int value="671106554" label="Windows 10 1507 (Build 10240.17914)"/>
+  <int value="671106558" label="Windows 10 1507 (Build 10240.17918)"/>
+  <int value="671106586" label="Windows 10 1507 (Build 10240.17946)"/>
+  <int value="671106616" label="Windows 10 1507 (Build 10240.17976)"/>
+  <int value="671106645" label="Windows 10 1507 (Build 10240.18005)"/>
+  <int value="671106676" label="Windows 10 1507 (Build 10240.18036)"/>
+  <int value="671106703" label="Windows 10 1507 (Build 10240.18063)"/>
+  <int value="671106704" label="Windows 10 1507 (Build 10240.18064)"/>
+  <int value="671106734" label="Windows 10 1507 (Build 10240.18094)"/>
+  <int value="671106772" label="Windows 10 1507 (Build 10240.18132)"/>
+  <int value="671106775" label="Windows 10 1507 (Build 10240.18135)"/>
+  <int value="671106798" label="Windows 10 1507 (Build 10240.18158)"/>
+  <int value="671106826" label="Windows 10 1507 (Build 10240.18186)"/>
+  <int value="671106827" label="Windows 10 1507 (Build 10240.18187)"/>
+  <int value="671106855" label="Windows 10 1507 (Build 10240.18215)"/>
+  <int value="671106858" label="Windows 10 1507 (Build 10240.18218)"/>
+  <int value="671106884" label="Windows 10 1507 (Build 10240.18244)"/>
+  <int value="671106915" label="Windows 10 1507 (Build 10240.18275)"/>
+  <int value="693764099" label="Windows 10 1511 (Build 10586.3)"/>
+  <int value="693764107" label="Windows 10 1511 (Build 10586.11)"/>
+  <int value="693764110" label="Windows 10 1511 (Build 10586.14)"/>
+  <int value="693764113" label="Windows 10 1511 (Build 10586.17)"/>
+  <int value="693764125" label="Windows 10 1511 (Build 10586.29)"/>
+  <int value="693764132" label="Windows 10 1511 (Build 10586.36)"/>
+  <int value="693764138" label="Windows 10 1511 (Build 10586.42)"/>
+  <int value="693764159" label="Windows 10 1511 (Build 10586.63)"/>
+  <int value="693764167" label="Windows 10 1511 (Build 10586.71)"/>
+  <int value="693764200" label="Windows 10 1511 (Build 10586.104)"/>
+  <int value="693764218" label="Windows 10 1511 (Build 10586.122)"/>
+  <int value="693764250" label="Windows 10 1511 (Build 10586.154)"/>
+  <int value="693764260" label="Windows 10 1511 (Build 10586.164)"/>
+  <int value="693764314" label="Windows 10 1511 (Build 10586.218)"/>
+  <int value="693764414" label="Windows 10 1511 (Build 10586.318)"/>
+  <int value="693764590" label="Windows 10 1511 (Build 10586.494)"/>
+  <int value="693764641" label="Windows 10 1511 (Build 10586.545)"/>
+  <int value="693764685" label="Windows 10 1511 (Build 10586.589)"/>
+  <int value="693764729" label="Windows 10 1511 (Build 10586.633)"/>
+  <int value="693764775" label="Windows 10 1511 (Build 10586.679)"/>
+  <int value="693764809" label="Windows 10 1511 (Build 10586.713)"/>
+  <int value="693764849" label="Windows 10 1511 (Build 10586.753)"/>
+  <int value="693764935" label="Windows 10 1511 (Build 10586.839)"/>
+  <int value="693764969" label="Windows 10 1511 (Build 10586.873)"/>
+  <int value="693765012" label="Windows 10 1511 (Build 10586.916)"/>
+  <int value="693765058" label="Windows 10 1511 (Build 10586.962)"/>
+  <int value="693765061" label="Windows 10 1511 (Build 10586.965)"/>
+  <int value="693765103" label="Windows 10 1511 (Build 10586.1007)"/>
+  <int value="693765141" label="Windows 10 1511 (Build 10586.1045)"/>
+  <int value="693765202" label="Windows 10 1511 (Build 10586.1106)"/>
+  <int value="693765272" label="Windows 10 1511 (Build 10586.1176)"/>
+  <int value="693765273" label="Windows 10 1511 (Build 10586.1177)"/>
+  <int value="693765328" label="Windows 10 1511 (Build 10586.1232)"/>
+  <int value="693765391" label="Windows 10 1511 (Build 10586.1295)"/>
+  <int value="693765452" label="Windows 10 1511 (Build 10586.1356)"/>
+  <int value="693765454" label="Windows 10 1511 (Build 10586.1358)"/>
+  <int value="693765513" label="Windows 10 1511 (Build 10586.1417)"/>
+  <int value="693765574" label="Windows 10 1511 (Build 10586.1478)"/>
+  <int value="943259649" label="Windows 10 1607 (Build 14393.1)"/>
+  <int value="943259699" label="Windows 10 1607 (Build 14393.51)"/>
+  <int value="943259730" label="Windows 10 1607 (Build 14393.82)"/>
+  <int value="943259753" label="Windows 10 1607 (Build 14393.105)"/>
+  <int value="943259796" label="Windows 10 1607 (Build 14393.148)"/>
+  <int value="943259815" label="Windows 10 1607 (Build 14393.167)"/>
+  <int value="943259825" label="Windows 10 1607 (Build 14393.177)"/>
+  <int value="943259835" label="Windows 10 1607 (Build 14393.187)"/>
+  <int value="943259870" label="Windows 10 1607 (Build 14393.222)"/>
+  <int value="943259891" label="Windows 10 1607 (Build 14393.243)"/>
+  <int value="943259906" label="Windows 10 1607 (Build 14393.258)"/>
+  <int value="943259915" label="Windows 10 1607 (Build 14393.267)"/>
+  <int value="943259969" label="Windows 10 1607 (Build 14393.321)"/>
+  <int value="943259999" label="Windows 10 1607 (Build 14393.351)"/>
+  <int value="943260095" label="Windows 10 1607 (Build 14393.447)"/>
+  <int value="943260127" label="Windows 10 1607 (Build 14393.479)"/>
+  <int value="943260224" label="Windows 10 1607 (Build 14393.576)"/>
+  <int value="943260341" label="Windows 10 1607 (Build 14393.693)"/>
+  <int value="943260601" label="Windows 10 1607 (Build 14393.953)"/>
+  <int value="943260617" label="Windows 10 1607 (Build 14393.969)"/>
+  <int value="943260714" label="Windows 10 1607 (Build 14393.1066)"/>
+  <int value="943260846" label="Windows 10 1607 (Build 14393.1198)"/>
+  <int value="943261006" label="Windows 10 1607 (Build 14393.1358)"/>
+  <int value="943261026" label="Windows 10 1607 (Build 14393.1378)"/>
+  <int value="943261180" label="Windows 10 1607 (Build 14393.1532)"/>
+  <int value="943261185" label="Windows 10 1607 (Build 14393.1537)"/>
+  <int value="943261241" label="Windows 10 1607 (Build 14393.1593)"/>
+  <int value="943261261" label="Windows 10 1607 (Build 14393.1613)"/>
+  <int value="943261363" label="Windows 10 1607 (Build 14393.1715)"/>
+  <int value="943261385" label="Windows 10 1607 (Build 14393.1737)"/>
+  <int value="943261442" label="Windows 10 1607 (Build 14393.1794)"/>
+  <int value="943261445" label="Windows 10 1607 (Build 14393.1797)"/>
+  <int value="943261532" label="Windows 10 1607 (Build 14393.1884)"/>
+  <int value="943261562" label="Windows 10 1607 (Build 14393.1914)"/>
+  <int value="943261592" label="Windows 10 1607 (Build 14393.1944)"/>
+  <int value="943261655" label="Windows 10 1607 (Build 14393.2007)"/>
+  <int value="943261683" label="Windows 10 1607 (Build 14393.2035)"/>
+  <int value="943261716" label="Windows 10 1607 (Build 14393.2068)"/>
+  <int value="943261745" label="Windows 10 1607 (Build 14393.2097)"/>
+  <int value="943261773" label="Windows 10 1607 (Build 14393.2125)"/>
+  <int value="943261803" label="Windows 10 1607 (Build 14393.2155)"/>
+  <int value="943261804" label="Windows 10 1607 (Build 14393.2156)"/>
+  <int value="943261837" label="Windows 10 1607 (Build 14393.2189)"/>
+  <int value="943261862" label="Windows 10 1607 (Build 14393.2214)"/>
+  <int value="943261896" label="Windows 10 1607 (Build 14393.2248)"/>
+  <int value="943261921" label="Windows 10 1607 (Build 14393.2273)"/>
+  <int value="943261960" label="Windows 10 1607 (Build 14393.2312)"/>
+  <int value="943261987" label="Windows 10 1607 (Build 14393.2339)"/>
+  <int value="943262011" label="Windows 10 1607 (Build 14393.2363)"/>
+  <int value="943262016" label="Windows 10 1607 (Build 14393.2368)"/>
+  <int value="943262043" label="Windows 10 1607 (Build 14393.2395)"/>
+  <int value="943262044" label="Windows 10 1607 (Build 14393.2396)"/>
+  <int value="943262105" label="Windows 10 1607 (Build 14393.2457)"/>
+  <int value="943262133" label="Windows 10 1607 (Build 14393.2485)"/>
+  <int value="943262163" label="Windows 10 1607 (Build 14393.2515)"/>
+  <int value="943262199" label="Windows 10 1607 (Build 14393.2551)"/>
+  <int value="943262256" label="Windows 10 1607 (Build 14393.2608)"/>
+  <int value="943262287" label="Windows 10 1607 (Build 14393.2639)"/>
+  <int value="943262289" label="Windows 10 1607 (Build 14393.2641)"/>
+  <int value="943262313" label="Windows 10 1607 (Build 14393.2665)"/>
+  <int value="943262372" label="Windows 10 1607 (Build 14393.2724)"/>
+  <int value="943262407" label="Windows 10 1607 (Build 14393.2759)"/>
+  <int value="943262439" label="Windows 10 1607 (Build 14393.2791)"/>
+  <int value="943262476" label="Windows 10 1607 (Build 14393.2828)"/>
+  <int value="943262496" label="Windows 10 1607 (Build 14393.2848)"/>
+  <int value="943262527" label="Windows 10 1607 (Build 14393.2879)"/>
+  <int value="943262554" label="Windows 10 1607 (Build 14393.2906)"/>
+  <int value="943262556" label="Windows 10 1607 (Build 14393.2908)"/>
+  <int value="943262589" label="Windows 10 1607 (Build 14393.2941)"/>
+  <int value="943262617" label="Windows 10 1607 (Build 14393.2969)"/>
+  <int value="943262620" label="Windows 10 1607 (Build 14393.2972)"/>
+  <int value="943262647" label="Windows 10 1607 (Build 14393.2999)"/>
+  <int value="943262673" label="Windows 10 1607 (Build 14393.3025)"/>
+  <int value="943262701" label="Windows 10 1607 (Build 14393.3053)"/>
+  <int value="943262704" label="Windows 10 1607 (Build 14393.3056)"/>
+  <int value="943262733" label="Windows 10 1607 (Build 14393.3085)"/>
+  <int value="943262763" label="Windows 10 1607 (Build 14393.3115)"/>
+  <int value="987168793" label="Windows 10 1703 (Build 15063.25)"/>
+  <int value="987168822" label="Windows 10 1703 (Build 15063.54)"/>
+  <int value="987168853" label="Windows 10 1703 (Build 15063.85)"/>
+  <int value="987168906" label="Windows 10 1703 (Build 15063.138)"/>
+  <int value="987169064" label="Windows 10 1703 (Build 15063.296)"/>
+  <int value="987169100" label="Windows 10 1703 (Build 15063.332)"/>
+  <int value="987169181" label="Windows 10 1703 (Build 15063.413)"/>
+  <int value="987169215" label="Windows 10 1703 (Build 15063.447)"/>
+  <int value="987169251" label="Windows 10 1703 (Build 15063.483)"/>
+  <int value="987169270" label="Windows 10 1703 (Build 15063.502)"/>
+  <int value="987169376" label="Windows 10 1703 (Build 15063.608)"/>
+  <int value="987169400" label="Windows 10 1703 (Build 15063.632)"/>
+  <int value="987169442" label="Windows 10 1703 (Build 15063.674)"/>
+  <int value="987169443" label="Windows 10 1703 (Build 15063.675)"/>
+  <int value="987169494" label="Windows 10 1703 (Build 15063.726)"/>
+  <int value="987169497" label="Windows 10 1703 (Build 15063.729)"/>
+  <int value="987169554" label="Windows 10 1703 (Build 15063.786)"/>
+  <int value="987169645" label="Windows 10 1703 (Build 15063.877)"/>
+  <int value="987169677" label="Windows 10 1703 (Build 15063.909)"/>
+  <int value="987169704" label="Windows 10 1703 (Build 15063.936)"/>
+  <int value="987169734" label="Windows 10 1703 (Build 15063.966)"/>
+  <int value="987169762" label="Windows 10 1703 (Build 15063.994)"/>
+  <int value="987169797" label="Windows 10 1703 (Build 15063.1029)"/>
+  <int value="987169826" label="Windows 10 1703 (Build 15063.1058)"/>
+  <int value="987169856" label="Windows 10 1703 (Build 15063.1088)"/>
+  <int value="987169880" label="Windows 10 1703 (Build 15063.1112)"/>
+  <int value="987169923" label="Windows 10 1703 (Build 15063.1155)"/>
+  <int value="987169950" label="Windows 10 1703 (Build 15063.1182)"/>
+  <int value="987169974" label="Windows 10 1703 (Build 15063.1206)"/>
+  <int value="987169977" label="Windows 10 1703 (Build 15063.1209)"/>
+  <int value="987170003" label="Windows 10 1703 (Build 15063.1235)"/>
+  <int value="987170034" label="Windows 10 1703 (Build 15063.1266)"/>
+  <int value="987170060" label="Windows 10 1703 (Build 15063.1292)"/>
+  <int value="987170092" label="Windows 10 1703 (Build 15063.1324)"/>
+  <int value="987170124" label="Windows 10 1703 (Build 15063.1356)"/>
+  <int value="987170155" label="Windows 10 1703 (Build 15063.1387)"/>
+  <int value="987170186" label="Windows 10 1703 (Build 15063.1418)"/>
+  <int value="987170214" label="Windows 10 1703 (Build 15063.1446)"/>
+  <int value="987170246" label="Windows 10 1703 (Build 15063.1478)"/>
+  <int value="987170274" label="Windows 10 1703 (Build 15063.1506)"/>
+  <int value="987170276" label="Windows 10 1703 (Build 15063.1508)"/>
+  <int value="987170331" label="Windows 10 1703 (Build 15063.1563)"/>
+  <int value="987170364" label="Windows 10 1703 (Build 15063.1596)"/>
+  <int value="987170399" label="Windows 10 1703 (Build 15063.1631)"/>
+  <int value="987170427" label="Windows 10 1703 (Build 15063.1659)"/>
+  <int value="987170457" label="Windows 10 1703 (Build 15063.1689)"/>
+  <int value="987170484" label="Windows 10 1703 (Build 15063.1716)"/>
+  <int value="987170514" label="Windows 10 1703 (Build 15063.1746)"/>
+  <int value="987170552" label="Windows 10 1703 (Build 15063.1784)"/>
+  <int value="987170553" label="Windows 10 1703 (Build 15063.1785)"/>
+  <int value="987170573" label="Windows 10 1703 (Build 15063.1805)"/>
+  <int value="987170576" label="Windows 10 1703 (Build 15063.1808)"/>
+  <int value="987170607" label="Windows 10 1703 (Build 15063.1839)"/>
+  <int value="987170636" label="Windows 10 1703 (Build 15063.1868)"/>
+  <int value="987170665" label="Windows 10 1703 (Build 15063.1897)"/>
+  <int value="987170666" label="Windows 10 1703 (Build 15063.1898)"/>
+  <int value="987170696" label="Windows 10 1703 (Build 15063.1928)"/>
+  <int value="987170723" label="Windows 10 1703 (Build 15063.1955)"/>
+  <int value="1068171283" label="Windows 10 1709 (Build 16299.19)"/>
+  <int value="1068171328" label="Windows 10 1709 (Build 16299.64)"/>
+  <int value="1068171346" label="Windows 10 1709 (Build 16299.82)"/>
+  <int value="1068171362" label="Windows 10 1709 (Build 16299.98)"/>
+  <int value="1068171379" label="Windows 10 1709 (Build 16299.115)"/>
+  <int value="1068171389" label="Windows 10 1709 (Build 16299.125)"/>
+  <int value="1068171456" label="Windows 10 1709 (Build 16299.192)"/>
+  <int value="1068171458" label="Windows 10 1709 (Build 16299.194)"/>
+  <int value="1068171465" label="Windows 10 1709 (Build 16299.201)"/>
+  <int value="1068171478" label="Windows 10 1709 (Build 16299.214)"/>
+  <int value="1068171512" label="Windows 10 1709 (Build 16299.248)"/>
+  <int value="1068171515" label="Windows 10 1709 (Build 16299.251)"/>
+  <int value="1068171573" label="Windows 10 1709 (Build 16299.309)"/>
+  <int value="1068171598" label="Windows 10 1709 (Build 16299.334)"/>
+  <int value="1068171635" label="Windows 10 1709 (Build 16299.371)"/>
+  <int value="1068171666" label="Windows 10 1709 (Build 16299.402)"/>
+  <int value="1068171695" label="Windows 10 1709 (Build 16299.431)"/>
+  <int value="1068171725" label="Windows 10 1709 (Build 16299.461)"/>
+  <int value="1068171756" label="Windows 10 1709 (Build 16299.492)"/>
+  <int value="1068171786" label="Windows 10 1709 (Build 16299.522)"/>
+  <int value="1068171811" label="Windows 10 1709 (Build 16299.547)"/>
+  <int value="1068171815" label="Windows 10 1709 (Build 16299.551)"/>
+  <int value="1068171843" label="Windows 10 1709 (Build 16299.579)"/>
+  <int value="1068171875" label="Windows 10 1709 (Build 16299.611)"/>
+  <int value="1068171901" label="Windows 10 1709 (Build 16299.637)"/>
+  <int value="1068171929" label="Windows 10 1709 (Build 16299.665)"/>
+  <int value="1068171930" label="Windows 10 1709 (Build 16299.666)"/>
+  <int value="1068171963" label="Windows 10 1709 (Build 16299.699)"/>
+  <int value="1068171990" label="Windows 10 1709 (Build 16299.726)"/>
+  <int value="1068172019" label="Windows 10 1709 (Build 16299.755)"/>
+  <int value="1068172049" label="Windows 10 1709 (Build 16299.785)"/>
+  <int value="1068172110" label="Windows 10 1709 (Build 16299.846)"/>
+  <int value="1068172111" label="Windows 10 1709 (Build 16299.847)"/>
+  <int value="1068172168" label="Windows 10 1709 (Build 16299.904)"/>
+  <int value="1068172200" label="Windows 10 1709 (Build 16299.936)"/>
+  <int value="1068172231" label="Windows 10 1709 (Build 16299.967)"/>
+  <int value="1068172268" label="Windows 10 1709 (Build 16299.1004)"/>
+  <int value="1068172293" label="Windows 10 1709 (Build 16299.1029)"/>
+  <int value="1068172323" label="Windows 10 1709 (Build 16299.1059)"/>
+  <int value="1068172351" label="Windows 10 1709 (Build 16299.1087)"/>
+  <int value="1068172391" label="Windows 10 1709 (Build 16299.1127)"/>
+  <int value="1068172410" label="Windows 10 1709 (Build 16299.1146)"/>
+  <int value="1068172446" label="Windows 10 1709 (Build 16299.1182)"/>
+  <int value="1068172481" label="Windows 10 1709 (Build 16299.1217)"/>
+  <int value="1068172501" label="Windows 10 1709 (Build 16299.1237)"/>
+  <int value="1068172503" label="Windows 10 1709 (Build 16299.1239)"/>
+  <int value="1068172532" label="Windows 10 1709 (Build 16299.1268)"/>
+  <int value="1068172560" label="Windows 10 1709 (Build 16299.1296)"/>
+  <int value="1122893856" label="Windows 10 1803 (Build 17134.32)"/>
+  <int value="1122893872" label="Windows 10 1803 (Build 17134.48)"/>
+  <int value="1122893883" label="Windows 10 1803 (Build 17134.59)"/>
+  <int value="1122893905" label="Windows 10 1803 (Build 17134.81)"/>
+  <int value="1122893907" label="Windows 10 1803 (Build 17134.83)"/>
+  <int value="1122893910" label="Windows 10 1803 (Build 17134.86)"/>
+  <int value="1122893936" label="Windows 10 1803 (Build 17134.112)"/>
+  <int value="1122893961" label="Windows 10 1803 (Build 17134.137)"/>
+  <int value="1122893989" label="Windows 10 1803 (Build 17134.165)"/>
+  <int value="1122893991" label="Windows 10 1803 (Build 17134.167)"/>
+  <int value="1122894015" label="Windows 10 1803 (Build 17134.191)"/>
+  <int value="1122894052" label="Windows 10 1803 (Build 17134.228)"/>
+  <int value="1122894078" label="Windows 10 1803 (Build 17134.254)"/>
+  <int value="1122894109" label="Windows 10 1803 (Build 17134.285)"/>
+  <int value="1122894110" label="Windows 10 1803 (Build 17134.286)"/>
+  <int value="1122894169" label="Windows 10 1803 (Build 17134.345)"/>
+  <int value="1122894200" label="Windows 10 1803 (Build 17134.376)"/>
+  <int value="1122894231" label="Windows 10 1803 (Build 17134.407)"/>
+  <int value="1122894265" label="Windows 10 1803 (Build 17134.441)"/>
+  <int value="1122894295" label="Windows 10 1803 (Build 17134.471)"/>
+  <int value="1122894296" label="Windows 10 1803 (Build 17134.472)"/>
+  <int value="1122894347" label="Windows 10 1803 (Build 17134.523)"/>
+  <int value="1122894380" label="Windows 10 1803 (Build 17134.556)"/>
+  <int value="1122894443" label="Windows 10 1803 (Build 17134.619)"/>
+  <int value="1122894472" label="Windows 10 1803 (Build 17134.648)"/>
+  <int value="1122894501" label="Windows 10 1803 (Build 17134.677)"/>
+  <int value="1122894530" label="Windows 10 1803 (Build 17134.706)"/>
+  <int value="1122894577" label="Windows 10 1803 (Build 17134.753)"/>
+  <int value="1122894589" label="Windows 10 1803 (Build 17134.765)"/>
+  <int value="1122894590" label="Windows 10 1803 (Build 17134.766)"/>
+  <int value="1122894623" label="Windows 10 1803 (Build 17134.799)"/>
+  <int value="1122894653" label="Windows 10 1803 (Build 17134.829)"/>
+  <int value="1122894682" label="Windows 10 1803 (Build 17134.858)"/>
+  <int value="1122894709" label="Windows 10 1803 (Build 17134.885)"/>
+  <int value="1122894739" label="Windows 10 1803 (Build 17134.915)"/>
+  <int value="1164115969" label="Windows 10 1809 (Build 17763.1)"/>
+  <int value="1164116023" label="Windows 10 1809 (Build 17763.55)"/>
+  <int value="1164116075" label="Windows 10 1809 (Build 17763.107)"/>
+  <int value="1164116102" label="Windows 10 1809 (Build 17763.134)"/>
+  <int value="1164116136" label="Windows 10 1809 (Build 17763.168)"/>
+  <int value="1164116162" label="Windows 10 1809 (Build 17763.194)"/>
+  <int value="1164116163" label="Windows 10 1809 (Build 17763.195)"/>
+  <int value="1164116221" label="Windows 10 1809 (Build 17763.253)"/>
+  <int value="1164116260" label="Windows 10 1809 (Build 17763.292)"/>
+  <int value="1164116284" label="Windows 10 1809 (Build 17763.316)"/>
+  <int value="1164116316" label="Windows 10 1809 (Build 17763.348)"/>
+  <int value="1164116347" label="Windows 10 1809 (Build 17763.379)"/>
+  <int value="1164116372" label="Windows 10 1809 (Build 17763.404)"/>
+  <int value="1164116405" label="Windows 10 1809 (Build 17763.437)"/>
+  <int value="1164116407" label="Windows 10 1809 (Build 17763.439)"/>
+  <int value="1164116443" label="Windows 10 1809 (Build 17763.475)"/>
+  <int value="1164116471" label="Windows 10 1809 (Build 17763.503)"/>
+  <int value="1164116472" label="Windows 10 1809 (Build 17763.504)"/>
+  <int value="1164116497" label="Windows 10 1809 (Build 17763.529)"/>
+  <int value="1164116525" label="Windows 10 1809 (Build 17763.557)"/>
+  <int value="1164116560" label="Windows 10 1809 (Build 17763.592)"/>
+  <int value="1164116561" label="Windows 10 1809 (Build 17763.593)"/>
+  <int value="1164116583" label="Windows 10 1809 (Build 17763.615)"/>
+  <int value="1164116620" label="Windows 10 1809 (Build 17763.652)"/>
+  <int value="1203372148" label="Windows 10 1903 (Build 18362.116)"/>
+  <int value="1203372177" label="Windows 10 1903 (Build 18362.145)"/>
+  <int value="1203372207" label="Windows 10 1903 (Build 18362.175)"/>
+  <int value="1203372239" label="Windows 10 1903 (Build 18362.207)"/>
+  <int value="1203372271" label="Windows 10 1903 (Build 18362.239)"/>
+</enum>
+
 <enum name="WindowsToastActivatorCLSIDMismatchReason">
   <obsolete>
     Deprecated 12/2018 as this is no longer needed.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ca9493c..8c92b48 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -56,6 +56,17 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.Android.ScreenReader" enum="BooleanEnabled"
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
+  <owner>aleventhal@chromium.org</owner>
+  <summary>
+    Tracks whether a screen reader is enabled on Android (e.g. Talkback). This
+    is checked once, 45 seconds after startup.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.Android.TabSwitcherPreferenceEnabled"
     enum="BooleanEnabled">
   <owner>twellington@chromium.org</owner>
@@ -461,6 +472,17 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.Mac.ScreenReader" enum="BooleanEnabled"
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
+  <owner>aleventhal@chromium.org</owner>
+  <summary>
+    Tracks whether a screen reader is enabled on Mac (e.g. VoiceOver). This is
+    checked once, 45 seconds after startup.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.ManuallyEnabled" enum="BooleanEnabled"
     expires_after="M85">
   <owner>dmazzoni@chromium.org</owner>
@@ -580,9 +602,12 @@
 </histogram>
 
 <histogram name="Accessibility.WinJAWS" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
+  <owner>aleventhal@chromium.org</owner>
   <summary>
     Whether the third-party JAWS screen reader is running (checked once 45 secs
     after startup).
@@ -590,9 +615,12 @@
 </histogram>
 
 <histogram name="Accessibility.WinNVDA" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
+  <owner>aleventhal@chromium.org</owner>
   <summary>
     Whether the third-party NVDA screen reader is running (checked once 45 secs
     after startup).
@@ -621,19 +649,37 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.WinScreenReader2" enum="BooleanEnabled">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
+  <owner>aleventhal@chromium.org</owner>
+  <summary>
+    Whether the accessibility mode flag shows that a screen reader is running
+    (checked once 45 secs after startup). In this case, Chrome has detected
+    accessibility calls that would normally only occur from a screen reader. See
+    also the more specific metrics such as Accessibility.WinJAWS/WinNVDA.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.WinStickyKeys" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
+  <owner>aleventhal@chromium.org</owner>
   <summary>
     Whether Windows system settings show that Sticky Keys are enabled.
   </summary>
 </histogram>
 
 <histogram name="Accessibility.WinZoomText" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="never">
+<!-- expires-never: usage drives a11y prioritization in browser and content. -->
+
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
+  <owner>aleventhal@chromium.org</owner>
   <summary>
     Whether the third-party ZoomText screen magnifier is running.
   </summary>
@@ -6729,15 +6775,14 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Pip.Events" enum="AshPipEvents" expires_after="2019-09-01">
+<histogram name="Ash.Pip.Events" enum="AshPipEvents" expires_after="M82">
   <owner>edcourtney@chromium.org</owner>
   <summary>
     Various individiual Picture-in-picture related events. See AshPipEvents.
   </summary>
 </histogram>
 
-<histogram name="Ash.Pip.FreeResizeFinishArea" units="%"
-    expires_after="2019-09-01">
+<histogram name="Ash.Pip.FreeResizeFinishArea" units="%" expires_after="M82">
   <owner>edcourtney@chromium.org</owner>
   <summary>
     Area of a Picture-in-picture window when ending a free-resize, as a
@@ -6745,8 +6790,7 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Pip.FreeResizeInitialArea" units="%"
-    expires_after="2019-09-01">
+<histogram name="Ash.Pip.FreeResizeInitialArea" units="%" expires_after="M82">
   <owner>edcourtney@chromium.org</owner>
   <summary>
     Initial area of a Picture-in-picture window when beginning a free-resize, as
@@ -6754,8 +6798,7 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.Pip.Position" enum="AshPipPosition"
-    expires_after="2019-09-01">
+<histogram name="Ash.Pip.Position" enum="AshPipPosition" expires_after="M82">
   <owner>edcourtney@chromium.org</owner>
   <summary>
     The position that a Picture-in-picture window was moved to by a user drag.
@@ -11871,6 +11914,74 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Availability.Prober.CacheEntryAge" units="hours"
+    expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    How old a cached probe result was when it was used, in hours.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.DidSucceed"
+    enum="BooleanSuccess" expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>Records the completion status of a probe when it completes.</summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.NetError" enum="NetErrorCodes"
+    expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the net error of a completed or timed out probe. Note that if a HTTP
+    response does not occur within the probe's TTL, when a sample will also be
+    added to the ERR_TIMED_OUT bucket.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.NumAttemptsBeforeSuccess"
+    units="count" expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the number of attempts that are made to achieve a successful probe
+    result. Only recorded on success. This metric is 1-based so if a probe
+    succeeds the first time, a sample of 1 will be recorded.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.ResponseCode"
+    enum="HttpResponseCode" expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the HTTP response code of a completed probe, when a HTTP response is
+    received.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.TimeUntilFailure" units="ms"
+    expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the amount of time spent working on a single probe attempt to get to
+    a failed state. Recorded every time a probe fails.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Availability.Prober.TimeUntilSuccess" units="ms"
+    expires_after="2020-08-01">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    Records the amount of time spent working on a single probe attempt to get to
+    a successful state. Recorded every time a probe succeeds.
+  </summary>
+</histogram>
+
 <histogram base="true" name="BackgroundFetch.EventDispatchFailure.Dispatch"
     enum="ServiceWorkerStatusCode">
 <!-- Name completed by histogram_suffixes name="BackgroundFetchEvents" -->
@@ -18436,20 +18547,41 @@
 <histogram name="ChromeColors.AppliedColor" enum="ChromeColorsInfo"
     expires_after="2019-12-31">
   <owner>gayane@chromium.org</owner>
+  <owner>yyushkina@chromium.org</owner>
   <summary>
     Records applied color id every time its applied from Colors menu.
   </summary>
 </histogram>
 
+<histogram name="ChromeColors.ChangesConfirmed"
+    enum="BooleanChromeColorsChangesConfirmed" expires_after="2019-12-31">
+  <owner>gayane@chromium.org</owner>
+  <owner>yyushkina@chromium.org</owner>
+  <summary>
+    Records whether color/theme changes were confirmed or reverted by
+    ChromeColorsService.
+  </summary>
+</histogram>
+
 <histogram name="ChromeColors.ColorOnLoad" enum="ChromeColorsInfo"
     expires_after="2019-12-31">
   <owner>gayane@chromium.org</owner>
+  <owner>yyushkina@chromium.org</owner>
   <summary>
     Records installed color id every time theme service is initialized from
     prefs. Happens once per profile load.
   </summary>
 </histogram>
 
+<histogram name="ChromeColors.RevertReason" enum="ChromeColorsRevertReason"
+    expires_after="2019-12-31">
+  <owner>gayane@chromium.org</owner>
+  <owner>yyushkina@chromium.org</owner>
+  <summary>
+    Records the reason color/theme changes were reverted by ChromeColorsService.
+  </summary>
+</histogram>
+
 <histogram name="ChromeElf.ApplyHookResult" enum="NTSTATUS" expires_after="M77">
   <owner>pmonette@chromium.org</owner>
   <summary>
@@ -32864,7 +32996,7 @@
 </histogram>
 
 <histogram name="DrmUtil.CreateDisplaySnapshot.HasEdidBlob" enum="Boolean"
-    expires_after="M77">
+    expires_after="2020-07-24">
   <owner>andrescj@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
   <summary>
@@ -32874,7 +33006,7 @@
 </histogram>
 
 <histogram name="DrmUtil.GetColorSpaceFromEdid.ChecksOutcome"
-    enum="EdidColorSpaceChecksOutcome" expires_after="M77">
+    enum="EdidColorSpaceChecksOutcome" expires_after="2020-07-24">
   <owner>andrescj@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
   <summary>
@@ -38841,9 +38973,9 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.BlacklistInstalled"
-    enum="ExtensionLocation">
-  <owner>vakh@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
+    enum="ExtensionLocation" expires_after="M78">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The number of extensions that were blacklisted when already installed,
     grouped by Extension::Location. Logged when ExtensionService blackists and
@@ -38852,9 +38984,9 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.BlockCRX" enum="ExtensionLocation"
-    expires_after="M77">
-  <owner>vakh@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
+    expires_after="M78">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The number of extensions that have been blocked from installing grouped by
     Extension::Location. Logged when ExtensionService refuses to install a
@@ -38862,9 +38994,10 @@
   </summary>
 </histogram>
 
-<histogram name="ExtensionBlacklist.SilentInstall" enum="ExtensionLocation">
-  <owner>vakh@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
+<histogram name="ExtensionBlacklist.SilentInstall" enum="ExtensionLocation"
+    expires_after="M78">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The number of extensions that have been silently installed in a blacklisted
     state, grouped by Extension::Location. Logged when ExtensionService installs
@@ -38875,9 +39008,9 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.UnblacklistInstalled"
-    enum="ExtensionLocation" expires_after="M77">
-  <owner>vakh@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
+    enum="ExtensionLocation" expires_after="M78">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The number of extensions that were unblacklisted when installed, grouped by
     Extension::Location. Logged when ExtensionService unblacklists and loads a
@@ -40195,6 +40328,9 @@
 <histogram
     name="Extensions.DeclarativeWebRequest.WebViewRequestDeclarativeRules"
     enum="BooleanDeclarativeRules" expires_after="M78">
+  <obsolete>
+    Removed July 2019.
+  </obsolete>
   <owner>karandeepb@chromium.org</owner>
   <summary>
     Whether a network request from a guest webview has any declarative web
@@ -53660,6 +53796,175 @@
   </summary>
 </histogram>
 
+<histogram name="Kerberos.AcquireKerberosTgtTime" units="ms"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Time in milliseconds to acquire a Kerberos ticket. The value is recorded no
+    matter if the operation was successful or not.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.DailyActiveUsers" enum="KerberosUserType"
+    expires_after="2021-05-31">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Type of the Chrome OS login user (primary user) who requests Kerberos
+    tickets. Reported at most once a day by the Kerberos daemon when a Kebreros
+    ticket is acquired or Kerberos files are requested, which happens e.g. on
+    Chrome startup.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.NumberOfAccounts.Managed" expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Number of managed Kerberos accounts owned by the current Chrome OS user. A
+    managed account is an account that got added via the KerberosAccounts
+    policy. Reported at most once a day similar to Kerberos.DailyActiveUsers.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.NumberOfAccounts.RememberedPassword"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Number of Kerberos accounts owned by the current Chrome OS user that use the
+    'Remember password' feature, i.e. the account password is stored by the
+    Kerberos daemon. Reported at most once a day similar to
+    Kerberos.DailyActiveUsers.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.NumberOfAccounts.Total" expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Total number of Kerberos accounts owned by the current Chrome OS user.
+    Reported at most once a day similar to Kerberos.DailyActiveUsers.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.NumberOfAccounts.Unmanaged" expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Number of unmanaged Kerberos accounts owned by the current Chrome OS user.
+    An unmanaged account is an account that was added manually by the user.
+    Reported at most once a day similar to Kerberos.DailyActiveUsers.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.NumberOfAccounts.UseLoginPassword"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Number of Kerberos accounts owned by the current Chrome OS user that use the
+    login password for authentication. These accounts were added via the
+    KerberosAccounts policy, using the '${PASSWORD}' placeholder as password.
+    Reported at most once a day similar to Kerberos.DailyActiveUsers.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.AcquireKerberosTgt" enum="KerberosErrorType"
+    expires_after="2021-05-31">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to acquire a Kerberos ticket. This happens whenever a
+    new ticket is added or an existing ticket is reauthenticated.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.AddAccount" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to add a new Kerberos account, either through the
+    Kerberos Tickets settings page or via the KerberosAccounts policy.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.ClearAccounts" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to clear Kerberos accounts, usually through toggling
+    Kerberos related policies. For instance, disabling the KerberosEnabled
+    policy clears all accounts.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.GetKerberosFiles" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to retrieve a Kerberos ticket from the Kerberos
+    daemon. Systems using Kerberos (Chrome, SMB file shares) perform this action
+    during startup and when they get notified that the files changed.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.ListAccounts" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to list Kerberos accounts, usually through opening
+    the Kerberos Tickets settings page or making changes to accounts.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.RemoveAccount" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to remove a new Kerberos account through the Kerberos
+    Tickets settings page.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.SetConfig" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to set Kerberos configuration. This happens right
+    before acquiring a Kerberos ticket.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.Result.ValidateConfig" enum="KerberosErrorType"
+    expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Result from an attempt to validate Kerberos configuration. This happens when
+    the user tries to set configuration in the Advanced section of the dialog to
+    add new Kerberos tickets.
+  </summary>
+</histogram>
+
+<histogram name="Kerberos.ValidateConfigErrorCode"
+    enum="KerberosConfigErrorCode" expires_after="M83">
+  <owner>ljusten@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Error code from an attempt validate Kerberos configuration. Contains more
+    specific information than KerberosErrorType about the result of the
+    validation.
+  </summary>
+</histogram>
+
 <histogram name="Keyboard.KeystrokeDeltas" units="ms"
     expires_after="2016-07-28">
   <obsolete>
@@ -105492,74 +105797,6 @@
   <summary>Records why the page load ended on a given preview type.</summary>
 </histogram>
 
-<histogram base="true" name="Previews.Prober.CacheEntryAge" units="hours"
-    expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    How old a cached probe result was when it was used, in hours.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.DidSucceed" enum="BooleanSuccess"
-    expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>Records the completion status of a probe when it completes.</summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.NetError" enum="NetErrorCodes"
-    expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Records the net error of a completed or timed out probe. Note that if a HTTP
-    response does not occur within the probe's TTL, when a sample will also be
-    added to the ERR_TIMED_OUT bucket.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.NumAttemptsBeforeSuccess"
-    units="count" expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Records the number of attempts that are made to achieve a successful probe
-    result. Only recorded on success. This metric is 1-based so if a probe
-    succeeds the first time, a sample of 1 will be recorded.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.ResponseCode"
-    enum="HttpResponseCode" expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Records the HTTP response code of a completed probe, when a HTTP response is
-    received.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.TimeUntilFailure" units="ms"
-    expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Records the amount of time spent working on a single probe attempt to get to
-    a failed state. Recorded every time a probe fails.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Previews.Prober.TimeUntilSuccess" units="ms"
-    expires_after="2020-08-01">
-  <owner>robertogden@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Records the amount of time spent working on a single probe attempt to get to
-    a successful state. Recorded every time a probe succeeds.
-  </summary>
-</histogram>
-
 <histogram name="Previews.ProcessHintsResult" enum="PreviewsProcessHintsResult">
   <obsolete>
     Merged with OptimizationGuide.ProcessHintsResult in 07/2019.
@@ -114079,7 +114316,10 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Store.IsStoreAvailable.ValidStore"
-    enum="BooleanAvailable" expires_after="M78">
+    enum="BooleanAvailable" expires_after="M77">
+  <obsolete>
+    Removed in M77 since the histogram was very stable: crbug.com/984286.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -115326,7 +115566,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CanUseLocalBlacklists"
-    enum="SB2RemoteCallCanUseLocalBlacklists" expires_after="M77">
+    enum="SB2RemoteCallCanUseLocalBlacklists" expires_after="M81">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -115414,7 +115654,10 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.InternalErrorStatusCode2"
-    enum="GooglePlayServicesConnectionResult" expires_after="M77">
+    enum="GooglePlayServicesConnectionResult" expires_after="never">
+<!-- expires-never: This tracks the result of connecting to GmsCore for
+Safe Browsing lookups, which is critical for security. -->
+
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -115427,7 +115670,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.LocalBlacklistsAgeInMs" units="ms"
-    expires_after="M77">
+    expires_after="M81">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -115450,7 +115693,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.LocalBlacklistsUpdateSucceeded"
-    enum="BooleanSuccess" expires_after="M77">
+    enum="BooleanSuccess" expires_after="M81">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -122952,6 +123195,18 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorkerCache.PeakParallelSharedOps" units="operations"
+    expires_after="M85">
+  <owner>wanderview@chromium.org</owner>
+  <owner>chrome-owp-storage@google.com</owner>
+  <summary>
+    The peak number of shared operations that ran simultaneously during a single
+    &quot;batch&quot; of operations. A batch is defined as the time from when an
+    idle scheduler begins running a shared operation until the count of running
+    shared operations drops back to zero.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorkerCache.Response.HasDeprecatedURL" enum="Boolean"
     expires_after="M77">
   <obsolete>
@@ -125017,6 +125272,8 @@
 
 <histogram name="Sharing.ClickToCallAppsToShow" units="apps"
     expires_after="2020-02-02">
+<!-- Name completed by histogram_suffixes name="SharingClickToCallUi" -->
+
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -125027,6 +125284,8 @@
 
 <histogram name="Sharing.ClickToCallDevicesToShow" units="devices"
     expires_after="2020-02-02">
+<!-- Name completed by histogram_suffixes name="SharingClickToCallUi" -->
+
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -125046,6 +125305,16 @@
   </summary>
 </histogram>
 
+<histogram name="Sharing.ClickToCallDialogShown"
+    enum="SharingClickToCallDialogType" expires_after="2020-02-02">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>knollr@chromium.org</owner>
+  <summary>
+    Logged whenever a dialog is shown for the Click to Call feature. Desktop
+    only.
+  </summary>
+</histogram>
+
 <histogram name="Sharing.ClickToCallSelectedAppIndex" units="index"
     expires_after="2020-02-02">
 <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" -->
@@ -125562,21 +125831,14 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Signin.AndroidGetAccountsTime" units="ms"
-    expires_after="M81">
-  <owner>bsazonov@chromium.org</owner>
-  <owner>alexilin@chromium.org</owner>
+<histogram name="Signin.AndroidGetAccountsTime" units="ms">
+  <owner>nyquist@chromium.org</owner>
   <summary>
-    The time it takes to populate or update the accounts cache in
-    AccountManagerFacade.
+    The time it takes to retrieve the list of accounts from the system.
   </summary>
 </histogram>
 
 <histogram name="Signin.AndroidGetAccountsTimeUiThread" units="ms">
-  <obsolete>
-    Removed in 07/2019. The list of accounts is no longer retrieved on the UI
-    thread.
-  </obsolete>
   <owner>bsazonov@chromium.org</owner>
   <summary>
     The time it takes to retrieve the list of accounts from the system on the UI
@@ -134909,6 +135171,9 @@
 
 <histogram name="Sync.MemoryPressureWarningBeforeCleanShutdown" units="count"
     expires_after="M77">
+  <obsolete>
+    Removed 2019-07.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     Counts the number of times a user's sync service received a
@@ -134925,6 +135190,9 @@
 
 <histogram name="Sync.MemoryPressureWarningBeforeUncleanShutdown" units="count"
     expires_after="M77">
+  <obsolete>
+    Removed 2019-07.
+  </obsolete>
   <owner>gangwu@chromium.org</owner>
   <summary>
     Counts the number of times a user's sync service received a
@@ -139864,7 +140132,7 @@
   </summary>
 </histogram>
 
-<histogram name="Thumbnails.CopyFromSurfaceTime" units="ms" expires_after="M78">
+<histogram name="Thumbnails.CopyFromSurfaceTime" units="ms" expires_after="M81">
   <owner>dfried@chromium.org</owner>
   <owner>pbos@chromium.org</owner>
   <summary>
@@ -139895,7 +140163,7 @@
   </summary>
 </histogram>
 
-<histogram name="Thumbnails.ProcessBitmapTime" units="ms" expires_after="M78">
+<histogram name="Thumbnails.ProcessBitmapTime" units="ms" expires_after="M81">
   <owner>dfried@chromium.org</owner>
   <owner>pbos@chromium.org</owner>
   <summary>
@@ -140307,6 +140575,16 @@
   </summary>
 </histogram>
 
+<histogram name="Tracing.Background.FinalizationDisallowedReason"
+    enum="TracingFinalizationDisallowedReason" expires_after="2020-01-30">
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Reason why background tracing finalization was not allowed. Also see
+    &quot;Tracing.Background.ScenarioState&quot; metric, which records the total
+    number of times finalization was allowed and not allowed.
+  </summary>
+</histogram>
+
 <histogram name="Tracing.Background.FinalizingTraceSizeInKB" units="KB">
   <owner>oysteine@chromium.org</owner>
   <summary>
@@ -140314,6 +140592,24 @@
   </summary>
 </histogram>
 
+<histogram name="Tracing.Background.NetworkConnectionTypeWhenStarted"
+    enum="NetworkConnectionType" expires_after="2019-10-10">
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Connection type of the network used to determine the trace buffer size, when
+    background tracing was triggered. Recorded only on Android.
+  </summary>
+</histogram>
+
+<histogram name="Tracing.Background.NetworkConnectionTypeWhenUploaded"
+    enum="NetworkConnectionType" expires_after="2019-10-10">
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    Connection type of the network used to determine the trace upload limit,
+    when background tracing upload was started. Recorded only on Android.
+  </summary>
+</histogram>
+
 <histogram name="Tracing.Background.ScenarioState"
     enum="BackgroundTracingState">
   <owner>oysteine@chromium.org</owner>
@@ -142553,7 +142849,8 @@
 
 <histogram name="UnifiedConsent.SyncAndGoogleServicesSettings"
     enum="UnifiedConsentSyncAndGoogleServicesSettings"
-    expires_after="2019-08-21">
+    expires_after="2019-12-21">
+  <owner>msarda@chromium.org</owner>
   <owner>tangltom@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -144795,6 +145092,20 @@
   </summary>
 </histogram>
 
+<histogram name="V8.TurboFan1KTicks" units="1000 ticks"
+    expires_after="2020-01-23">
+  <owner>tebbi@chromium.org</owner>
+  <owner>mvstanton@chromium.org</owner>
+  <summary>
+    Number of ticks (in 1000s of ticks) from starting optimizing to installing
+    the code object. Recorded on each regular optimization of a function.
+
+    Similar to V8.TurboFanOptimizeTotalTime, but instead of counting time, we
+    count a deterministic number of ticks sprinkled throughout the Turbofan
+    compiler.
+  </summary>
+</histogram>
+
 <histogram name="V8.TurboFanOptimizeExecute" units="microseconds"
     expires_after="M80">
   <owner>bmeurer@chromium.org</owner>
@@ -153557,6 +153868,19 @@
   </summary>
 </histogram>
 
+<histogram name="Windows.PatchLevel" enum="WindowsPatchLevel">
+  <owner>wfh@chromium.org</owner>
+  <owner>brucedawson@chromium.org</owner>
+  <summary>
+    A 32-bit value formed from combining the minor and patch level of the
+    currently running Windows operating system. E.g. &quot;Windows 10 OS Version
+    1809 (Build 17763.503)&quot; would be 17763 (0x4563), combined with 503
+    (0x1F7) = 0x456301F7. If either minor or patch level exceeds the value that
+    can fit in a 16-bit unsigned integer, then this histogram will report 0.
+    Reported once, shortly after browser startup.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Windows.PostOperationState"
     enum="PostOperationState">
   <owner>grt@chromium.org</owner>
@@ -154769,24 +155093,11 @@
   <affected-histogram name="Bluetooth.Web.Android"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="AndroidGetAccountsAPITypes" separator="_">
-  <suffix base="true" name="AccountManager"
-      label="Using Android AccountManager API."/>
-  <suffix base="true" name="GoogleAuthUtil" label="Using GoogleAuthUtil API."/>
+<histogram_suffixes name="AndroidGetAccountsTypes" separator="_">
+  <suffix name="AccountManager" label="Using Android AccountManager API"/>
+  <suffix name="GoogleAuthUtil" label="Using GoogleAuthUtil API"/>
   <affected-histogram name="Signin.AndroidGetAccountsTime"/>
-  <affected-histogram name="Signin.AndroidGetAccountsTimeUiThread">
-    <obsolete>
-      Removed in 07/2019.
-    </obsolete>
-  </affected-histogram>
-</histogram_suffixes>
-
-<histogram_suffixes name="AndroidGetAccountsDurationTypes" separator=".">
-  <suffix name="Accounts" label="Obtaining a list of all accounts."/>
-  <suffix name="Ids" label="Getting account ids for all accounts."/>
-  <suffix name="Total" label="Total duration."/>
-  <affected-histogram name="Signin.AndroidGetAccountsTime_AccountManager"/>
-  <affected-histogram name="Signin.AndroidGetAccountsTime_GoogleAuthUtil"/>
+  <affected-histogram name="Signin.AndroidGetAccountsTimeUiThread"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AndroidProcessType" separator=".">
@@ -155328,6 +155639,17 @@
   <affected-histogram name="Autofill.WalletCards2"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="Availability.Prober.Clients" separator=".">
+  <suffix name="Litepages" label="Lite page HTTPS Server Previews"/>
+  <affected-histogram name="Availability.Prober.CacheEntryAge"/>
+  <affected-histogram name="Availability.Prober.DidSucceed"/>
+  <affected-histogram name="Availability.Prober.NetError"/>
+  <affected-histogram name="Availability.Prober.NumAttemptsBeforeSuccess"/>
+  <affected-histogram name="Availability.Prober.ResponseCode"/>
+  <affected-histogram name="Availability.Prober.TimeUntilFailure"/>
+  <affected-histogram name="Availability.Prober.TimeUntilSuccess"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="BackgroundFetchDatabaseStorageErrors" separator=".">
   <suffix name="CleanupTask" label="CleanupTask"/>
   <suffix name="CreateMetadataTask" label="CreateMetadata DatabaseTask"/>
@@ -165326,17 +165648,6 @@
   <affected-histogram name="Prerender.TimeUntilUsed2"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="Previews.Prober.Clients" separator=".">
-  <suffix name="Litepages" label="Lite page HTTPS Server Previews"/>
-  <affected-histogram name="Previews.Prober.CacheEntryAge"/>
-  <affected-histogram name="Previews.Prober.DidSucceed"/>
-  <affected-histogram name="Previews.Prober.NetError"/>
-  <affected-histogram name="Previews.Prober.NumAttemptsBeforeSuccess"/>
-  <affected-histogram name="Previews.Prober.ResponseCode"/>
-  <affected-histogram name="Previews.Prober.TimeUntilFailure"/>
-  <affected-histogram name="Previews.Prober.TimeUntilSuccess"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="Previews.ServerLitePage.Penalty.Types" separator=".">
   <suffix name="Bypass" label="Bypass"/>
   <suffix name="Failure" label="Failure"/>
@@ -167656,6 +167967,8 @@
 <histogram_suffixes name="SharingClickToCallUi" separator=".">
   <suffix name="ContextMenu" label="Context menu"/>
   <suffix name="Dialog" label="Dialog"/>
+  <affected-histogram name="Sharing.ClickToCallAppsToShow"/>
+  <affected-histogram name="Sharing.ClickToCallDevicesToShow"/>
   <affected-histogram name="Sharing.ClickToCallSelectedAppIndex"/>
   <affected-histogram name="Sharing.ClickToCallSelectedDeviceIndex"/>
 </histogram_suffixes>
@@ -168473,6 +168786,7 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="SystemNotificationAgeType" separator=".">
+  <suffix name="ClickToCall" label="Click To Call"/>
   <suffix name="SendTabToSelf" label="Send Tab To Self"/>
   <affected-histogram name="Mobile.SystemNotification.Action.Click.Age"/>
   <affected-histogram name="Mobile.SystemNotification.Content.Click.Age"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 61948da3..1074ce8b 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -678,6 +678,237 @@
   </metric>
 </event>
 
+<event name="AppListNonAppImpression">
+  <owner>jiameng@chromium.org</owner>
+  <owner>thanhdng@chromium.org</owner>
+  <owner>tby@chromium.org</owner>
+  <summary>
+    Records details of impressions and launches of non-apps in the ChromeOS
+    launcher, such as files, omnibox results, and answer cards.
+  </summary>
+  <metric name="Category" enum="AppListNonAppImpressionCategory">
+    <summary>
+      The kind of launcher item. Corresponds to the value of
+      ChromeSearchResult::result_[sub]type.
+    </summary>
+  </metric>
+  <metric name="DayOfWeek">
+    <summary>
+      The day of the week that the event was logged on, in the local time zone.
+      Sunday is 0.
+    </summary>
+  </metric>
+  <metric name="DeviceMode" enum="AppListNonAppImpressionDeviceMode">
+    <summary>
+      The mode of the device.
+    </summary>
+  </metric>
+  <metric name="EventId">
+    <summary>
+      An ID linking together events within a single set of launcher search
+      results. One of these may be a launch, and the rest impressions.
+    </summary>
+  </metric>
+  <metric name="FileExtension" enum="AppListNonAppImpressionFileExtension">
+    <summary>
+      An enum representing the file extension if this event represents a local
+      or DriveFS file.
+    </summary>
+  </metric>
+  <metric name="HourOfDay">
+    <summary>
+      The hour of the day that the event was logged in. Hours measured from
+      midnight in the local timezone.
+    </summary>
+  </metric>
+  <metric name="IsLaunched" enum="Boolean">
+    <summary>
+      True if the item represented by this event was launched, false otherwise.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour00">
+    <summary>
+      The number of launches within the last 24 hours at hour 0. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour01">
+    <summary>
+      The number of launches within the last 24 hours at hour 1. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour02">
+    <summary>
+      The number of launches within the last 24 hours at hour 2. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour03">
+    <summary>
+      The number of launches within the last 24 hours at hour 3. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour04">
+    <summary>
+      The number of launches within the last 24 hours at hour 4. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour05">
+    <summary>
+      The number of launches within the last 24 hours at hour 5. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour06">
+    <summary>
+      The number of launches within the last 24 hours at hour 6. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour07">
+    <summary>
+      The number of launches within the last 24 hours at hour 7. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour08">
+    <summary>
+      The number of launches within the last 24 hours at hour 8. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour09">
+    <summary>
+      The number of launches within the last 24 hours at hour 9. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour10">
+    <summary>
+      The number of launches within the last 24 hours at hour 10. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour11">
+    <summary>
+      The number of launches within the last 24 hours at hour 11. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour12">
+    <summary>
+      The number of launches within the last 24 hours at hour 12. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour13">
+    <summary>
+      The number of launches within the last 24 hours at hour 13. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour14">
+    <summary>
+      The number of launches within the last 24 hours at hour 14. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour15">
+    <summary>
+      The number of launches within the last 24 hours at hour 15. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour16">
+    <summary>
+      The number of launches within the last 24 hours at hour 16. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour17">
+    <summary>
+      The number of launches within the last 24 hours at hour 17. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour18">
+    <summary>
+      The number of launches within the last 24 hours at hour 18. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour19">
+    <summary>
+      The number of launches within the last 24 hours at hour 19. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour20">
+    <summary>
+      The number of launches within the last 24 hours at hour 20. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour21">
+    <summary>
+      The number of launches within the last 24 hours at hour 21. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour22">
+    <summary>
+      The number of launches within the last 24 hours at hour 22. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesAtHour23">
+    <summary>
+      The number of launches within the last 24 hours at hour 23. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="LaunchesThisSession">
+    <summary>
+      How many times this item has been launched during this session. Rounded
+      down to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="Position">
+    <summary>
+      The index of this item in the search results list. Indices start at 0.
+    </summary>
+  </metric>
+  <metric name="QueryLength">
+    <summary>
+      The length of the raw search query with whitespace trimmed. Rounded down
+      to the nearest exponential bucket, with a bucket ratio of 1.15.
+    </summary>
+  </metric>
+  <metric name="RelevanceScore">
+    <summary>
+      The raw relevance score returned by the relevant search provider for this
+      item.
+    </summary>
+  </metric>
+  <metric name="TimeOfLastLaunch">
+    <summary>
+      The hour when the previous launch of this item occurred. Hours measured
+      from midnight in the local timezone. This can be any value 0-23, as the
+      last launch could have been on a previous day.
+    </summary>
+  </metric>
+  <metric name="TimeSinceLastLaunch">
+    <summary>
+      Seconds since this item was last launched in this session. Rounded down to
+      the nearest exponential bucket, with a bucket ratio of 1.045. This makes
+      the bucket at 24 hour mark approximately 1 hour long.
+    </summary>
+  </metric>
+</event>
+
 <event name="Autofill.CardUploadDecision">
   <owner>sebsg@chromium.org</owner>
   <metric name="UploadDecision">
diff --git a/tools/perf/scripts_smoke_unittest.py b/tools/perf/scripts_smoke_unittest.py
index d08bff42..c3f31c2 100644
--- a/tools/perf/scripts_smoke_unittest.py
+++ b/tools/perf/scripts_smoke_unittest.py
@@ -33,8 +33,6 @@
     return_code = proc.returncode
     return return_code, stdout
 
-  # TODO(crbug.com/985712): Re-enable after new command line interface lands.
-  @decorators.Disabled('all')
   def testRunBenchmarkHelp(self):
     return_code, stdout = self.RunPerfScript('run_benchmark --help')
     self.assertEquals(return_code, 0, stdout)
@@ -45,8 +43,6 @@
     self.assertIn('Pass --browser to list benchmarks', stdout)
     self.assertNotEquals(return_code, 0)
 
-  # TODO(crbug.com/985712): Re-enable after new command line interface lands.
-  @decorators.Disabled('all')
   def testRunBenchmarkRunNonExistingBenchmark(self):
     return_code, stdout = self.RunPerfScript('run_benchmark foo')
     self.assertIn('no such benchmark: foo', stdout)
diff --git a/tools/polymer/BUILD.gn b/tools/polymer/BUILD.gn
new file mode 100644
index 0000000..092066b
--- /dev/null
+++ b/tools/polymer/BUILD.gn
@@ -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.
+
+group("polymer_tools_python_unittests") {
+  testonly = true
+
+  data = [
+    "//testing/scripts/common.py",
+    "//testing/scripts/run_isolated_script_test.py",
+    "//testing/xvfb.py",
+    "//tools/polymer/",
+    "//third_party/catapult/third_party/typ/",
+  ]
+}
diff --git a/tools/polymer/run_polymer_tools_tests.py b/tools/polymer/run_polymer_tools_tests.py
new file mode 100755
index 0000000..6a258b0e
--- /dev/null
+++ b/tools/polymer/run_polymer_tools_tests.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Unit test suite that collects all test cases for Polymer.'''
+
+import os
+import sys
+
+CUR_DIR = os.path.dirname(os.path.realpath(__file__))
+SRC_DIR = os.path.dirname(os.path.dirname(CUR_DIR))
+TYP_DIR = os.path.join(
+    SRC_DIR, 'third_party', 'catapult', 'third_party', 'typ')
+
+if TYP_DIR not in sys.path:
+    sys.path.insert(0, TYP_DIR)
+
+import typ
+
+
+def main(args):
+    os.chdir(CUR_DIR)
+    return typ.main(top_level_dirs=[CUR_DIR])
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
+
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 6181c08..300eeeb 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -159,7 +159,7 @@
  <item id="ntp_custom_link_checker_request" hash_code="78408551" type="0" deprecated="2018-10-26" content_hash_code="13407730" file_path=""/>
  <item id="ntp_icon_source" hash_code="29197139" type="0" content_hash_code="16399294" os_list="linux,windows" file_path="chrome/browser/search/ntp_icon_source.cc"/>
  <item id="ntp_snippets_fetch" hash_code="15418154" type="0" content_hash_code="10078959" os_list="linux,windows" file_path="components/ntp_snippets/remote/json_request.cc"/>
- <item id="nux_ntp_background_preview" hash_code="124847649" type="0" content_hash_code="31404656" os_list="linux,windows" file_path="chrome/browser/ui/webui/welcome/nux/ntp_background_fetcher.cc"/>
+ <item id="nux_ntp_background_preview" hash_code="124847649" type="0" content_hash_code="31404656" os_list="linux,windows" file_path="chrome/browser/ui/webui/welcome/ntp_background_fetcher.cc"/>
  <item id="oauth2_access_token_fetcher" hash_code="27915688" type="0" content_hash_code="33501872" os_list="linux,windows" file_path="google_apis/gaia/oauth2_access_token_fetcher_impl.cc"/>
  <item id="oauth2_api_call_flow" hash_code="29188932" type="2" content_hash_code="108831236" os_list="linux,windows" policy_fields="-1" file_path="google_apis/gaia/oauth2_api_call_flow.cc"/>
  <item id="oauth2_mint_token_flow" hash_code="1112842" type="1" second_id="29188932" content_hash_code="91581432" os_list="linux,windows" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="google_apis/gaia/oauth2_mint_token_flow.cc"/>
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index 6aed3474..d0cd8be8 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -162,6 +162,9 @@
   // Invalidate the subtree rooted at this node.
   void InvalidateClientSubtree(ClientTreeNode* client_node);
 
+  // Delete all descendants of this node.
+  void DeleteDescendants(ClientTreeNode* client_node);
+
   // Delete the client subtree rooted at this node.
   void DeleteClientSubtree(ClientTreeNode* client_node);
 
@@ -406,7 +409,7 @@
         out_update->node_id_to_clear = tree_->GetId(lca);
         ClientTreeNode* client_lca = ClientTreeNodeById(tree_->GetId(lca));
         CHECK(client_lca);
-        DeleteClientSubtree(client_lca);
+        DeleteDescendants(client_lca);
       }
     }
   } while (need_delete);
@@ -462,11 +465,20 @@
 template <typename AXSourceNode, typename AXNodeData, typename AXTreeData>
 void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::
     DeleteClientSubtree(ClientTreeNode* client_node) {
-  for (size_t i = 0; i < client_node->children.size(); ++i) {
-    client_id_map_.erase(client_node->children[i]->id);
-    DeleteClientSubtree(client_node->children[i]);
-    delete client_node->children[i];
+  if (client_node == client_root_) {
+    Reset();  // Do not try to reuse a bad root later.
+  } else {
+    DeleteDescendants(client_node);
+    client_id_map_.erase(client_node->id);
+    delete client_node;
   }
+}
+
+template <typename AXSourceNode, typename AXNodeData, typename AXTreeData>
+void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::DeleteDescendants(
+    ClientTreeNode* client_node) {
+  for (size_t i = 0; i < client_node->children.size(); ++i)
+    DeleteClientSubtree(client_node->children[i]);
   client_node->children.clear();
 }
 
@@ -531,7 +543,8 @@
 
     // There shouldn't be any reparenting because we've already handled it
     // above. If this happens, reset and return an error.
-    ClientTreeNode* client_child = client_id_map_[new_child_id];
+
+    ClientTreeNode* client_child = ClientTreeNodeById(new_child_id);
     if (client_child && client_child->parent != client_node) {
       DVLOG(1) << "Reparenting detected";
       Reset();
@@ -552,9 +565,7 @@
     ClientTreeNode* old_child = old_children[i];
     int old_child_id = old_child->id;
     if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
-      client_id_map_.erase(old_child_id);
       DeleteClientSubtree(old_child);
-      delete old_child;
     } else {
       client_child_id_map[old_child_id] = old_child;
     }
@@ -593,8 +604,10 @@
 
     new_child_ids.erase(child_id);
     actual_serialized_node_child_ids.push_back(child_id);
-    if (client_child_id_map.find(child_id) != client_child_id_map.end()) {
-      ClientTreeNode* reused_child = client_child_id_map[child_id];
+    ClientTreeNode* reused_child = nullptr;
+    if (client_child_id_map.find(child_id) != client_child_id_map.end())
+      reused_child = ClientTreeNodeById(child_id);
+    if (reused_child) {
       client_node->children.push_back(reused_child);
       const bool ignored_state_changed =
           reused_child->ignored !=
diff --git a/ui/base/cocoa/menu_controller_unittest.mm b/ui/base/cocoa/menu_controller_unittest.mm
index 87d84c40..c1769f2 100644
--- a/ui/base/cocoa/menu_controller_unittest.mm
+++ b/ui/base/cocoa/menu_controller_unittest.mm
@@ -5,10 +5,10 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/mac_util.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #import "testing/gtest_mac.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #import "ui/base/cocoa/menu_controller.h"
@@ -73,8 +73,7 @@
 
 const int kTestLabelResourceId = IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE;
 
-class MenuControllerTest : public CocoaTest {
-};
+class MenuControllerTest : public CocoaTest {};
 
 class TestSimpleMenuModelVisibility : public SimpleMenuModel {
  public:
@@ -239,11 +238,9 @@
 class FontListMenuModel : public SimpleMenuModel {
  public:
   FontListMenuModel(SimpleMenuModel::Delegate* delegate,
-                    const gfx::FontList* font_list, int index)
-      : SimpleMenuModel(delegate),
-        font_list_(font_list),
-        index_(index) {
-  }
+                    const gfx::FontList* font_list,
+                    int index)
+      : SimpleMenuModel(delegate), font_list_(font_list), index_(index) {}
   ~FontListMenuModel() override {}
   const gfx::FontList* GetLabelFontListAt(int index) const override {
     return (index == index_) ? font_list_ : NULL;
@@ -377,9 +374,9 @@
 // Tests hiding a submenu item. If a submenu item with children is set to
 // hidden, then the submenu should hide.
 TEST_F(MenuControllerTest, HiddenSubmenu) {
-  // SimpleMenuModel posts a task that calls Delegate::MenuClosed. Create
-  // a MessageLoop for that purpose.
-  base::MessageLoopForUI message_loop;
+  // SimpleMenuModel posts a task that calls Delegate::MenuClosed.
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   // Create the model.
   Delegate delegate;
@@ -424,9 +421,9 @@
 }
 
 TEST_F(MenuControllerTest, DisabledSubmenu) {
-  // SimpleMenuModel posts a task that calls Delegate::MenuClosed. Create
-  // a MessageLoop for that purpose.
-  base::MessageLoopForUI message_loop;
+  // SimpleMenuModel posts a task that calls Delegate::MenuClosed.
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   // Create the model.
   Delegate delegate;
@@ -611,9 +608,9 @@
 }
 
 TEST_F(MenuControllerTest, OpenClose) {
-  // SimpleMenuModel posts a task that calls Delegate::MenuClosed. Create
-  // a MessageLoop for that purpose.
-  base::MessageLoopForUI message_loop;
+  // SimpleMenuModel posts a task that calls Delegate::MenuClosed.
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   // Create the model.
   Delegate delegate;
@@ -633,7 +630,7 @@
   // In the event tracking run loop mode of the menu, verify that the controller
   // resports the menu as open.
   CFRunLoopPerformBlock(CFRunLoopGetCurrent(), NSEventTrackingRunLoopMode, ^{
-      EXPECT_TRUE([menu isMenuOpen]);
+    EXPECT_TRUE([menu isMenuOpen]);
   });
 
   // Pop open the menu, which will spin an event-tracking run loop.
@@ -682,7 +679,8 @@
   if (![NSMenuItem instancesRespondToSelector:@selector(_sendItemSelectedNote)])
     return;
 
-  base::MessageLoopForUI message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   Delegate delegate;
   delegate.auto_close_ = false;
@@ -787,7 +785,8 @@
 // MenuControllerCocoa and destroys itself. Note this usually needs asan to
 // actually crash (before it was fixed).
 TEST_F(MenuControllerTest, OwningDelegate) {
-  base::MessageLoopForUI message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
   bool did_delete = false;
   BOOL did_dealloc = NO;
   OwningDelegate* delegate;
diff --git a/ui/base/ime/input_method_base_unittest.cc b/ui/base/ime/input_method_base_unittest.cc
index 209cefe..157da3b 100644
--- a/ui/base/ime/input_method_base_unittest.cc
+++ b/ui/base/ime/input_method_base_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/base/ime/input_method_observer.h"
@@ -110,20 +110,9 @@
 };
 
 class InputMethodBaseTest : public testing::Test {
- protected:
-  InputMethodBaseTest() {
-  }
-  ~InputMethodBaseTest() override {}
-
-  void SetUp() override {
-    message_loop_ = std::make_unique<base::MessageLoopForUI>();
-  }
-
-  void TearDown() override { message_loop_.reset(); }
-
  private:
-  std::unique_ptr<base::MessageLoop> message_loop_;
-  DISALLOW_COPY_AND_ASSIGN(InputMethodBaseTest);
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
 };
 
 class MockInputMethodBase : public InputMethodBase {
diff --git a/ui/base/models/simple_combobox_model.cc b/ui/base/models/simple_combobox_model.cc
index edf339d..ff4f04f 100644
--- a/ui/base/models/simple_combobox_model.cc
+++ b/ui/base/models/simple_combobox_model.cc
@@ -4,12 +4,12 @@
 
 #include "ui/base/models/simple_combobox_model.h"
 
+#include <utility>
+
 namespace ui {
 
-SimpleComboboxModel::SimpleComboboxModel(
-    const std::vector<base::string16>& items)
-    : items_(items) {
-}
+SimpleComboboxModel::SimpleComboboxModel(std::vector<base::string16> items)
+    : items_(std::move(items)) {}
 
 SimpleComboboxModel::~SimpleComboboxModel() {
 }
diff --git a/ui/base/models/simple_combobox_model.h b/ui/base/models/simple_combobox_model.h
index 06e534a..a50373a 100644
--- a/ui/base/models/simple_combobox_model.h
+++ b/ui/base/models/simple_combobox_model.h
@@ -16,7 +16,7 @@
 // An empty string will be a separator.
 class UI_BASE_EXPORT SimpleComboboxModel : public ComboboxModel {
  public:
-  explicit SimpleComboboxModel(const std::vector<base::string16>& items);
+  explicit SimpleComboboxModel(std::vector<base::string16> items);
   ~SimpleComboboxModel() override;
 
   // ui::ComboboxModel:
diff --git a/ui/base/test/scoped_fake_nswindow_fullscreen_unittest.mm b/ui/base/test/scoped_fake_nswindow_fullscreen_unittest.mm
index 22ff976..c848882 100644
--- a/ui/base/test/scoped_fake_nswindow_fullscreen_unittest.mm
+++ b/ui/base/test/scoped_fake_nswindow_fullscreen_unittest.mm
@@ -7,9 +7,9 @@
 #import "base/mac/mac_util.h"
 #import "base/mac/scoped_nsobject.h"
 #import "base/mac/sdk_forward_declarations.h"
-#include "base/message_loop/message_loop.h"
-#import "testing/gtest_mac.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
 #import "ui/base/test/windowed_nsnotification_observer.h"
 #import "ui/gfx/mac/nswindow_frame_controls.h"
 
@@ -41,7 +41,8 @@
 
 // Test the order of notifications sent when faking fullscreen transitions.
 TEST(ScopedFakeNSWindowFullscreenTest, TestOrdering) {
-  base::MessageLoopForUI message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
 
   NSUInteger style_mask = NSTexturedBackgroundWindowMask | NSTitledWindowMask |
                           NSClosableWindowMask | NSMiniaturizableWindowMask |
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index b0a62f8a..115be2e 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -12,6 +12,8 @@
   output_name = "ui_base_x"
 
   sources = [
+    "x11_display_manager.cc",
+    "x11_display_manager.h",
     "x11_display_util.cc",
     "x11_display_util.h",
     "x11_menu_list.cc",
diff --git a/ui/base/x/selection_requestor_unittest.cc b/ui/base/x/selection_requestor_unittest.cc
index 74b55ed..2954b66 100644
--- a/ui/base/x/selection_requestor_unittest.cc
+++ b/ui/base/x/selection_requestor_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted_memory.h"
-#include "base/message_loop/message_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/x/selection_utils.h"
@@ -89,7 +89,8 @@
   std::unique_ptr<PlatformEventSource> event_source_;
   std::unique_ptr<SelectionRequestor> requestor_;
 
-  base::MessageLoopForUI message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SelectionRequestorTest);
diff --git a/ui/base/x/x11_display_manager.cc b/ui/base/x/x11_display_manager.cc
new file mode 100644
index 0000000..bb99a04
--- /dev/null
+++ b/ui/base/x/x11_display_manager.cc
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/x/x11_display_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/base/x/x11_display_util.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+
+namespace ui {
+
+namespace {
+
+constexpr int kMinXrandrVersion = 103;  // Need at least xrandr version 1.3
+constexpr auto kDisplayListUpdateDelay = base::TimeDelta::FromMilliseconds(250);
+
+}  // namespace
+
+XDisplayManager::XDisplayManager(Delegate* delegate)
+    : delegate_(delegate),
+      xdisplay_(gfx::GetXDisplay()),
+      x_root_window_(DefaultRootWindow(xdisplay_)),
+      xrandr_version_(GetXrandrVersion(xdisplay_)) {}
+
+XDisplayManager::~XDisplayManager() = default;
+
+void XDisplayManager::Init() {
+  if (IsXrandrAvailable()) {
+    int error_base_ignored = 0;
+    XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
+
+    XRRSelectInput(xdisplay_, x_root_window_,
+                   RRScreenChangeNotifyMask | RROutputChangeNotifyMask |
+                       RRCrtcChangeNotifyMask);
+  }
+  FetchDisplayList();
+}
+
+// Need at least xrandr version 1.3
+bool XDisplayManager::IsXrandrAvailable() const {
+  return xrandr_version_ >= kMinXrandrVersion;
+}
+
+display::Display XDisplayManager::GetPrimaryDisplay() const {
+  DCHECK(!displays_.empty());
+  return displays_[primary_display_index_];
+}
+
+void XDisplayManager::AddObserver(display::DisplayObserver* observer) {
+  change_notifier_.AddObserver(observer);
+}
+
+void XDisplayManager::RemoveObserver(display::DisplayObserver* observer) {
+  change_notifier_.RemoveObserver(observer);
+}
+
+bool XDisplayManager::CanProcessEvent(const XEvent& xev) {
+  return xev.type - xrandr_event_base_ == RRScreenChangeNotify ||
+         xev.type - xrandr_event_base_ == RRNotify ||
+         (xev.type == PropertyNotify &&
+          xev.xproperty.window == x_root_window_ &&
+          xev.xproperty.atom == gfx::GetAtom("_NET_WORKAREA"));
+}
+
+bool XDisplayManager::ProcessEvent(XEvent* xev) {
+  DCHECK(xev);
+  int ev_type = xev->type - xrandr_event_base_;
+  if (ev_type == RRScreenChangeNotify) {
+    // Pass the event through to xlib.
+    XRRUpdateConfiguration(xev);
+    return true;
+  }
+  if (ev_type == RRNotify ||
+      (xev->type == PropertyNotify &&
+       xev->xproperty.atom == gfx::GetAtom("_NET_WORKAREA"))) {
+    DispatchDelayedDisplayListUpdate();
+    return true;
+  }
+  return false;
+}
+
+void XDisplayManager::SetDisplayList(std::vector<display::Display> displays) {
+  displays_ = std::move(displays);
+  delegate_->OnXDisplayListUpdated();
+}
+
+// Talks to xrandr to get the information of the outputs for a screen and
+// updates display::Display list. The minimum required version of xrandr is
+// 1.3.
+void XDisplayManager::FetchDisplayList() {
+  std::vector<display::Display> displays;
+  float scale = delegate_->GetXDisplayScaleFactor();
+  if (IsXrandrAvailable()) {
+    displays = BuildDisplaysFromXRandRInfo(xrandr_version_, scale,
+                                           &primary_display_index_);
+  } else {
+    displays = GetFallbackDisplayList(scale);
+  }
+  SetDisplayList(std::move(displays));
+}
+
+void XDisplayManager::UpdateDisplayList() {
+  std::vector<display::Display> old_displays = displays_;
+  FetchDisplayList();
+  change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
+}
+
+void XDisplayManager::DispatchDelayedDisplayListUpdate() {
+  delayed_update_task_.Reset(base::BindOnce(&XDisplayManager::UpdateDisplayList,
+                                            base::Unretained(this)));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, delayed_update_task_.callback(), kDisplayListUpdateDelay);
+}
+
+gfx::Point XDisplayManager::GetCursorLocation() const {
+  XID root, child;
+  int root_x, root_y, win_x, win_y;
+  unsigned int mask;
+  XQueryPointer(xdisplay_, x_root_window_, &root, &child, &root_x, &root_y,
+                &win_x, &win_y, &mask);
+
+  return gfx::Point(root_x, root_y);
+}
+
+}  // namespace ui
diff --git a/ui/base/x/x11_display_manager.h b/ui/base/x/x11_display_manager.h
new file mode 100644
index 0000000..40d54fa
--- /dev/null
+++ b/ui/base/x/x11_display_manager.h
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_X_X11_DISPLAY_MANAGER_H_
+#define UI_BASE_X_X11_DISPLAY_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/cancelable_callback.h"
+#include "base/component_export.h"
+#include "ui/display/display.h"
+#include "ui/display/display_change_notifier.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace views {
+class DesktopScreenX11Test;
+}
+
+namespace ui {
+class X11ScreenOzoneTest;
+
+////////////////////////////////////////////////////////////////////////////////
+// XDisplayManager class
+//
+// Responsible for fetching and maintaining list of |display::Display|s
+// representing X11 screens connected to the system. XRandR extension is used
+// when version >= 1.3 is available, otherwise it falls back to
+// |DefaultScreenOfDisplay| Xlib API.
+//
+// Scale Factor information and simple hooks are delegated to API clients
+// through |XDisplayManager::Delegate| interface. To get notifications about
+// dynamic display changes, clients must register |DisplayObserver| instances
+// and feed |XDisplayManager| with |XEvent|s.
+//
+// All bounds and size values are assumed to be expressed in pixels.
+class COMPONENT_EXPORT(UI_BASE_X) XDisplayManager {
+ public:
+  class Delegate;
+
+  explicit XDisplayManager(Delegate* delegate);
+  virtual ~XDisplayManager();
+
+  void Init();
+  bool IsXrandrAvailable() const;
+  bool CanProcessEvent(const XEvent& xev);
+  bool ProcessEvent(XEvent* xev);
+  void UpdateDisplayList();
+  void DispatchDelayedDisplayListUpdate();
+  display::Display GetPrimaryDisplay() const;
+
+  void AddObserver(display::DisplayObserver* observer);
+  void RemoveObserver(display::DisplayObserver* observer);
+
+  const std::vector<display::Display>& displays() const { return displays_; }
+  gfx::Point GetCursorLocation() const;
+
+ private:
+  friend class ui::X11ScreenOzoneTest;
+  friend class views::DesktopScreenX11Test;
+
+  void SetDisplayList(std::vector<display::Display> displays);
+  void FetchDisplayList();
+
+  Delegate* const delegate_;
+  std::vector<display::Display> displays_;
+  display::DisplayChangeNotifier change_notifier_;
+
+  XDisplay* const xdisplay_;
+  XID x_root_window_;
+  int64_t primary_display_index_ = 0;
+
+  // XRandR version. MAJOR * 100 + MINOR. Zero if no xrandr is present.
+  const int xrandr_version_;
+
+  // The base of the event numbers used to represent XRandr events used in
+  // decoding events regarding output add/remove.
+  int xrandr_event_base_ = 0;
+
+  // The task to delay fetching display info. We delay it so that we can
+  // coalesce events.
+  base::CancelableOnceClosure delayed_update_task_;
+
+  DISALLOW_COPY_AND_ASSIGN(XDisplayManager);
+};
+
+class COMPONENT_EXPORT(UI_BASE_X) XDisplayManager::Delegate {
+ public:
+  virtual ~Delegate() = default;
+  virtual void OnXDisplayListUpdated() = 0;
+  virtual float GetXDisplayScaleFactor() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_X_X11_DISPLAY_MANAGER_H_
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index afdde4f..73018b9 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -504,6 +504,10 @@
       "blink/fling_booster_unittest.cc",
       "blink/input_handler_proxy_unittest.cc",
       "blink/input_scroll_elasticity_controller_unittest.cc",
+      "blink/prediction/empty_filter_unittests.cc",
+      "blink/prediction/filter_factory_unittests.cc",
+      "blink/prediction/input_filter_unittest_helpers.cc",
+      "blink/prediction/input_filter_unittest_helpers.h",
       "blink/prediction/input_predictor_unittest_helpers.cc",
       "blink/prediction/input_predictor_unittest_helpers.h",
       "blink/prediction/kalman_predictor_unittest.cc",
diff --git a/ui/events/blink/BUILD.gn b/ui/events/blink/BUILD.gn
index 1613613..e4b823f 100644
--- a/ui/events/blink/BUILD.gn
+++ b/ui/events/blink/BUILD.gn
@@ -37,8 +37,13 @@
     "input_scroll_elasticity_controller.h",
     "momentum_scroll_jank_tracker.cc",
     "momentum_scroll_jank_tracker.h",
+    "prediction/empty_filter.cc",
+    "prediction/empty_filter.h",
     "prediction/empty_predictor.cc",
     "prediction/empty_predictor.h",
+    "prediction/filter_factory.cc",
+    "prediction/filter_factory.h",
+    "prediction/input_filter.h",
     "prediction/input_predictor.h",
     "prediction/kalman_filter.cc",
     "prediction/kalman_filter.h",
diff --git a/ui/events/blink/blink_features.cc b/ui/events/blink/blink_features.cc
index 545195b..54965ec 100644
--- a/ui/events/blink/blink_features.cc
+++ b/ui/events/blink/blink_features.cc
@@ -9,6 +9,9 @@
 const base::Feature kResamplingScrollEvents{"ResamplingScrollEvents",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kFilteringScrollPrediction{
+    "FilteringScrollPrediction", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kSendMouseLeaveEvents{"SendMouseLeaveEvents",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/ui/events/blink/blink_features.h b/ui/events/blink/blink_features.h
index d51e49d..b05f1559 100644
--- a/ui/events/blink/blink_features.h
+++ b/ui/events/blink/blink_features.h
@@ -14,6 +14,10 @@
 COMPONENT_EXPORT(BLINK_FEATURES)
 extern const base::Feature kResamplingScrollEvents;
 
+// Enables filtering of predicted scroll events on compositor thread.
+COMPONENT_EXPORT(BLINK_FEATURES)
+extern const base::Feature kFilteringScrollPrediction;
+
 // This feature allows native ET_MOUSE_EXIT events to be passed
 // through to blink as mouse leave events. Traditionally these events were
 // converted to mouse move events due to a number of inconsistencies on
diff --git a/ui/events/blink/prediction/empty_filter.cc b/ui/events/blink/prediction/empty_filter.cc
new file mode 100644
index 0000000..dd3a3ff1
--- /dev/null
+++ b/ui/events/blink/prediction/empty_filter.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 "ui/events/blink/prediction/empty_filter.h"
+#include "ui/events/blink/prediction/filter_factory.h"
+
+namespace ui {
+
+EmptyFilter::EmptyFilter() {}
+EmptyFilter::~EmptyFilter() {}
+
+bool EmptyFilter::Filter(const base::TimeTicks timestamp,
+                         gfx::PointF* position) const {
+  return true;
+}
+
+const char* EmptyFilter::GetName() const {
+  return input_prediction::kFilterNameEmpty;
+}
+
+InputFilter* EmptyFilter::Clone() {
+  return new EmptyFilter();
+}
+
+void EmptyFilter::Reset() {}
+
+}  // namespace ui
diff --git a/ui/events/blink/prediction/empty_filter.h b/ui/events/blink/prediction/empty_filter.h
new file mode 100644
index 0000000..4b8bdd5
--- /dev/null
+++ b/ui/events/blink/prediction/empty_filter.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_BLINK_PREDICTION_EMPTY_FILTER_H_
+#define UI_EVENTS_BLINK_PREDICTION_EMPTY_FILTER_H_
+
+#include "ui/events/blink/prediction/input_filter.h"
+
+namespace ui {
+
+// Empty filter is a fake filter. Always returns the same input position as
+// the filtered position. Mainly used for testing purpose.
+class EmptyFilter : public InputFilter {
+ public:
+  explicit EmptyFilter();
+  ~EmptyFilter() override;
+
+  // Filters the position sent to the filter at a specific timestamp.
+  // Returns true if the value is filtered, false otherwise.
+  bool Filter(const base::TimeTicks timestamp,
+              gfx::PointF* position) const override;
+
+  // Returns the name of the filter
+  const char* GetName() const override;
+
+  // Returns a copy of the filter.
+  InputFilter* Clone() override;
+
+  // Reset the filter to its initial state
+  void Reset() override;
+
+  DISALLOW_COPY_AND_ASSIGN(EmptyFilter);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_BLINK_PREDICTION_EMPTY_FILTER_H_
diff --git a/ui/events/blink/prediction/empty_filter_unittests.cc b/ui/events/blink/prediction/empty_filter_unittests.cc
new file mode 100644
index 0000000..a8b3c36
--- /dev/null
+++ b/ui/events/blink/prediction/empty_filter_unittests.cc
@@ -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.
+
+#include "base/rand_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/blink/prediction/empty_filter.h"
+#include "ui/events/blink/prediction/filter_factory.h"
+#include "ui/events/blink/prediction/input_filter_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+class EmptyFilterTest : public InputFilterTest {
+ public:
+  explicit EmptyFilterTest() {}
+
+  void SetUp() override { filter_ = std::make_unique<ui::EmptyFilter>(); }
+
+  DISALLOW_COPY_AND_ASSIGN(EmptyFilterTest);
+};
+
+// Test the Clone function of the filter
+TEST_F(EmptyFilterTest, TestClone) {
+  TestCloneFilter();
+}
+
+// Test the Reset function of the filter
+TEST_F(EmptyFilterTest, TestReset) {
+  TestResetFilter();
+}
+
+// Test the empty filter gives the same values
+TEST_F(EmptyFilterTest, filteringValues) {
+  base::TimeTicks ts = blink::WebInputEvent::GetStaticTimeStampForTests();
+  gfx::PointF point, filtered_point;
+  for (int i = 0; i < 100; i++) {
+    point.SetPoint(base::RandDouble(), base::RandDouble());
+    filtered_point = point;
+    EXPECT_TRUE(filter_->Filter(ts, &filtered_point));
+    EXPECT_EQ(point.x(), filtered_point.x());
+    EXPECT_EQ(point.y(), filtered_point.y());
+  }
+}
+
+}  // namespace test
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/blink/prediction/filter_factory.cc b/ui/events/blink/prediction/filter_factory.cc
new file mode 100644
index 0000000..65de8e9c
--- /dev/null
+++ b/ui/events/blink/prediction/filter_factory.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 "ui/events/blink/prediction/filter_factory.h"
+#include "ui/events/blink/prediction/empty_filter.h"
+
+namespace ui {
+
+namespace input_prediction {
+
+const char kFilterNameEmpty[] = "empty_filter";
+
+}  // namespace input_prediction
+
+namespace {
+using input_prediction::FilterType;
+}  // namespace
+
+FilterType FilterFactory::GetFilterTypeFromName(
+    const std::string& filter_name) {
+  return FilterType::kEmpty;
+}
+
+std::unique_ptr<InputFilter> FilterFactory::CreateFilter(
+    input_prediction::FilterType filter_type) {
+  return std::make_unique<EmptyFilter>();
+}
+
+}  // namespace ui
diff --git a/ui/events/blink/prediction/filter_factory.h b/ui/events/blink/prediction/filter_factory.h
new file mode 100644
index 0000000..12da7406
--- /dev/null
+++ b/ui/events/blink/prediction/filter_factory.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 UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_
+#define UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_
+
+#include "ui/events/blink/prediction/input_filter.h"
+
+namespace ui {
+
+namespace input_prediction {
+
+extern const char kFilterNameEmpty[];
+
+enum class FilterType {
+  kEmpty,
+};
+}  // namespace input_prediction
+
+// FilterFactory is a class containing static public methods to create filters.
+// It defines filters name and type constants. It also reads filter settings
+// from fieldtrials if needed.
+class FilterFactory {
+ public:
+  // Returns the FilterType associated to the given filter
+  // name if found, otherwise returns kFilterTypeEmpty
+  static input_prediction::FilterType GetFilterTypeFromName(
+      const std::string& filter_name);
+
+  // Returns the filter designed by its type.
+  static std::unique_ptr<InputFilter> CreateFilter(
+      input_prediction::FilterType filter_type);
+
+ private:
+  FilterFactory() = delete;
+  ~FilterFactory() = delete;
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_
\ No newline at end of file
diff --git a/ui/events/blink/prediction/filter_factory_unittests.cc b/ui/events/blink/prediction/filter_factory_unittests.cc
new file mode 100644
index 0000000..4cc7906
--- /dev/null
+++ b/ui/events/blink/prediction/filter_factory_unittests.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/blink/prediction/filter_factory.h"
+
+namespace ui {
+namespace test {
+
+class FilterFactoryTest : public testing::Test {
+ public:
+  explicit FilterFactoryTest() {}
+
+  DISALLOW_COPY_AND_ASSIGN(FilterFactoryTest);
+};
+
+// Check if the FilterType returned is correct
+TEST_F(FilterFactoryTest, TestGetFilterType) {
+  EXPECT_EQ(
+      input_prediction::FilterType::kEmpty,
+      FilterFactory::GetFilterTypeFromName(input_prediction::kFilterNameEmpty));
+  // Default type Empty
+  EXPECT_EQ(input_prediction::FilterType::kEmpty,
+            FilterFactory::GetFilterTypeFromName(""));
+}
+
+TEST_F(FilterFactoryTest, TestGetFilter) {
+  EXPECT_STREQ(input_prediction::kFilterNameEmpty,
+               FilterFactory::CreateFilter(input_prediction::FilterType::kEmpty)
+                   ->GetName());
+}
+
+}  // namespace test
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/blink/prediction/input_filter.h b/ui/events/blink/prediction/input_filter.h
new file mode 100644
index 0000000..68aab232
--- /dev/null
+++ b/ui/events/blink/prediction/input_filter.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 UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_H_
+#define UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_H_
+
+#include "ui/events/base_event_utils.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace ui {
+
+// This class expects a sequence of inputs with coordinates and timestamps to
+// return a smooth path from the sent coordinates.
+class InputFilter {
+ public:
+  virtual ~InputFilter() = default;
+
+  // Filters the position sent to the filter at a specific timestamp.
+  // Returns true if the value is filtered, false otherwise.
+  virtual bool Filter(const base::TimeTicks timestamp,
+                      gfx::PointF* position) const = 0;
+
+  // Returns the name of the filter
+  virtual const char* GetName() const = 0;
+
+  // Returns a copy of the filter.
+  virtual InputFilter* Clone() = 0;
+
+  // Reset the filter to its initial state
+  virtual void Reset() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_H_
\ No newline at end of file
diff --git a/ui/events/blink/prediction/input_filter_unittest_helpers.cc b/ui/events/blink/prediction/input_filter_unittest_helpers.cc
new file mode 100644
index 0000000..0cd6be4
--- /dev/null
+++ b/ui/events/blink/prediction/input_filter_unittest_helpers.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/blink/prediction/input_filter_unittest_helpers.h"
+
+#include "base/rand_util.h"
+
+namespace ui {
+namespace test {
+
+InputFilterTest::InputFilterTest() = default;
+InputFilterTest::~InputFilterTest() = default;
+
+// Check if the filter is well cloned. We send random values to the filter and
+// then we clone it. If we send the same new random values to both filters,
+// we should have the same filtered results
+void InputFilterTest::TestCloneFilter() {
+  gfx::PointF point;
+  base::TimeTicks ts = blink::WebInputEvent::GetStaticTimeStampForTests();
+  base::TimeDelta delta = base::TimeDelta::FromMilliseconds(8);
+
+  for (int i = 0; i < 100; i++) {
+    point.SetPoint(base::RandDouble(), base::RandDouble());
+    EXPECT_TRUE(filter_->Filter(ts, &point));  // We just feed the filter
+    ts += delta;
+  }
+
+  std::unique_ptr<InputFilter> fork_filter;
+  fork_filter.reset(filter_->Clone());
+
+  gfx::PointF filtered_point, fork_filtered_point;
+  for (int i = 0; i < 100; i++) {
+    point.SetPoint(base::RandDouble(), base::RandDouble());
+    filtered_point = point;
+    fork_filtered_point = point;
+    EXPECT_TRUE(filter_->Filter(ts, &filtered_point));
+    EXPECT_TRUE(fork_filter->Filter(ts, &fork_filtered_point));
+    EXPECT_NEAR(filtered_point.x(), fork_filtered_point.x(), kEpsilon);
+    EXPECT_NEAR(filtered_point.y(), fork_filtered_point.y(), kEpsilon);
+    ts += delta;
+  }
+}
+
+// Check if the filter is well reset. We send random values, save the values and
+// results, then we reset the filter. We send again the same values and see if
+// we have the same results, which would be statistically impossible with 100
+// random without a proper resetting.
+void InputFilterTest::TestResetFilter() {
+  std::vector<gfx::PointF> points;
+  std::vector<base::TimeTicks> timestamps;
+  std::vector<gfx::PointF> results;
+  gfx::PointF point;
+  base::TimeTicks ts = blink::WebInputEvent::GetStaticTimeStampForTests();
+  base::TimeDelta delta = base::TimeDelta::FromMilliseconds(8);
+
+  for (int i = 0; i < 100; i++) {
+    point.SetPoint(base::RandDouble(), base::RandDouble());
+    points.push_back(point);
+    timestamps.push_back(ts);
+    EXPECT_TRUE(filter_->Filter(ts, &point));
+    results.push_back(point);
+    ts += delta;
+  }
+
+  filter_->Reset();
+
+  EXPECT_EQ((int)points.size(), 100);
+  EXPECT_EQ((int)timestamps.size(), 100);
+  EXPECT_EQ((int)results.size(), 100);
+
+  for (int i = 0; i < 100; i++) {
+    point = points[i];
+    EXPECT_TRUE(filter_->Filter(timestamps[i], &point));
+    EXPECT_NEAR(results[i].x(), point.x(), kEpsilon);
+    EXPECT_NEAR(results[i].y(), point.y(), kEpsilon);
+  }
+}
+
+}  // namespace test
+}  // namespace ui
diff --git a/ui/events/blink/prediction/input_filter_unittest_helpers.h b/ui/events/blink/prediction/input_filter_unittest_helpers.h
new file mode 100644
index 0000000..fef5b10
--- /dev/null
+++ b/ui/events/blink/prediction/input_filter_unittest_helpers.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 UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
+#define UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
+
+#include "ui/events/blink/prediction/input_filter.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/blink/blink_event_util.h"
+
+namespace ui {
+namespace test {
+
+constexpr double kEpsilon = 0.0001;
+
+// Base class for predictor unit tests
+class InputFilterTest : public testing::Test {
+ public:
+  InputFilterTest();
+  ~InputFilterTest() override;
+
+  void TestCloneFilter();
+
+  void TestResetFilter();
+
+ protected:
+  std::unique_ptr<InputFilter> filter_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputFilterTest);
+};
+
+}  // namespace test
+}  // namespace ui
+
+#endif  // UI_EVENTS_BLINK_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc
index 3234f0d..e973aac 100644
--- a/ui/events/blink/scroll_predictor.cc
+++ b/ui/events/blink/scroll_predictor.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/trace_event/trace_event.h"
+#include "ui/events/blink/prediction/filter_factory.h"
 #include "ui/events/blink/prediction/predictor_factory.h"
 
 using blink::WebInputEvent;
@@ -16,12 +17,26 @@
 namespace ui {
 
 ScrollPredictor::ScrollPredictor() {
+  // Get the predictor from feature flags
   std::string predictor_name = GetFieldTrialParamValueByFeature(
       features::kResamplingScrollEvents, "predictor");
 
   input_prediction::PredictorType predictor_type =
       ui::PredictorFactory::GetPredictorTypeFromName(predictor_name);
   predictor_ = ui::PredictorFactory::GetPredictor(predictor_type);
+
+  filtering_enabled_ =
+      base::FeatureList::IsEnabled(features::kFilteringScrollPrediction);
+
+  if (filtering_enabled_) {
+    // Get the filter from feature flags
+    std::string filter_name = GetFieldTrialParamValueByFeature(
+        features::kFilteringScrollPrediction, "filter");
+
+    input_prediction::FilterType filter_type =
+        ui::FilterFactory::GetFilterTypeFromName(filter_name);
+    filter_ = ui::FilterFactory::CreateFilter(filter_type);
+  }
 }
 
 ScrollPredictor::~ScrollPredictor() = default;
@@ -79,6 +94,8 @@
 
 void ScrollPredictor::Reset() {
   predictor_->Reset();
+  if (filtering_enabled_)
+    filter_->Reset();
   current_accumulated_delta_ = gfx::PointF();
   last_accumulated_delta_ = gfx::PointF();
 }
@@ -114,7 +131,7 @@
   InputPredictor::InputData result;
 
   base::TimeDelta prediction_delta = time_stamp - gesture_event->TimeStamp();
-
+  bool predicted = false;
   // Disable prediction when dt < 0.
   if (prediction_delta > base::TimeDelta()) {
     // For resampling, we don't want to predict too far away because the result
@@ -131,9 +148,17 @@
         predictor_->GeneratePrediction(prediction_time, &result)) {
       predicted_accumulated_delta = result.pos;
       gesture_event->SetTimeStamp(prediction_time);
+      predicted = true;
     }
   }
 
+  // Feed the filter with the first non-predicted events but only apply
+  // filtering on predicted events
+  gfx::PointF filtered_pos = predicted_accumulated_delta;
+  if (filtering_enabled_ && filter_->Filter(time_stamp, &filtered_pos) &&
+      predicted)
+    predicted_accumulated_delta = filtered_pos;
+
   // If the last resampled GSU over predict the delta, new GSU might try to
   // scroll back to make up the difference, which cause the scroll to jump back.
   // So we set the new delta to 0 when predicted delta is in different direction
diff --git a/ui/events/blink/scroll_predictor.h b/ui/events/blink/scroll_predictor.h
index ac85b9d..08d50cb5 100644
--- a/ui/events/blink/scroll_predictor.h
+++ b/ui/events/blink/scroll_predictor.h
@@ -9,6 +9,7 @@
 
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/event_with_callback.h"
+#include "ui/events/blink/prediction/input_filter.h"
 #include "ui/events/blink/prediction/input_predictor.h"
 
 namespace ui {
@@ -60,6 +61,10 @@
   void ComputeAccuracy(const WebScopedInputEvent& event);
 
   std::unique_ptr<InputPredictor> predictor_;
+  std::unique_ptr<InputFilter> filter_;
+
+  // Whether predicted scroll events should be filtered or not
+  bool filtering_enabled_ = false;
 
   // Total scroll delta, used for prediction. Reset when GestureScrollBegin
   gfx::PointF current_accumulated_delta_;
diff --git a/ui/events/blink/scroll_predictor_unittest.cc b/ui/events/blink/scroll_predictor_unittest.cc
index 679f7eb..568d57f96 100644
--- a/ui/events/blink/scroll_predictor_unittest.cc
+++ b/ui/events/blink/scroll_predictor_unittest.cc
@@ -10,7 +10,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/blink/blink_event_util.h"
 #include "ui/events/blink/blink_features.h"
+#include "ui/events/blink/prediction/empty_filter.h"
 #include "ui/events/blink/prediction/empty_predictor.h"
+#include "ui/events/blink/prediction/filter_factory.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"
@@ -107,8 +109,10 @@
     return scroll_predictor_->should_resample_scroll_events_;
   }
 
-  void ConfigureFieldTrial(const base::Feature& feature,
-                           const std::string& predictor_type) {
+  bool isFilteringEnabled() { return scroll_predictor_->filtering_enabled_; }
+
+  void ConfigurePredictorFieldTrial(const base::Feature& feature,
+                                    const std::string& predictor_type) {
     base::FieldTrialParams params;
     params["predictor"] = predictor_type;
 
@@ -122,6 +126,21 @@
     EXPECT_EQ(expected_type, scroll_predictor_->predictor_->GetName());
   }
 
+  void ConfigureFilterFieldTrial(const base::Feature& feature,
+                                 const std::string& filter_name) {
+    base::FieldTrialParams params;
+    params["filter"] = filter_name;
+
+    scoped_feature_list_.Reset();
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(feature, params);
+    EXPECT_EQ(params["filter"],
+              GetFieldTrialParamValueByFeature(feature, "filter"));
+  }
+
+  void VerifyFilterType(const char* expected_type) {
+    EXPECT_EQ(expected_type, scroll_predictor_->filter_->GetName());
+  }
+
  protected:
   EventWithCallback::OriginalEventList original_events_;
   std::unique_ptr<ScrollPredictor> scroll_predictor_;
@@ -378,26 +397,80 @@
 
   // When resampling is enabled, predictor type is set from
   // kResamplingScrollEvents.
-  ConfigureFieldTrial(features::kResamplingScrollEvents,
-                      input_prediction::kScrollPredictorNameEmpty);
+  ConfigurePredictorFieldTrial(features::kResamplingScrollEvents,
+                               input_prediction::kScrollPredictorNameEmpty);
   scroll_predictor_ = std::make_unique<ScrollPredictor>();
   VerifyPredictorType(input_prediction::kScrollPredictorNameEmpty);
 
-  ConfigureFieldTrial(features::kResamplingScrollEvents,
-                      input_prediction::kScrollPredictorNameLsq);
+  ConfigurePredictorFieldTrial(features::kResamplingScrollEvents,
+                               input_prediction::kScrollPredictorNameLsq);
   scroll_predictor_ = std::make_unique<ScrollPredictor>();
   VerifyPredictorType(input_prediction::kScrollPredictorNameLsq);
 
-  ConfigureFieldTrial(features::kResamplingScrollEvents,
-                      input_prediction::kScrollPredictorNameKalman);
+  ConfigurePredictorFieldTrial(features::kResamplingScrollEvents,
+                               input_prediction::kScrollPredictorNameKalman);
   scroll_predictor_ = std::make_unique<ScrollPredictor>();
   VerifyPredictorType(input_prediction::kScrollPredictorNameKalman);
 
-  ConfigureFieldTrial(features::kResamplingScrollEvents,
-                      input_prediction::kScrollPredictorNameLinearFirst);
+  ConfigurePredictorFieldTrial(
+      features::kResamplingScrollEvents,
+      input_prediction::kScrollPredictorNameLinearFirst);
   scroll_predictor_ = std::make_unique<ScrollPredictor>();
   VerifyPredictorType(input_prediction::kScrollPredictorNameLinearFirst);
 }
 
+// Check the right filter is selected
+TEST_F(ScrollPredictorTest, DefaultFilter) {
+  ConfigureFilterFieldTrial(features::kFilteringScrollPrediction, "");
+  scroll_predictor_ = std::make_unique<ScrollPredictor>();
+  VerifyFilterType(input_prediction::kFilterNameEmpty);
+  EXPECT_TRUE(isFilteringEnabled());
+
+  ConfigureFilterFieldTrial(features::kFilteringScrollPrediction,
+                            input_prediction::kFilterNameEmpty);
+  scroll_predictor_ = std::make_unique<ScrollPredictor>();
+  VerifyFilterType(input_prediction::kFilterNameEmpty);
+  EXPECT_TRUE(isFilteringEnabled());
+}
+
+// We first send 100 events to the scroll predictor with kalman predictor
+// enabled and filetring disable and save the results.
+// We then send the same events with kalman and the empty filter, we should
+// expect the same results.
+TEST_F(ScrollPredictorTest, FilteringPrediction) {
+  ConfigureFilterFieldTrial(features::kResamplingScrollEvents,
+                            input_prediction::kScrollPredictorNameKalman);
+  scroll_predictor_ = std::make_unique<ScrollPredictor>();
+
+  std::vector<double> accumulated_deltas;
+  WebScopedInputEvent gesture_update;
+
+  for (int i = 0; i < 100; i++) {
+    // Create event at time 8*i
+    gesture_update = CreateGestureScrollUpdate(0, 3 * i, 8 * i /* ms */);
+    // Handle the event 5 ms later
+    HandleResampleScrollEvents(gesture_update, 8 * i + 5 /* ms */);
+    EXPECT_FALSE(isFilteringEnabled());
+    accumulated_deltas.push_back(GetLastAccumulatedDelta().y());
+  }
+  EXPECT_EQ((int)accumulated_deltas.size(), 100);
+
+  // Now we enable filtering and compare the deltas
+  ConfigurePredictorFieldTrial(features::kResamplingScrollEvents,
+                               input_prediction::kScrollPredictorNameKalman);
+  ConfigureFilterFieldTrial(features::kFilteringScrollPrediction,
+                            input_prediction::kFilterNameEmpty);
+  scroll_predictor_ = std::make_unique<ScrollPredictor>();
+
+  for (int i = 0; i < 100; i++) {
+    // Create event at time 8*i
+    gesture_update = CreateGestureScrollUpdate(0, 3 * i, 8 * i /* ms */);
+    // Handle the event 5 ms later
+    HandleResampleScrollEvents(gesture_update, 8 * i + 5 /* ms */);
+    EXPECT_TRUE(isFilteringEnabled());
+    EXPECT_NEAR(accumulated_deltas[i], GetLastAccumulatedDelta().y(), 0.00001);
+  }
+}
+
 }  // namespace test
 }  // namespace ui
diff --git a/ui/events/platform/platform_event_source_unittest.cc b/ui/events/platform/platform_event_source_unittest.cc
index 8dba043b..3be3623 100644
--- a/ui/events/platform/platform_event_source_unittest.cc
+++ b/ui/events/platform/platform_event_source_unittest.cc
@@ -12,10 +12,10 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
@@ -498,7 +498,7 @@
   ~PlatformEventTestWithMessageLoop() override {}
 
   void Run() {
-    message_loop_.task_runner()->PostTask(
+    scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(&PlatformEventTestWithMessageLoop::RunTestImpl,
                        base::Unretained(this)));
@@ -509,7 +509,8 @@
   virtual void RunTestImpl() = 0;
 
  private:
-  base::MessageLoopForUI message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
 
   DISALLOW_COPY_AND_ASSIGN(PlatformEventTestWithMessageLoop);
 };
diff --git a/ui/file_manager/base/js/volume_manager_types.js b/ui/file_manager/base/js/volume_manager_types.js
index 2e74b938..6fc10e7 100644
--- a/ui/file_manager/base/js/volume_manager_types.js
+++ b/ui/file_manager/base/js/volume_manager_types.js
@@ -43,9 +43,9 @@
  * @const
  */
 VolumeManagerCommon.FileSystemTypeVolumeNameLengthLimit = {
-  [VolumeManagerCommon.FileSystemType.VFAT]: 11,
-  [VolumeManagerCommon.FileSystemType.EXFAT]: 15,
-  [VolumeManagerCommon.FileSystemType.NTFS]: 32,
+  'vfat': 11,
+  'exfat': 15,
+  'ntfs': 32,
 };
 
 /**
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 d973f885..f27ca5c 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
@@ -631,7 +631,15 @@
 CommandHandler.COMMANDS_['format'] = new class extends Command {
   execute(event, fileManager) {
     const directoryModel = fileManager.directoryModel;
-    let root = CommandUtil.getCommandEntry(fileManager, event.target);
+    let root;
+    if (event.target instanceof DirectoryItem ||
+        event.target instanceof DirectoryTree) {
+      // The command is executed from the directory tree context menu.
+      root = CommandUtil.getCommandEntry(fileManager, event.target);
+    } else {
+      // The command is executed from the gear menu.
+      root = directoryModel.getCurrentDirEntry();
+    }
     // If an entry is not found from the event target, use the current
     // directory. This can happen for the format button for unsupported and
     // unrecognized volumes.
@@ -658,7 +666,15 @@
   /** @override */
   canExecute(event, fileManager) {
     const directoryModel = fileManager.directoryModel;
-    let root = CommandUtil.getCommandEntry(fileManager, event.target);
+    let root;
+    if (event.target instanceof DirectoryItem ||
+        event.target instanceof DirectoryTree) {
+      // The command is executed from the directory tree context menu.
+      root = CommandUtil.getCommandEntry(fileManager, event.target);
+    } else {
+      // The command is executed from the gear menu.
+      root = directoryModel.getCurrentDirEntry();
+    }
     // |root| is null for unrecognized volumes. Enable format command for such
     // volumes.
     const isUnrecognizedVolume = (root == null);
diff --git a/ui/gfx/platform_font_skia.h b/ui/gfx/platform_font_skia.h
index 9822bf7..d38e6d6 100644
--- a/ui/gfx/platform_font_skia.h
+++ b/ui/gfx/platform_font_skia.h
@@ -57,8 +57,6 @@
   int GetFontSize() const override;
   const FontRenderParams& GetFontRenderParams() override;
 
-  sk_sp<SkTypeface> typeface() const { return typeface_; }
-
  private:
   // Create a new instance of this object with the specified properties. Called
   // from DeriveFont.
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc
index 90938df..415bc43 100644
--- a/ui/gl/direct_composition_surface_win.cc
+++ b/ui/gl/direct_composition_surface_win.cc
@@ -281,6 +281,16 @@
 }
 
 // static
+bool DirectCompositionSurfaceWin::IsDecodeSwapChainSupported() {
+  if (base::FeatureList::IsEnabled(
+          features::kDirectCompositionUseNV12DecodeSwapChain)) {
+    InitializeHardwareOverlaySupport();
+    return GetOverlayFormatUsed() == DXGI_FORMAT_NV12;
+  }
+  return false;
+}
+
+// static
 void DirectCompositionSurfaceWin::DisableOverlays() {
   g_supports_overlays = false;
 }
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h
index b43e1da..b504372 100644
--- a/ui/gl/direct_composition_surface_win.h
+++ b/ui/gl/direct_composition_surface_win.h
@@ -52,6 +52,9 @@
   // --enable-direct-composition-layers and --disable-direct-composition-layers.
   static bool AreOverlaysSupported();
 
+  // Returns true if zero copy decode swap chain is supported.
+  static bool IsDecodeSwapChainSupported();
+
   // After this is called, hardware overlay support is disabled during the
   // current GPU process' lifetime.
   static void DisableOverlays();
diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn
index 42637c3..5203655d 100644
--- a/ui/ozone/platform/x11/BUILD.gn
+++ b/ui/ozone/platform/x11/BUILD.gn
@@ -88,6 +88,7 @@
     ":x11",
     "//testing/gmock",
     "//testing/gtest",
+    "//ui/base/x",
     "//ui/events:test_support",
     "//ui/events/devices/x11",
     "//ui/events/platform/x11",
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.cc b/ui/ozone/platform/x11/x11_screen_ozone.cc
index b2188c9..5ad33f03 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.cc
+++ b/ui/ozone/platform/x11/x11_screen_ozone.cc
@@ -4,18 +4,12 @@
 
 #include "ui/ozone/platform/x11/x11_screen_ozone.h"
 
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "ui/base/x/x11_display_util.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/display/display_finder.h"
 #include "ui/display/util/display_util.h"
-#include "ui/display/util/x11/edid_parser_x11.h"
 #include "ui/events/platform/x11/x11_event_source.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/geometry/dip_util.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/ozone/platform/x11/x11_window_manager_ozone.h"
 #include "ui/ozone/platform/x11/x11_window_ozone.h"
 
@@ -23,10 +17,6 @@
 
 namespace {
 
-constexpr int kMinVersionXrandr = 103;  // Need at least xrandr version 1.3.
-
-constexpr auto kDisplayListUpdateDelay = base::TimeDelta::FromMilliseconds(250);
-
 float GetDeviceScaleFactor() {
   float device_scale_factor = 1.0f;
   // TODO(crbug.com/891175): Implement PlatformScreen for X11
@@ -111,44 +101,31 @@
 
 X11ScreenOzone::X11ScreenOzone(X11WindowManagerOzone* window_manager)
     : window_manager_(window_manager),
-      xdisplay_(gfx::GetXDisplay()),
-      x_root_window_(DefaultRootWindow(xdisplay_)),
-      xrandr_version_(GetXrandrVersion(xdisplay_)) {
+      x11_display_manager_(std::make_unique<XDisplayManager>(this)) {
   DCHECK(window_manager_);
 }
 
 X11ScreenOzone::~X11ScreenOzone() {
-  if (xrandr_version_ >= kMinVersionXrandr &&
+  if (x11_display_manager_->IsXrandrAvailable() &&
       X11EventSourceLibevent::GetInstance()) {
     X11EventSourceLibevent::GetInstance()->RemoveXEventDispatcher(this);
   }
 }
 
 void X11ScreenOzone::Init() {
-  // Need at least xrandr version 1.3.
-  if (xrandr_version_ >= kMinVersionXrandr) {
-    int error_base_ignored = 0;
-    XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
-
-    DCHECK(X11EventSourceLibevent::GetInstance());
+  if (x11_display_manager_->IsXrandrAvailable() &&
+      X11EventSourceLibevent::GetInstance()) {
     X11EventSourceLibevent::GetInstance()->AddXEventDispatcher(this);
-
-    XRRSelectInput(xdisplay_, x_root_window_,
-                   RRScreenChangeNotifyMask | RROutputChangeNotifyMask |
-                       RRCrtcChangeNotifyMask);
   }
-  FetchDisplayList();
+  x11_display_manager_->Init();
 }
 
 const std::vector<display::Display>& X11ScreenOzone::GetAllDisplays() const {
-  return displays_;
+  return x11_display_manager_->displays();
 }
 
 display::Display X11ScreenOzone::GetPrimaryDisplay() const {
-  auto iter = displays_.begin();
-  if (iter == displays_.end())
-    return display::Display::GetDefaultDisplay();
-  return *iter;
+  return x11_display_manager_->GetPrimaryDisplay();
 }
 
 display::Display X11ScreenOzone::GetDisplayForAcceleratedWidget(
@@ -190,82 +167,35 @@
     const gfx::Rect& match_rect) const {
   const display::Display* matching_display =
       display::FindDisplayWithBiggestIntersection(
-          displays_, gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect));
+          x11_display_manager_->displays(),
+          gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect));
   return matching_display ? *matching_display : GetPrimaryDisplay();
 }
 
 void X11ScreenOzone::AddObserver(display::DisplayObserver* observer) {
-  change_notifier_.AddObserver(observer);
+  x11_display_manager_->AddObserver(observer);
 }
 
 void X11ScreenOzone::RemoveObserver(display::DisplayObserver* observer) {
-  change_notifier_.RemoveObserver(observer);
+  x11_display_manager_->RemoveObserver(observer);
 }
 
-// TODO(nickdiego): Factor event dispatching and display fetching so that it
-// can be shared between ozone and non-ozone code paths.
 bool X11ScreenOzone::DispatchXEvent(XEvent* xev) {
-  DCHECK(xev);
-  int ev_type = xev->type - xrandr_event_base_;
-  if (ev_type == RRScreenChangeNotify) {
-    // Pass the event through to xlib.
-    XRRUpdateConfiguration(xev);
-    return true;
-  }
-  if (ev_type == RRNotify ||
-      (xev->type == PropertyNotify &&
-       xev->xproperty.atom == gfx::GetAtom("_NET_WORKAREA"))) {
-    RestartDelayedUpdateDisplayListTask();
-    return true;
-  }
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// X11ScreenOzone, private:
-
-void X11ScreenOzone::SetDisplayList(std::vector<display::Display> displays) {
-  displays_ = std::move(displays);
-  gfx::SetFontRenderParamsDeviceScaleFactor(
-      GetPrimaryDisplay().device_scale_factor());
-}
-
-// Talks to xrandr to get the information of the outputs for a screen and
-// updates display::Display list. The minimum required version of xrandr is
-// 1.3.
-void X11ScreenOzone::FetchDisplayList() {
-  std::vector<display::Display> displays;
-  float scale = GetDeviceScaleFactor();
-  if (xrandr_version_ >= kMinVersionXrandr) {
-    displays = BuildDisplaysFromXRandRInfo(xrandr_version_, scale,
-                                           &primary_display_index_);
-  } else {
-    displays = GetFallbackDisplayList(scale);
-  }
-  SetDisplayList(std::move(displays));
-}
-
-void X11ScreenOzone::UpdateDisplayList() {
-  std::vector<display::Display> old_displays = displays_;
-  FetchDisplayList();
-  change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
-}
-
-void X11ScreenOzone::RestartDelayedUpdateDisplayListTask() {
-  delayed_update_task_.Reset(base::BindOnce(&X11ScreenOzone::UpdateDisplayList,
-                                            base::Unretained(this)));
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, delayed_update_task_.callback(), kDisplayListUpdateDelay);
+  return x11_display_manager_->ProcessEvent(xev);
 }
 
 gfx::Point X11ScreenOzone::GetCursorLocation() const {
-  ::Window root, child;
-  int root_x, root_y, win_x, win_y;
-  unsigned int mask;
-  XQueryPointer(xdisplay_, x_root_window_, &root, &child, &root_x, &root_y,
-                &win_x, &win_y, &mask);
+  return x11_display_manager_->GetCursorLocation();
+}
 
-  return gfx::Point(root_x, root_y);
+void X11ScreenOzone::OnXDisplayListUpdated() {
+  float scale_factor =
+      x11_display_manager_->GetPrimaryDisplay().device_scale_factor();
+  gfx::SetFontRenderParamsDeviceScaleFactor(scale_factor);
+}
+
+float X11ScreenOzone::GetXDisplayScaleFactor() {
+  return GetDeviceScaleFactor();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.h b/ui/ozone/platform/x11/x11_screen_ozone.h
index 49eed35b..4afda89 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.h
+++ b/ui/ozone/platform/x11/x11_screen_ozone.h
@@ -6,16 +6,14 @@
 #define UI_OZONE_PLATFORM_X11_X11_SCREEN_OZONE_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
-#include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "ui/display/display.h"
-#include "ui/display/display_change_notifier.h"
+#include "ui/base/x/x11_display_manager.h"
 #include "ui/events/platform/x11/x11_event_source_libevent.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/x/x11_types.h"
 #include "ui/ozone/public/platform_screen.h"
 
 namespace ui {
@@ -23,7 +21,9 @@
 class X11WindowManagerOzone;
 
 // A PlatformScreen implementation for X11.
-class X11ScreenOzone : public PlatformScreen, public XEventDispatcher {
+class X11ScreenOzone : public PlatformScreen,
+                       public XEventDispatcher,
+                       public XDisplayManager::Delegate {
  public:
   explicit X11ScreenOzone(X11WindowManagerOzone* window_manager);
   ~X11ScreenOzone() override;
@@ -52,31 +52,14 @@
  private:
   friend class X11ScreenOzoneTest;
 
-  void SetDisplayList(std::vector<display::Display> displays);
-  void FetchDisplayList();
-  void UpdateDisplayList();
-  void RestartDelayedUpdateDisplayListTask();
+  // Overridden from ui::XDisplayManager::Delegate:
+  void OnXDisplayListUpdated() override;
+  float GetXDisplayScaleFactor() override;
+
   gfx::Point GetCursorLocation() const;
 
-  std::vector<display::Display> displays_;
-  display::DisplayChangeNotifier change_notifier_;
-
   X11WindowManagerOzone* const window_manager_;
-
-  XDisplay* const xdisplay_;
-  XID x_root_window_;
-  int64_t primary_display_index_ = 0;
-
-  // XRandR version. MAJOR * 100 + MINOR. Zero if no xrandr is present.
-  const int xrandr_version_;
-
-  // The base of the event numbers used to represent XRandr events used in
-  // decoding events regarding output add/remove.
-  int xrandr_event_base_ = 0;
-
-  // The task to delay fetching display info. We delay it so that we can
-  // coalesce events.
-  base::CancelableOnceClosure delayed_update_task_;
+  std::unique_ptr<ui::XDisplayManager> x11_display_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(X11ScreenOzone);
 };
diff --git a/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc b/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
index 5e51f7e..a5da019 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
+++ b/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/x/x11_display_manager.h"
 #include "ui/display/display.h"
 #include "ui/display/display_observer.h"
 #include "ui/events/platform/x11/x11_event_source_libevent.h"
@@ -60,7 +61,7 @@
     primary_display_ = std::make_unique<display::Display>(
         NextDisplayId(), kPrimaryDisplayBounds);
     screen_.reset(new X11ScreenOzone(window_manager_.get()));
-    screen_->SetDisplayList({*primary_display_});
+    UpdateDisplayListForTest({*primary_display_});
     screen_->AddObserver(&display_observer_);
   }
 
@@ -73,23 +74,26 @@
   }
 
   void AddDisplayForTest(const display::Display& display) {
-    std::vector<display::Display> new_displays(screen_->displays_);
+    auto display_list = screen_->GetAllDisplays();
+    std::vector<display::Display> new_displays(display_list);
     new_displays.push_back(display);
     UpdateDisplayListForTest(std::move(new_displays));
   }
 
   void RemoveDisplayForTest(const display::Display& display_to_remove) {
-    std::vector<display::Display> new_displays(screen_->displays_.size() - 1);
-    std::remove_copy(screen_->displays_.begin(), screen_->displays_.end(),
+    auto display_list = screen_->GetAllDisplays();
+    std::vector<display::Display> new_displays(display_list.size() - 1);
+    std::remove_copy(display_list.begin(), display_list.end(),
                      new_displays.begin(), display_to_remove);
     UpdateDisplayListForTest(std::move(new_displays));
   }
 
   void UpdateDisplayListForTest(std::vector<display::Display> displays) {
-    std::vector<display::Display> old_displays = std::move(screen_->displays_);
-    screen_->SetDisplayList(std::move(displays));
-    screen_->change_notifier_.NotifyDisplaysChanged(old_displays,
-                                                    screen_->displays_);
+    ui::XDisplayManager* manager = screen_->x11_display_manager_.get();
+    std::vector<display::Display> old_displays = std::move(manager->displays_);
+    manager->SetDisplayList(std::move(displays));
+    manager->change_notifier_.NotifyDisplaysChanged(old_displays,
+                                                    manager->displays_);
   }
 
   std::unique_ptr<X11WindowOzone> CreatePlatformWindow(
@@ -121,7 +125,7 @@
   // Initially only primary display is expected to be in place
   EXPECT_EQ(1u, screen()->GetAllDisplays().size());
   EXPECT_CALL(display_observer_, OnDisplayAdded(_)).Times(2);
-  EXPECT_CALL(display_observer_, OnDisplayRemoved(_)).Times(3);
+  EXPECT_CALL(display_observer_, OnDisplayRemoved(_)).Times(2);
 
   auto display_2 = CreateDisplay(gfx::Rect(800, 0, 1280, 720));
   AddDisplayForTest(*display_2);
@@ -135,8 +139,6 @@
   EXPECT_EQ(2u, screen()->GetAllDisplays().size());
   RemoveDisplayForTest(*display_2);
   EXPECT_EQ(1u, screen()->GetAllDisplays().size());
-  RemoveDisplayForTest(primary_display());
-  EXPECT_EQ(0u, screen()->GetAllDisplays().size());
 }
 
 // This test case exercises GetDisplayForAcceleratedWidget when simple cases
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index b3a09ac4..e69dd4c 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -977,7 +977,7 @@
         Call this number from your phone?
       </message>
       <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_FAILED_TO_SEND" desc="The label to be shown as the title of the dialog when user click on a phone number, if sending it to a device failed.">
-        Number failed to send
+        Can't send number
       </message>
       <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_HELP_TEXT_NO_DEVICES" desc="The label to be shown as a help text of the dialog when user click on a phone number, if there are no phones or apps to choose from.">
         To make a call from your phone, turn on sync.
@@ -985,11 +985,8 @@
       <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_SYNC_HELP_TEXT" desc="The label to be shown on the dialog when user click on a phone number for the link to a help page to turn on sync.">
         Learn how to turn on sync
       </message>
-      <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE" desc="The label to be shown on the dialog when user click on a phone number for the link to a help page to turn on sync.">
-        Looks like something has gone wrong. Try again or <ph name="TROUBLESHOOT_LINK">$1<ex>learn how to troubleshoot Click to Call</ex></ph>.
-      </message>
-      <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TROUBLESHOOT_LINK" desc="The label to be shown on the dialog when user click on a phone number for the link to a help page to turn on sync.">
-        learn how to troubleshoot Click to Call
+      <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE" desc="The label to be shown on the dialog when user click on a phone number, if sending it to a device failed.">
+        Check your phone's connection and try again
       </message>
       <message name="IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_SEND_SUCCESS" desc="The label to be shown next to the omnibox icon after a phone number got sent to a users device.">
         Sent to device
diff --git a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE.png.sha1 b/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE.png.sha1
index 6b80593..97e13fa 100644
--- a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE.png.sha1
+++ b/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_FAILED_MESSAGE.png.sha1
@@ -1 +1 @@
-0118433c23636f72e89ecb6792a680d6e13b702f
\ No newline at end of file
+5ea3791f2b6f05be1604b41f540cfa0a2d2555eb
\ No newline at end of file
diff --git a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_FAILED_TO_SEND.png.sha1 b/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_FAILED_TO_SEND.png.sha1
index 6b80593..97e13fa 100644
--- a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_FAILED_TO_SEND.png.sha1
+++ b/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_FAILED_TO_SEND.png.sha1
@@ -1 +1 @@
-0118433c23636f72e89ecb6792a680d6e13b702f
\ No newline at end of file
+5ea3791f2b6f05be1604b41f540cfa0a2d2555eb
\ No newline at end of file
diff --git a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TROUBLESHOOT_LINK.png.sha1 b/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TROUBLESHOOT_LINK.png.sha1
deleted file mode 100644
index 6b80593..0000000
--- a/ui/strings/ui_strings_grd/IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TROUBLESHOOT_LINK.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0118433c23636f72e89ecb6792a680d6e13b702f
\ No newline at end of file
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 812c440..905c9144 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -751,6 +751,7 @@
   DCHECK_LE(index, GetTabCount());
   contents->SetVisible(false);
   contents->GetViewAccessibility().OverrideName(title);
+  contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTab);
 
   tab_strip_->AddChildViewAt(
       std::make_unique<MdTab>(this, title, contents.get()),
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
index effb323..b92e074 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
@@ -34,6 +34,12 @@
   return ax_node_data.GetString16Attribute(ax::mojom::StringAttribute::kName);
 }
 
+ax::mojom::Role GetAccessibleRole(View* view) {
+  ui::AXNodeData ax_node_data;
+  view->GetViewAccessibility().GetAccessibleNodeData(&ax_node_data);
+  return ax_node_data.role;
+}
+
 }  // namespace
 
 class TabbedPaneTest : public ViewsTestBase {
@@ -294,5 +300,16 @@
   EXPECT_EQ(kSecondTitle, GetAccessibleName(tab2_contents));
 }
 
+TEST_F(TabbedPaneTest, AccessiblePaneContentsRoleIsTab) {
+  const base::string16 kFirstTitle = ASCIIToUTF16("Tab1");
+  const base::string16 kSecondTitle = ASCIIToUTF16("Tab2");
+  View* const tab1_contents =
+      tabbed_pane_->AddTab(kFirstTitle, std::make_unique<View>());
+  View* const tab2_contents =
+      tabbed_pane_->AddTab(kSecondTitle, std::make_unique<View>());
+  EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab1_contents));
+  EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab2_contents));
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/test/OWNERS b/ui/views/test/OWNERS
index 597e3ef..3b9f6f4 100644
--- a/ui/views/test/OWNERS
+++ b/ui/views/test/OWNERS
@@ -1 +1,2 @@
 per-file *_mac*=tapted@chromium.org
+per-file *x11*=thomasanderson@chromium.org
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
index 949f3e08..e2be730 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
@@ -7,6 +7,7 @@
 <link rel="import" href="chrome://resources/cr_components/chromeos/cellular_setup/provisioning_page.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/cellular_setup/final_page.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.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-pages/iron-pages.html">
 
 <dom-module id="cellular-setup">
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.html
index 1cde306..ec6ef31 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.html
@@ -2,13 +2,33 @@
 
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/cellular_setup/base_page.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_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/paper-spinner/paper-spinner-lite.html">
 
 <dom-module id="sim-detect-page">
   <template>
-    <base-page title="[[i18n('simDetectPageTitle')]]">
-      <div slot="page-body">
-        <!-- TODO(azeemarshad): Add page assets and strings -->
-        [[i18n('simDetectPageTitle')]]
+    <style include="iron-flex cr-hidden-style">
+      paper-spinner-lite {
+        height: 200px;
+        width: 200px;
+      }
+
+      #error-icon-container {
+        background-image: -webkit-image-set(
+            url(error_1x.png) 1x,
+            url(error_2x.png) 2x);
+        background-position: center center;
+        background-repeat: no-repeat;
+        height: 100%;
+        width: 100%;
+      }
+    </style>
+    <base-page title="[[getTitle_(showError)]]"
+        message="[[getMessage_(showError)]]">
+      <div slot="page-body" class="layout horizontal center-center">
+        <paper-spinner-lite active hidden$="[[showError]]"></paper-spinner-lite>
+        <div id="error-icon-container" hidden$="[[!showError]]"></div>
       </div>
     </base-page>
   </template>
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.js
index 8f6357dc..eb5581d 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.js
@@ -18,9 +18,25 @@
      * Whether error state should be shown.
      * @type {boolean}
      */
-    showError: {
-      type: Boolean,
-      value: false,
-    },
-  }
+    showError: Boolean,
+  },
+
+  /**
+   * @param {boolean} showError
+   * @return {string}
+   * @private
+   */
+  getTitle_: function(showError) {
+    return this.i18n(
+        showError ? 'simDetectPageErrorTitle' : 'simDetectPageTitle');
+  },
+
+  /**
+   * @param {boolean} showError
+   * @return {string}
+   * @private
+   */
+  getMessage_: function(showError) {
+    return showError ? this.i18n('simDetectPageErrorMessage') : '';
+  },
 });