diff --git a/DEPS b/DEPS
index 1cfa208..c0b8a6a 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,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': '6c2b2bb02402f48d06536db177c1e9612759d97b',
+  'skia_revision': '106e86516480c7519becf7da1c4e6e42040b62a3',
   # 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': 'f505d9d8ac44d693d227abd6eddf48e04712a557',
+  'v8_revision': 'ddd5171bc2abfa9eff5f957c2279268c324cef8c',
   # 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.
@@ -179,11 +179,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '6950a87f905453f8626aa6932463fb9be6528628',
+  'angle_revision': 'a21362f5a6ae8929899fb54fb646f395d141d98b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'ae022faf53b9f648324874063d7147ba7b555417',
+  'swiftshader_revision': '1c462ebc904cee7d85a0881a12fa1d42f3b2a9ff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -218,7 +218,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '545a481a74a3c3b70af8928793a01a84f8b0ee9b',
+  'freetype_revision': '90a30f154a612693641e5366ea8d1d27ea2a4a99',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -230,7 +230,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': '284b45288355d9b698867dff1667dd2267b30279',
+  'catapult_revision': '1550399cc0a14c198e43df4cb612c26edc7ec628',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-node-modules
   # and whatever else without interference from each other.
-  'devtools_node_modules_revision': '499e57bfea8a4f8e8373321f2c99632372553e56',
+  'devtools_node_modules_revision': '300c8063dda8f87348bca1643bc04c603a42453a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -286,7 +286,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '82f84c4b8f19a3a59647e851a5b3921d63b7b8e4',
+  'spv_tools_revision': 'eba98c4eb7818a310ee1af822f4fc5dcf483c2d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '88c57b498266573fa404657944b256e97ec259f3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'af9c6ce85491f6cb57b6919cb8fb878020fc1612',
       'condition': 'checkout_linux',
   },
 
@@ -887,7 +887,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8e57b4bc55c05081b8f6331da37f41b558920dfb',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e3703bb8b8af7978ecf1bdfbfe8c3e6c910be8bc',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1280,7 +1280,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '561ba7ee890b2ae08a5ec9bc3ef1301ba3a3ef16',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f5952b718415b26217c79b11afccfe850d281f96',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e6f9bd000949fe31b177cef8311792a1f8f5563d',
+    Var('webrtc_git') + '/src.git' + '@' + '5740f3e2b8fdee7288748907920d9fabe948e895',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@54b0677aeba54b28b88e49933cefbd9ea5f8612a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8d99c8fdb68b6223b7070e694821549ccaf9afa9',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 6f5b5b8..71eb728 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -380,7 +380,6 @@
   '^components/variations/',
   '^components/visitedlink/',
   '^components/web_cache/',
-  '^components/web_resource/',
   '^components/webcrypto/',
   '^components/webdata/',
   '^components/webdata_services/',
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index d20d20d8..74ff52b 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -6,7 +6,6 @@
 
 import android.Manifest;
 import android.content.Context;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Looper;
@@ -218,7 +217,7 @@
      * Set up resources on a background thread.
      * @param context The context.
      */
-    public void setUpResourcesOnBackgroundThread(PackageInfo webViewPackageInfo, Context context) {
+    public void setUpResourcesOnBackgroundThread(int packageId, Context context) {
         try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped(
                      "WebViewChromiumAwInit.setUpResourcesOnBackgroundThread")) {
             assert mSetUpResourcesThread == null : "This method shouldn't be called twice.";
@@ -228,7 +227,7 @@
                 @Override
                 public void run() {
                     // Run this in parallel as it takes some time.
-                    setUpResources(webViewPackageInfo, context);
+                    setUpResources(packageId, context);
                 }
             });
             mSetUpResourcesThread.start();
@@ -244,17 +243,10 @@
         }
     }
 
-    private void setUpResources(PackageInfo webViewPackageInfo, Context context) {
+    private void setUpResources(int packageId, Context context) {
         try (ScopedSysTraceEvent e =
                         ScopedSysTraceEvent.scoped("WebViewChromiumAwInit.setUpResources")) {
-            String packageName = webViewPackageInfo.packageName;
-            if (webViewPackageInfo.applicationInfo.metaData != null) {
-                packageName = webViewPackageInfo.applicationInfo.metaData.getString(
-                        "com.android.webview.WebViewDonorPackage", packageName);
-            }
-
-            R.onResourcesLoaded(mFactory.getWebViewDelegate().getPackageId(
-                    context.getResources(), packageName));
+            R.onResourcesLoaded(packageId);
 
             AwResource.setResources(context.getResources());
             AwResource.setConfigKeySystemUuidMapping(android.R.array.config_keySystemUuidMapping);
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 295ac0a..011a48c 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -266,8 +266,18 @@
             // WebView needs to make sure to always use the wrapped application context.
             ContextUtils.initApplicationContext(ResourcesContextWrapperFactory.get(ctx));
 
+            // Find the package ID for the package that WebView's resources come from.
+            // This will be the donor package if there is one, not our main package.
+            String resourcePackage = packageInfo.packageName;
+            if (packageInfo.applicationInfo.metaData != null) {
+                resourcePackage = packageInfo.applicationInfo.metaData.getString(
+                        "com.android.webview.WebViewDonorPackage", resourcePackage);
+            }
+            int packageId = webViewDelegate.getPackageId(
+                    ContextUtils.getApplicationContext().getResources(), resourcePackage);
+
             mAwInit.setUpResourcesOnBackgroundThread(
-                    packageInfo, ContextUtils.getApplicationContext());
+                    packageId, ContextUtils.getApplicationContext());
 
             try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
                          "WebViewChromiumFactoryProvider.initCommandLine")) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
index 84e2bee..1c10dd94 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -462,4 +462,89 @@
             webServer.shutdown();
         }
     }
+
+    // Test loadDataSync() with a page containing an iframe that has a data:
+    // URL for its source. WebView handles conversion from data: URLs to origins
+    // in  a different way than normal desktop and Android builds so we want to
+    // make sure commit time checks properly pass on WebView.
+    // See http://crbug.com/1013171 for details.
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testLoadDataWithDataUrlIframe() throws Throwable {
+        final TestAwContentsClient contentsClient = new TestAwContentsClient();
+        final AwTestContainerView testContainerView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testContainerView.getAwContents();
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+
+        final String iframeLoadedMessage = "iframe loaded";
+        final String iframeHtml = "<html><body><script>"
+                + "console.log('" + iframeLoadedMessage + "')"
+                + ";</script></body></html>";
+        final String pageHtml = "<html><body>"
+                + "<iframe src=\"data:text/html," + iframeHtml + "\"></iframe>"
+                + "</body></html>";
+
+        CallbackHelper onPageFinishedHelper = contentsClient.getOnPageFinishedHelper();
+        int onPageFinishedCallCount = onPageFinishedHelper.getCallCount();
+
+        TestAwContentsClient.AddMessageToConsoleHelper addMessageToConsoleHelper =
+                contentsClient.getAddMessageToConsoleHelper();
+        int logCallCount = addMessageToConsoleHelper.getCallCount();
+
+        // Test load with an anonymous opaque origin.
+        mActivityTestRule.loadDataSync(
+                awContents, contentsClient.getOnPageFinishedHelper(), pageHtml, "text/html", false);
+        onPageFinishedHelper.waitForCallback(onPageFinishedCallCount);
+
+        addMessageToConsoleHelper.waitForCallback(logCallCount);
+        Assert.assertEquals(iframeLoadedMessage, addMessageToConsoleHelper.getMessage());
+    }
+
+    // Test loadUrlSync() with a page containing an iframe that has a data: URL
+    // for its source. WebView handles conversion from data: URLs to origins in
+    // a different way than normal desktop and Android builds so we want to make
+    // sure commit time checks properly pass on WebView.
+    // See http://crbug.com/1013171 for details.
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testLoadUrlWithDataUrlIframe() throws Throwable {
+        final TestAwContentsClient contentsClient = new TestAwContentsClient();
+        final AwTestContainerView testContainerView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testContainerView.getAwContents();
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+
+        final String iframeLoadedMessage = "iframe loaded";
+        final String iframeHtml = "<html><body><script>"
+                + "console.log('" + iframeLoadedMessage + "')"
+                + ";</script></body></html>";
+        final String pageHtml = "<html><body>"
+                + "<iframe src=\"data:text/html," + iframeHtml + "\"></iframe>"
+                + "</body></html>";
+
+        CallbackHelper onPageFinishedHelper = contentsClient.getOnPageFinishedHelper();
+        int onPageFinishedCallCount = onPageFinishedHelper.getCallCount();
+
+        TestAwContentsClient.AddMessageToConsoleHelper addMessageToConsoleHelper =
+                contentsClient.getAddMessageToConsoleHelper();
+        int logCallCount = addMessageToConsoleHelper.getCallCount();
+
+        // Test load with an opaque origin that contains precursor info.
+        TestWebServer webServer = TestWebServer.start();
+        try {
+            final String url = webServer.setResponse("/page.html", pageHtml, null);
+
+            mActivityTestRule.loadUrlSync(
+                    awContents, contentsClient.getOnPageFinishedHelper(), url);
+            onPageFinishedHelper.waitForCallback(onPageFinishedCallCount);
+
+            addMessageToConsoleHelper.waitForCallback(logCallCount);
+            Assert.assertEquals(iframeLoadedMessage, addMessageToConsoleHelper.getMessage());
+        } finally {
+            webServer.shutdown();
+        }
+    }
 }
diff --git a/build/android/docs/README.md b/build/android/docs/README.md
index 404ca11..cecccf2 100644
--- a/build/android/docs/README.md
+++ b/build/android/docs/README.md
@@ -2,8 +2,8 @@
 
 * [android_app_bundles.md](android_app_bundles.md)
 * [build_config.md](build_config.md)
-* [building_dex.md](building_dex.md)
 * [coverage.md](coverage.md)
+* [java_toolchain.md](java_toolchain.md)
 * [lint.md](lint.md)
 * [life_of_a_resource.md](life_of_a_resource.md)
 * [../incremental_install/README.md](../incremental_install/README.md)
diff --git a/build/android/docs/building_dex.md b/build/android/docs/java_toolchain.md
similarity index 91%
rename from build/android/docs/building_dex.md
rename to build/android/docs/java_toolchain.md
index e671937..fed7654 100644
--- a/build/android/docs/building_dex.md
+++ b/build/android/docs/java_toolchain.md
@@ -20,7 +20,7 @@
 All targets names must end with "_java" so that the build system can distinguish
 them from non-java targets (or [other variations](https://cs.chromium.org/chromium/src/build/config/android/internal_rules.gni?rcl=ec2c17d7b4e424e060c3c7972842af87343526a1&l=20)).
 
-## Step 1: Compile
+## Step 1a: Compile with javac
 
 This step is the only step that does not apply to prebuilt targets.
 
@@ -39,6 +39,18 @@
     recompiled.
   * Prefer smaller targets to avoid slow compiles.
 
+## Step 1b: Compile with ErrorProne
+
+This step can be disabled via GN arg: `use_errorprone_java_compiler = false`
+
+* Concurrently with step 1a: [ErrorProne] compiles java files and checks for bug
+  patterns, including some [custom to Chromium][ep_plugins].
+* ErrorProne used to replace step 1a, but was changed to a concurrent step after
+  being identified as being slower.
+
+[ErrorProne]: https://errorprone.info/
+[ep_plugins]: /tools/android/errorprone_plugin/
+
 ## Step 2: Creating an .interface.jar
 
 This step happens in parallel with subsequent steps.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 312f41f0..64226d867 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8899937146435408864
\ No newline at end of file
+8899905896794786240
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b0df935f..d037ab1b 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8899939392191507248
\ No newline at end of file
+8899912590972143056
\ No newline at end of file
diff --git a/chrome/android/java/res_download/layout/download_home_toolbar.xml b/chrome/android/java/res_download/layout/download_home_toolbar.xml
index 2b6a684..5d4b42fb20 100644
--- a/chrome/android/java/res_download/layout/download_home_toolbar.xml
+++ b/chrome/android/java/res_download/layout/download_home_toolbar.xml
@@ -19,16 +19,7 @@
         android:id="@+id/download_toolbar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/toolbar_height_no_shadow"
-        style="@style/ModernToolbar">
-
-        <TextView
-            android:id="@+id/title_bar"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@style/TextAppearance.BlackHeadline"
-            android:text="@string/menu_downloads"/>
-
-    </org.chromium.chrome.browser.download.home.toolbar.DownloadHomeToolbar>
+        style="@style/ModernToolbar"/>
 
     <org.chromium.chrome.browser.ui.widget.FadingShadowView
         android:id="@+id/shadow"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
index da05ee6..557208b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
@@ -22,7 +22,6 @@
  */
 public class DownloadHomeToolbar extends SelectableListToolbar<ListItem> {
     private UiConfig mUiConfig;
-    private View mTitleBar;
 
     public DownloadHomeToolbar(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -32,7 +31,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mTitleBar = findViewById(R.id.title_bar);
         post(() -> {
             mUiConfig = new UiConfig(this);
             configureWideDisplayStyle(mUiConfig);
@@ -59,7 +57,6 @@
         boolean wasSelectionEnabled = mIsSelectionEnabled;
         super.onSelectionStateChange(selectedItems);
 
-        mTitleBar.setVisibility((mIsSelectionEnabled || isSearching()) ? GONE : VISIBLE);
         if (mIsSelectionEnabled) {
             int numSelected = mSelectionDelegate.getSelectedItems().size();
 
@@ -82,16 +79,4 @@
             }
         }
     }
-
-    @Override
-    public void showSearchView() {
-        super.showSearchView();
-        mTitleBar.setVisibility(GONE);
-    }
-
-    @Override
-    public void hideSearchView() {
-        super.hideSearchView();
-        mTitleBar.setVisibility(VISIBLE);
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
index cab8518..1f769cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
@@ -101,7 +101,7 @@
         mToolbar = mView.findViewById(R.id.download_toolbar);
         mShadow = mView.findViewById(R.id.shadow);
 
-        mToolbar.initialize(selectionDelegate, 0 /* titleResId */, R.id.normal_menu_group,
+        mToolbar.initialize(selectionDelegate, R.string.menu_downloads, R.id.normal_menu_group,
                 R.id.selection_mode_menu_group, hasCloseButton);
         mToolbar.setOnMenuItemClickListener(this ::onMenuItemClick);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java
index c57af3e8..3fc661cb0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -160,12 +161,29 @@
             }
         }
 
+        for (RelatedApplication installedApp : installedApps) {
+            setVersionInfo(installedApp);
+        }
+
         RelatedApplication[] installedAppsArray = new RelatedApplication[installedApps.size()];
         installedApps.toArray(installedAppsArray);
         return Pair.create(installedAppsArray, delayMillis);
     }
 
     /**
+     * Sets the version information, if available, to |installedApp|.
+     * @param installedApp
+     */
+    private void setVersionInfo(RelatedApplication installedApp) {
+        assert installedApp.id != null;
+        try {
+            PackageInfo info = mContext.getPackageManager().getPackageInfo(installedApp.id, 0);
+            installedApp.version = info.versionName;
+        } catch (NameNotFoundException e) {
+        }
+    }
+
+    /**
      * Returns whether or not the app ID is for an instant app/instant app holdback.
      */
     private boolean isInstantAppId(String appId) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
index 64db589..ef88574d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
@@ -58,6 +58,7 @@
         public CharSequence title;
         public PendingIntent intent;
         public @Type int type;
+        public @NotificationUmaTracker.ActionType int umaActionType;
 
         /**
          * If the action.type is TEXT, this corresponds to the placeholder text for the input.
@@ -66,11 +67,18 @@
 
         Action(int iconId, CharSequence title, PendingIntent intent, @Type int type,
                 String placeholder) {
+            this(iconId, title, intent, type, placeholder,
+                    NotificationUmaTracker.ActionType.UNKNOWN);
+        }
+
+        Action(int iconId, CharSequence title, PendingIntent intent, @Type int type,
+                String placeholder, @NotificationUmaTracker.ActionType int umaActionType) {
             this.iconId = iconId;
             this.title = title;
             this.intent = intent;
             this.type = type;
             this.placeholder = placeholder;
+            this.umaActionType = umaActionType;
         }
 
         Action(Bitmap iconBitmap, CharSequence title, PendingIntent intent, @Type int type,
@@ -80,6 +88,7 @@
             this.intent = intent;
             this.type = type;
             this.placeholder = placeholder;
+            this.umaActionType = NotificationUmaTracker.ActionType.UNKNOWN;
         }
     }
 
@@ -363,7 +372,8 @@
      */
     public NotificationBuilderBase addSettingsAction(
             int iconId, @Nullable CharSequence title, @Nullable PendingIntent intent) {
-        mSettingsAction = new Action(iconId, limitLength(title), intent, Action.Type.BUTTON, null);
+        mSettingsAction = new Action(iconId, limitLength(title), intent, Action.Type.BUTTON, null,
+                NotificationUmaTracker.ActionType.SETTINGS);
         return this;
     }
 
@@ -568,7 +578,13 @@
                                 .setLabel(action.placeholder)
                                 .build());
             }
-            builder.addAction(actionBuilder.build());
+
+            if (action.umaActionType == NotificationUmaTracker.ActionType.UNKNOWN) {
+                builder.addAction(actionBuilder.build());
+            } else {
+                builder.addAction(actionBuilder.build(), PendingIntent.FLAG_UPDATE_CURRENT,
+                        action.umaActionType);
+            }
         } else {
             builder.addAction(action.iconId, action.title, action.intent);
         }
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 802903a4..05ecad7e 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
@@ -92,7 +92,8 @@
             ActionType.DOWNLOAD_PAGE_RESUME, ActionType.DOWNLOAD_PAGE_CANCEL,
             ActionType.CONTENT_SUGGESTION_SETTINGS, ActionType.WEB_APP_ACTION_SHARE,
             ActionType.WEB_APP_ACTION_OPEN_IN_CHROME,
-            ActionType.OFFLINE_CONTENT_SUGGESTION_SETTINGS, ActionType.SHARING_TRY_AGAIN})
+            ActionType.OFFLINE_CONTENT_SUGGESTION_SETTINGS, ActionType.SHARING_TRY_AGAIN,
+            ActionType.SETTINGS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionType {
         int UNKNOWN = -1;
@@ -120,8 +121,10 @@
         // int SHARING_DISMISS = 10; deprecated
         // Try again button on sharing error notification.
         int SHARING_TRY_AGAIN = 11;
+        // Settings button for notifications.
+        int SETTINGS = 12;
 
-        int NUM_ENTRIES = 12;
+        int NUM_ENTRIES = 13;
     }
 
     private static final String LAST_SHOWN_NOTIFICATION_TYPE_KEY =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
index 8c5c266..a2bbf2ba 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
@@ -7,8 +7,6 @@
 import android.net.Uri;
 import android.support.test.filters.SmallTest;
 
-import androidx.browser.customtabs.CustomTabsService;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -21,7 +19,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.browserservices.OriginVerifier.OriginVerificationListener;
 import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
@@ -39,6 +36,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsService;
+
 /** Tests for OriginVerifier. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@@ -60,9 +59,6 @@
     private static final String SHA_256_FINGERPRINT_OFFICIAL =
             "19:75:B2:F1:71:77:BC:89:A5:DF:F3:1F:9E:64:A6:CA:E2:81:A5"
             + ":3D:C1:D1:D5:9B:1D:14:7F:E1:C8:2A:FA:00";
-    private static final String SHA_256_FINGERPRINT = ChromeVersionInfo.isOfficialBuild()
-            ? SHA_256_FINGERPRINT_OFFICIAL
-            : SHA_256_FINGERPRINT_PUBLIC;
 
     private Origin mHttpsOrigin;
     private Origin mHttpOrigin;
@@ -103,8 +99,16 @@
     @SmallTest
     public void testSHA256CertificateChecks() {
         Assert.assertEquals(STRING_ARRAY, OriginVerifier.byteArrayToHexString(BYTE_ARRAY));
-        Assert.assertEquals(SHA_256_FINGERPRINT,
-                OriginVerifier.getCertificateSHA256FingerprintForPackage(PACKAGE_NAME));
+
+        String fingerprint = OriginVerifier.getCertificateSHA256FingerprintForPackage(PACKAGE_NAME);
+
+        // We could try to determine which fingerprint we should be signed with, but it's easier to
+        // just check that we match either of the fingerprints. The chances of our code returning
+        // an incorrect value that just happens to match the wrong fingerprint is incredibly small.
+        if (SHA_256_FINGERPRINT_OFFICIAL.equals(fingerprint)) return;
+        if (SHA_256_FINGERPRINT_PUBLIC.equals(fingerprint)) return;
+
+        Assert.fail("Generated fingerprint matches neither official nor public.");
     }
 
     @Test
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index aad86bf..c0a5a82c 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -204,12 +204,12 @@
   TestWebappRegistry() : WebappRegistry() { }
 
   void UnregisterWebappsForUrls(
-      const base::Callback<bool(const GURL&)>& url_filter) override {
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter) override {
     // Mocks out a JNI call.
   }
 
   void ClearWebappHistoryForUrls(
-      const base::Callback<bool(const GURL&)>& url_filter) override {
+      const base::RepeatingCallback<bool(const GURL&)>& url_filter) override {
     // Mocks out a JNI call.
   }
 };
@@ -530,7 +530,7 @@
     return last_clear_mode_;
   }
 
-  const base::Callback<bool(const GURL&)>& last_filter() const {
+  const base::RepeatingCallback<bool(const GURL&)>& last_filter() const {
     return last_filter_;
   }
 
@@ -710,18 +710,19 @@
 // filters, but its constructor currently only takes a single URL and constructs
 // its own url filter. If an url filter was directly passed to
 // BrowsingDataRemover (what should eventually be the case), we can use the same
-// instance in the test as well, and thus simply test base::Callback::Equals()
-// in this matcher.
+// instance in the test as well, and thus simply test
+// base::RepeatingCallback::Equals() in this matcher.
 class ProbablySameFilterMatcher
-    : public MatcherInterface<const base::Callback<bool(const GURL&)>&> {
+    : public MatcherInterface<
+          const base::RepeatingCallback<bool(const GURL&)>&> {
  public:
   explicit ProbablySameFilterMatcher(
-      const base::Callback<bool(const GURL&)>& filter)
-      : to_match_(filter) {
-  }
+      const base::RepeatingCallback<bool(const GURL&)>& filter)
+      : to_match_(filter) {}
 
-  virtual bool MatchAndExplain(const base::Callback<bool(const GURL&)>& filter,
-                               MatchResultListener* listener) const {
+  virtual bool MatchAndExplain(
+      const base::RepeatingCallback<bool(const GURL&)>& filter,
+      MatchResultListener* listener) const {
     if (filter.is_null() && to_match_.is_null())
       return true;
     if (filter.is_null() != to_match_.is_null())
@@ -748,17 +749,17 @@
   }
 
  private:
-  const base::Callback<bool(const GURL&)>& to_match_;
+  const base::RepeatingCallback<bool(const GURL&)>& to_match_;
 };
 
-inline Matcher<const base::Callback<bool(const GURL&)>&> ProbablySameFilter(
-    const base::Callback<bool(const GURL&)>& filter) {
+inline Matcher<const base::RepeatingCallback<bool(const GURL&)>&>
+ProbablySameFilter(const base::RepeatingCallback<bool(const GURL&)>& filter) {
   return MakeMatcher(new ProbablySameFilterMatcher(filter));
 }
 
 bool ProbablySameFilters(
-    const base::Callback<bool(const GURL&)>& filter1,
-    const base::Callback<bool(const GURL&)>& filter2) {
+    const base::RepeatingCallback<bool(const GURL&)>& filter1,
+    const base::RepeatingCallback<bool(const GURL&)>& filter2) {
   return ProbablySameFilter(filter1).MatchAndExplain(filter2, nullptr);
 }
 
@@ -1899,7 +1900,7 @@
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemovePasswordStatistics) {
   RemovePasswordsTester tester(GetProfile());
-  base::Callback<bool(const GURL&)> empty_filter;
+  base::RepeatingCallback<bool(const GURL&)> empty_filter;
 
   EXPECT_CALL(*tester.store(), RemoveStatisticsByOriginAndTimeImpl(
                                    ProbablySameFilter(empty_filter),
@@ -1918,7 +1919,8 @@
   std::unique_ptr<BrowsingDataFilterBuilder> builder(
       BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
   builder->AddRegisterableDomain(kTestRegisterableDomain1);
-  base::Callback<bool(const GURL&)> filter = builder->BuildGeneralFilter();
+  base::RepeatingCallback<bool(const GURL&)> filter =
+      builder->BuildGeneralFilter();
 
   EXPECT_CALL(*tester.store(),
               RemoveStatisticsByOriginAndTimeImpl(
@@ -1930,7 +1932,7 @@
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemovePasswordsByTimeOnly) {
   RemovePasswordsTester tester(GetProfile());
-  base::Callback<bool(const GURL&)> filter =
+  base::RepeatingCallback<bool(const GURL&)> filter =
       BrowsingDataFilterBuilder::BuildNoopFilter();
 
   EXPECT_CALL(*tester.store(),
@@ -1948,7 +1950,8 @@
   std::unique_ptr<BrowsingDataFilterBuilder> builder(
       BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
   builder->AddRegisterableDomain(kTestRegisterableDomain1);
-  base::Callback<bool(const GURL&)> filter = builder->BuildGeneralFilter();
+  base::RepeatingCallback<bool(const GURL&)> filter =
+      builder->BuildGeneralFilter();
 
   EXPECT_CALL(*tester.store(),
               RemoveLoginsByURLAndTimeImpl(ProbablySameFilter(filter), _, _))
@@ -1961,7 +1964,7 @@
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, DisableAutoSignIn) {
   RemovePasswordsTester tester(GetProfile());
-  base::Callback<bool(const GURL&)> empty_filter =
+  base::RepeatingCallback<bool(const GURL&)> empty_filter =
       BrowsingDataFilterBuilder::BuildNoopFilter();
 
   EXPECT_CALL(
@@ -1977,7 +1980,7 @@
 TEST_F(ChromeBrowsingDataRemoverDelegateTest,
        DisableAutoSignInAfterRemovingPasswords) {
   RemovePasswordsTester tester(GetProfile());
-  base::Callback<bool(const GURL&)> empty_filter =
+  base::RepeatingCallback<bool(const GURL&)> empty_filter =
       BrowsingDataFilterBuilder::BuildNoopFilter();
 
   EXPECT_CALL(*tester.store(), RemoveLoginsByURLAndTimeImpl(_, _, _))
diff --git a/chrome/browser/chrome_back_forward_cache_browsertest.cc b/chrome/browser/chrome_back_forward_cache_browsertest.cc
index 36041b6..b02950c9 100644
--- a/chrome/browser/chrome_back_forward_cache_browsertest.cc
+++ b/chrome/browser/chrome_back_forward_cache_browsertest.cc
@@ -2,30 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 class ChromeBackForwardCacheBrowserTest : public InProcessBrowserTest {
  public:
-  ChromeBackForwardCacheBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kBackForwardCache,
-        {
-            // Set a very long TTL before expiration (longer than the test
-            // timeout) so tests that are expecting deletion don't pass when
-            // they shouldn't.
-            {"TimeToLiveInBackForwardCacheInSeconds", "3600"},
-        });
-  }
-  ~ChromeBackForwardCacheBrowserTest() override {}
+  ChromeBackForwardCacheBrowserTest() = default;
+  ~ChromeBackForwardCacheBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -45,6 +43,26 @@
   }
 
  protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // For using an HTTPS server.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kIgnoreCertificateErrors);
+    // For using WebBluetooth.
+    command_line->AppendSwitch(
+        switches::kEnableExperimentalWebPlatformFeatures);
+
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kBackForwardCache,
+        {
+            // Set a very long TTL before expiration (longer than the test
+            // timeout) so tests that are expecting deletion don't pass when
+            // they shouldn't.
+            {"TimeToLiveInBackForwardCacheInSeconds", "3600"},
+        });
+
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
   content::WebContents* web_contents() const {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
@@ -144,3 +162,40 @@
   EXPECT_EQ("rfh_a", content::EvalJs(rfh_a, "token"));
   EXPECT_EQ("rfh_b", content::EvalJs(rfh_b, "token"));
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, WebBluetooth) {
+  // Fake the BluetoothAdapter to say it's present.
+  scoped_refptr<device::MockBluetoothAdapter> adapter =
+      new testing::NiceMock<device::MockBluetoothAdapter>;
+  device::BluetoothAdapterFactory::SetAdapterForTesting(adapter);
+
+  // WebBluetooth requires HTTPS.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.AddDefaultHandlers(GetChromeTestDataDir());
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
+  ASSERT_TRUE(https_server.Start());
+  GURL url(https_server.GetURL("a.com", "/back_forward_cache/no-favicon.html"));
+
+  EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
+  content::BackForwardCacheDisabledTester tester;
+
+  EXPECT_EQ("device not found", content::EvalJs(current_frame_host(), R"(
+    new Promise(resolve => {
+      navigator.bluetooth.requestDevice({
+        filters: [
+          { services: [0x1802, 0x1803] },
+        ]
+      })
+      .then(() => resolve("device found"))
+      .catch(() => resolve("device not found"))
+    });
+  )"));
+  EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
+      current_frame_host()->GetProcess()->GetID(),
+      current_frame_host()->GetRoutingID(), "WebBluetooth"));
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  content::RenderFrameDeletedObserver delete_observer(current_frame_host());
+  EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com")));
+  delete_observer.WaitUntilDeleted();
+}
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 48abea7..5cc1649 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -832,7 +832,9 @@
                                  "Combo box * of *"));
 }
 
-IN_PROC_BROWSER_TEST_F(OobeSpokenFeedbackTest, ChromeVoxPanelTabsMenuEmpty) {
+// This test is flaky (https://crbug.com/1013551).
+IN_PROC_BROWSER_TEST_F(OobeSpokenFeedbackTest,
+                       DISABLED_ChromeVoxPanelTabsMenuEmpty) {
   // The ChromeVox panel should not populate the tabs menu if we are in the
   // OOBE.
   ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
index ee99127..2fb8c96 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -127,15 +127,6 @@
   ~ArcAccessibilityHelperBridgeFactory() override = default;
 };
 
-}  // namespace
-
-// static
-ArcAccessibilityHelperBridge*
-ArcAccessibilityHelperBridge::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return ArcAccessibilityHelperBridgeFactory::GetForBrowserContext(context);
-}
-
 static constexpr const char* kTextShadowRaised =
     "-2px -2px 4px rgba(0, 0, 0, 0.5)";
 static constexpr const char* kTextShadowDepressed =
@@ -146,11 +137,63 @@
 static constexpr const char* kTextShadowDropShadow =
     "0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black";
 
-void ArcAccessibilityHelperBridge::UpdateCaptionSettings() const {
-  base::Optional<ui::CaptionStyle> prefs_caption_style =
-      pref_names_util::GetCaptionStyleFromPrefs(profile_->GetPrefs());
+std::string GetARGBFromPrefs(PrefService* prefs,
+                             const char* color_pref_name,
+                             const char* opacity_pref_name) {
+  const std::string color = prefs->GetString(color_pref_name);
+  if (color.empty()) {
+    return "";
+  }
+  const int opacity = prefs->GetInteger(opacity_pref_name);
+  return base::StringPrintf("rgba(%s,%s)", color.c_str(),
+                            base::NumberToString(opacity / 100.0).c_str());
+}
 
-  if (!prefs_caption_style)
+}  // namespace
+
+arc::mojom::CaptionStylePtr GetCaptionStyleFromPrefs(PrefService* prefs) {
+  DCHECK(prefs);
+
+  arc::mojom::CaptionStylePtr style = arc::mojom::CaptionStyle::New();
+
+  style->text_size = prefs->GetString(prefs::kAccessibilityCaptionsTextSize);
+  style->text_color =
+      GetARGBFromPrefs(prefs, prefs::kAccessibilityCaptionsTextColor,
+                       prefs::kAccessibilityCaptionsTextOpacity);
+  style->background_color =
+      GetARGBFromPrefs(prefs, prefs::kAccessibilityCaptionsBackgroundColor,
+                       prefs::kAccessibilityCaptionsBackgroundOpacity);
+  style->user_locale = prefs->GetString(language::prefs::kApplicationLocale);
+
+  const std::string test_shadow =
+      prefs->GetString(prefs::kAccessibilityCaptionsTextShadow);
+  if (test_shadow == kTextShadowRaised) {
+    style->text_shadow_type = arc::mojom::CaptionTextShadowType::RAISED;
+  } else if (test_shadow == kTextShadowDepressed) {
+    style->text_shadow_type = arc::mojom::CaptionTextShadowType::DEPRESSED;
+  } else if (test_shadow == kTextShadowUniform) {
+    style->text_shadow_type = arc::mojom::CaptionTextShadowType::UNIFORM;
+  } else if (test_shadow == kTextShadowDropShadow) {
+    style->text_shadow_type = arc::mojom::CaptionTextShadowType::DROP_SHADOW;
+  } else {
+    style->text_shadow_type = arc::mojom::CaptionTextShadowType::NONE;
+  }
+
+  return style;
+}
+
+// static
+ArcAccessibilityHelperBridge*
+ArcAccessibilityHelperBridge::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return ArcAccessibilityHelperBridgeFactory::GetForBrowserContext(context);
+}
+
+void ArcAccessibilityHelperBridge::UpdateCaptionSettings() const {
+  arc::mojom::CaptionStylePtr caption_style =
+      GetCaptionStyleFromPrefs(profile_->GetPrefs());
+
+  if (!caption_style)
     return;
 
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
@@ -159,29 +202,6 @@
   if (!instance)
     return;
 
-  arc::mojom::CaptionStylePtr caption_style = arc::mojom::CaptionStyle::New();
-  caption_style->text_size = prefs_caption_style->text_size.c_str();
-  caption_style->background_color = prefs_caption_style->background_color;
-  caption_style->text_color = prefs_caption_style->text_color;
-  caption_style->user_locale =
-      profile_->GetPrefs()->GetString(language::prefs::kApplicationLocale);
-
-  // Convert the text shadow CSS string to a mojom::CaptionTextShadowType enum.
-  if (prefs_caption_style->text_shadow == kTextShadowRaised) {
-    caption_style->text_shadow_type = arc::mojom::CaptionTextShadowType::RAISED;
-  } else if (prefs_caption_style->text_shadow == kTextShadowDepressed) {
-    caption_style->text_shadow_type =
-        arc::mojom::CaptionTextShadowType::DEPRESSED;
-  } else if (prefs_caption_style->text_shadow == kTextShadowUniform) {
-    caption_style->text_shadow_type =
-        arc::mojom::CaptionTextShadowType::UNIFORM;
-  } else if (prefs_caption_style->text_shadow == kTextShadowDropShadow) {
-    caption_style->text_shadow_type =
-        arc::mojom::CaptionTextShadowType::DROP_SHADOW;
-  } else {
-    caption_style->text_shadow_type = arc::mojom::CaptionTextShadowType::NONE;
-  }
-
   instance->SetCaptionStyle(std::move(caption_style));
 }
 
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
index 6c2ece76..dba47819 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
@@ -22,6 +22,7 @@
 #include "ui/aura/window_tracker.h"
 #include "ui/wm/public/activation_change_observer.h"
 
+class PrefService;
 class Profile;
 
 namespace content {
@@ -37,6 +38,8 @@
 class AXTreeSourceArc;
 class ArcBridgeService;
 
+arc::mojom::CaptionStylePtr GetCaptionStyleFromPrefs(PrefService* prefs);
+
 // ArcAccessibilityHelperBridge is an instance to receive converted Android
 // accessibility events and info via mojo interface and dispatch them to chrome
 // os components.
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
index 5054380..01a45b5 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -18,7 +18,9 @@
 #include "base/command_line.h"
 #include "base/observer_list.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
 #include "chrome/common/extensions/api/accessibility_private.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -26,6 +28,9 @@
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/exo/shell_surface.h"
 #include "components/exo/shell_surface_util.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "extensions/browser/test_event_router.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
@@ -546,4 +551,65 @@
   arc_notification_surface_manager_->RemoveSurface(surface.get());
 }
 
+class GetCaptionStyleFromPrefsTests : public ::testing::Test {
+ public:
+  void SetUp() override {
+    prefs_.registry()->RegisterStringPref(prefs::kAccessibilityCaptionsTextSize,
+                                          "");
+    prefs_.registry()->RegisterStringPref(
+        prefs::kAccessibilityCaptionsTextColor, "");
+    prefs_.registry()->RegisterIntegerPref(
+        prefs::kAccessibilityCaptionsTextOpacity, 100);
+    prefs_.registry()->RegisterStringPref(
+        prefs::kAccessibilityCaptionsBackgroundColor, "");
+    prefs_.registry()->RegisterIntegerPref(
+        prefs::kAccessibilityCaptionsBackgroundOpacity, 100);
+    prefs_.registry()->RegisterStringPref(
+        prefs::kAccessibilityCaptionsTextShadow, "");
+    prefs_.registry()->RegisterStringPref(language::prefs::kApplicationLocale,
+                                          "");
+  }
+
+ protected:
+  TestingPrefServiceSimple prefs_;
+};
+
+TEST_F(GetCaptionStyleFromPrefsTests, ValidValues) {
+  prefs_.SetUserPref(prefs::kAccessibilityCaptionsTextSize,
+                     std::make_unique<base::Value>("200%"));
+  prefs_.SetUserPref(prefs::kAccessibilityCaptionsTextColor,
+                     std::make_unique<base::Value>("10,20,30"));
+  prefs_.SetUserPref(prefs::kAccessibilityCaptionsTextOpacity,
+                     std::make_unique<base::Value>(90));
+  prefs_.SetUserPref(prefs::kAccessibilityCaptionsBackgroundColor,
+                     std::make_unique<base::Value>("40,50,60"));
+  prefs_.SetUserPref(prefs::kAccessibilityCaptionsBackgroundOpacity,
+                     std::make_unique<base::Value>(80));
+  prefs_.SetUserPref(
+      prefs::kAccessibilityCaptionsTextShadow,
+      std::make_unique<base::Value>("-2px -2px 4px rgba(0, 0, 0, 0.5)"));
+  prefs_.SetUserPref(language::prefs::kApplicationLocale,
+                     std::make_unique<base::Value>("my_locale"));
+
+  auto style = GetCaptionStyleFromPrefs(&prefs_);
+
+  ASSERT_TRUE(style);
+  EXPECT_EQ("200%", style->text_size);
+  EXPECT_EQ("rgba(10,20,30,0.9)", style->text_color);
+  EXPECT_EQ("rgba(40,50,60,0.8)", style->background_color);
+  EXPECT_EQ("my_locale", style->user_locale);
+  EXPECT_EQ(arc::mojom::CaptionTextShadowType::RAISED, style->text_shadow_type);
+}
+
+TEST_F(GetCaptionStyleFromPrefsTests, EmptyValues) {
+  auto style = GetCaptionStyleFromPrefs(&prefs_);
+
+  ASSERT_TRUE(style);
+  EXPECT_EQ("", style->text_size);
+  EXPECT_EQ("", style->text_color);
+  EXPECT_EQ("", style->background_color);
+  EXPECT_EQ("", style->user_locale);
+  EXPECT_EQ(arc::mojom::CaptionTextShadowType::NONE, style->text_shadow_type);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/login/version_info_updater.cc b/chrome/browser/chromeos/login/version_info_updater.cc
index 96dc97e..cd099c9 100644
--- a/chrome/browser/chromeos/login/version_info_updater.cc
+++ b/chrome/browser/chromeos/login/version_info_updater.cc
@@ -63,7 +63,8 @@
   if (base::SysInfo::IsRunningOnChromeOS()) {
     base::PostTaskAndReplyWithResult(
         FROM_HERE,
-        {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskPriority::USER_VISIBLE},
         base::Bind(&version_loader::GetVersion,
                    is_official_build ? version_loader::VERSION_SHORT_WITH_DATE
                                      : version_loader::VERSION_FULL),
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index 4a2a019..d2e6db2 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -636,6 +636,8 @@
     status.second.allowed = false;
   }
   microphone_camera_state_ = MICROPHONE_CAMERA_NOT_ACCESSED;
+  camera_was_just_granted_on_site_level_ = false;
+  mic_was_just_granted_on_site_level_ = false;
   load_plugins_link_enabled_ = true;
   content_settings::UpdateLocationBarUiForWebContents(web_contents());
 }
@@ -697,6 +699,17 @@
     ContentSetting setting = map_->GetContentSetting(
         media_origin, media_origin, content_type, std::string());
     ContentSettingsStatus& status = content_settings_status_[content_type];
+
+    if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
+        setting == CONTENT_SETTING_ALLOW) {
+      mic_was_just_granted_on_site_level_ = true;
+    }
+
+    if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA &&
+        setting == CONTENT_SETTING_ALLOW) {
+      camera_was_just_granted_on_site_level_ = true;
+    }
+
     status.allowed = setting == CONTENT_SETTING_ALLOW;
     status.blocked = setting == CONTENT_SETTING_BLOCK;
   }
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index aa45d1e..2913871a 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -201,6 +201,14 @@
     return media_stream_selected_video_device_;
   }
 
+  bool camera_was_just_granted_on_site_level() {
+    return camera_was_just_granted_on_site_level_;
+  }
+
+  bool mic_was_just_granted_on_site_level() {
+    return mic_was_just_granted_on_site_level_;
+  }
+
   // Returns the state of the camera and microphone usage.
   // The return value always includes all active media capture devices, on top
   // of the devices from the last request.
@@ -451,6 +459,11 @@
   std::string media_stream_requested_audio_device_;
   std::string media_stream_requested_video_device_;
 
+  // The camera and/or microphone permission was granted to this origin from a
+  // permission prompt that was triggered by the currently active document.
+  bool camera_was_just_granted_on_site_level_ = false;
+  bool mic_was_just_granted_on_site_level_ = false;
+
   // Observer to watch for content settings changed.
   ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_{
       this};
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 3a3d705..bd35106 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -54,6 +54,9 @@
 const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRules[] =
     "triggeredRules";
 const char SafeBrowsingPrivateEventRouter::kKeyThreatType[] = "threatType";
+const char SafeBrowsingPrivateEventRouter::kKeyContentType[] = "contentType";
+const char SafeBrowsingPrivateEventRouter::kKeyContentSize[] = "contentSize";
+const char SafeBrowsingPrivateEventRouter::kKeyTrigger[] = "trigger";
 
 const char SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent[] =
     "passwordReuseEvent";
@@ -68,6 +71,12 @@
 const char SafeBrowsingPrivateEventRouter::kKeyLargeUnscannedFileEvent[] =
     "largeUnscannedFileEvent";
 
+const char SafeBrowsingPrivateEventRouter::kTriggerFileDownload[] =
+    "FILE_DOWNLOAD";
+const char SafeBrowsingPrivateEventRouter::kTriggerFileUpload[] = "FILE_UPLOAD";
+const char SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload[] =
+    "WEB_CONTENT_UPLOAD";
+
 SafeBrowsingPrivateEventRouter::SafeBrowsingPrivateEventRouter(
     content::BrowserContext* context)
     : context_(context) {
@@ -148,7 +157,9 @@
 void SafeBrowsingPrivateEventRouter::OnDangerousDownloadOpened(
     const GURL& url,
     const std::string& file_name,
-    const std::string& download_digest_sha256) {
+    const std::string& download_digest_sha256,
+    const std::string& mime_type,
+    const int64_t content_size) {
   api::safe_browsing_private::DangerousDownloadInfo params;
   params.url = url.spec();
   params.file_name = file_name;
@@ -174,6 +185,12 @@
     event.SetStringKey(kKeyFileName, params.file_name);
     event.SetStringKey(kKeyDownloadDigestSha256, params.download_digest_sha256);
     event.SetStringKey(kKeyProfileUserName, params.user_name);
+    event.SetStringKey(kKeyContentType, mime_type);
+    // |content_size| can be set to -1 to indicate an unknown size, in which
+    // case the field is not set.
+    if (content_size >= 0)
+      event.SetIntKey(kKeyContentSize, content_size);
+    event.SetStringKey(kKeyTrigger, kTriggerFileDownload);
     ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
   }
 }
@@ -256,7 +273,10 @@
     const GURL& url,
     const std::string& file_name,
     const std::string& download_digest_sha256,
-    const std::string& threat_type) {
+    const std::string& threat_type,
+    const std::string& mime_type,
+    const std::string& trigger,
+    const int64_t content_size) {
   if (IsRealtimeReportingEnabled()) {
     // Create a real-time event dictionary from the arguments and report it.
     base::Value event(base::Value::Type::DICTIONARY);
@@ -265,6 +285,12 @@
     event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
     event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
     event.SetStringKey(kKeyThreatType, threat_type);
+    event.SetStringKey(kKeyContentType, mime_type);
+    // |content_size| can be set to -1 to indicate an unknown size, in which
+    // case the field is not set.
+    if (content_size >= 0)
+      event.SetIntKey(kKeyContentSize, content_size);
+    event.SetStringKey(kKeyTrigger, trigger);
     ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
   }
 }
@@ -273,7 +299,10 @@
     const safe_browsing::DlpDeepScanningVerdict& verdict,
     const GURL& url,
     const std::string& file_name,
-    const std::string& download_digest_sha256) {
+    const std::string& download_digest_sha256,
+    const std::string& mime_type,
+    const std::string& trigger,
+    const int64_t content_size) {
   if (IsRealtimeReportingEnabled()) {
     // Create a real-time event dictionary from the arguments and report it.
     base::Value event(base::Value::Type::DICTIONARY);
@@ -281,6 +310,12 @@
     event.SetStringKey(kKeyFileName, file_name);
     event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
     event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
+    event.SetStringKey(kKeyContentType, mime_type);
+    // |content_size| can be set to -1 to indicate an unknown size, in which
+    // case the field is not set.
+    if (content_size >= 0)
+      event.SetIntKey(kKeyContentSize, content_size);
+    event.SetStringKey(kKeyTrigger, trigger);
 
     base::ListValue triggered_rules;
     for (auto rule : verdict.triggered_rules()) {
@@ -294,7 +329,10 @@
 void SafeBrowsingPrivateEventRouter::OnLargeUnscannedFileEvent(
     const GURL& url,
     const std::string& file_name,
-    const std::string& download_digest_sha256) {
+    const std::string& download_digest_sha256,
+    const std::string& mime_type,
+    const std::string& trigger,
+    const int64_t content_size) {
   if (IsRealtimeReportingEnabled()) {
     // Create a real-time event dictionary from the arguments and report it.
     base::Value event(base::Value::Type::DICTIONARY);
@@ -302,6 +340,12 @@
     event.SetStringKey(kKeyFileName, file_name);
     event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
     event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
+    event.SetStringKey(kKeyContentType, mime_type);
+    // |content_size| can be set to -1 to indicate an unknown size, in which
+    // case the field is not set.
+    if (content_size >= 0)
+      event.SetIntKey(kKeyContentSize, content_size);
+    event.SetStringKey(kKeyTrigger, trigger);
     ReportRealtimeEvent(kKeyLargeUnscannedFileEvent, std::move(event));
   }
 }
@@ -310,7 +354,9 @@
     const GURL& url,
     const std::string& file_name,
     const std::string& download_digest_sha256,
-    const std::string& threat_type) {
+    const std::string& threat_type,
+    const std::string& mime_type,
+    const int64_t content_size) {
   if (!IsRealtimeReportingEnabled())
     return;
 
@@ -322,6 +368,12 @@
   event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
   event.SetStringKey(kKeyThreatType, threat_type);
   event.SetBoolKey(kKeyClickedThrough, false);
+  event.SetStringKey(kKeyContentType, mime_type);
+  // |content_size| can be set to -1 to indicate an unknown size, in which case
+  // the field is not set.
+  if (content_size >= 0)
+    event.SetIntKey(kKeyContentSize, content_size);
+  event.SetStringKey(kKeyTrigger, kTriggerFileDownload);
   ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
 }
 
@@ -329,7 +381,9 @@
     const GURL& url,
     const std::string& file_name,
     const std::string& download_digest_sha256,
-    const std::string& threat_type) {
+    const std::string& threat_type,
+    const std::string& mime_type,
+    const int64_t content_size) {
   if (!IsRealtimeReportingEnabled())
     return;
 
@@ -341,6 +395,12 @@
   event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
   event.SetStringKey(kKeyThreatType, threat_type);
   event.SetBoolKey(kKeyClickedThrough, true);
+  event.SetStringKey(kKeyContentType, mime_type);
+  // |content_size| can be set to -1 to indicate an unknown size, in which case
+  // the field is not set.
+  if (content_size >= 0)
+    event.SetIntKey(kKeyContentSize, content_size);
+  event.SetStringKey(kKeyTrigger, kTriggerFileDownload);
   ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
 }
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index fcc0b25..754a73b3 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -59,6 +59,9 @@
   static const char kKeyClickedThrough[];
   static const char kKeyTriggeredRules[];
   static const char kKeyThreatType[];
+  static const char kKeyContentType[];
+  static const char kKeyContentSize[];
+  static const char kKeyTrigger[];
 
   static const char kKeyPasswordReuseEvent[];
   static const char kKeyPasswordChangedEvent[];
@@ -67,6 +70,11 @@
   static const char kKeySensitiveDataEvent[];
   static const char kKeyLargeUnscannedFileEvent[];
 
+  // String constants for the "trigger" event field.
+  static const char kTriggerFileDownload[];
+  static const char kTriggerFileUpload[];
+  static const char kTriggerWebContentUpload[];
+
   explicit SafeBrowsingPrivateEventRouter(content::BrowserContext* context);
 
   ~SafeBrowsingPrivateEventRouter() override;
@@ -83,7 +91,9 @@
   // Notifies listeners that the user just opened a dangerous download.
   void OnDangerousDownloadOpened(const GURL& url,
                                  const std::string& file_name,
-                                 const std::string& download_digest_sha256);
+                                 const std::string& download_digest_sha256,
+                                 const std::string& mime_type,
+                                 const int64_t content_size);
 
   // Notifies listeners that the user saw a security interstitial.
   void OnSecurityInterstitialShown(const GURL& url,
@@ -99,19 +109,28 @@
   void OnDangerousDeepScanningResult(const GURL& url,
                                      const std::string& file_name,
                                      const std::string& download_digest_sha256,
-                                     const std::string& threat_type);
+                                     const std::string& threat_type,
+                                     const std::string& mime_type,
+                                     const std::string& trigger,
+                                     const int64_t content_size);
 
   // Notifies listeners that scanning for sensitive data detected a violation.
   void OnSensitiveDataEvent(
       const safe_browsing::DlpDeepScanningVerdict& verdict,
       const GURL& url,
       const std::string& file_name,
-      const std::string& download_digest_sha256);
+      const std::string& download_digest_sha256,
+      const std::string& mime_type,
+      const std::string& trigger,
+      const int64_t content_size);
 
   // Notifies listeners that deep scanning failed, since the file was too large.
   void OnLargeUnscannedFileEvent(const GURL& url,
                                  const std::string& file_name,
-                                 const std::string& download_digest_sha256);
+                                 const std::string& download_digest_sha256,
+                                 const std::string& mime_type,
+                                 const std::string& trigger,
+                                 const int64_t content_size);
 
   // Notifies listeners that the user saw a download warning.
   // - |url| is the download URL
@@ -121,7 +140,9 @@
   void OnDangerousDownloadWarning(const GURL& url,
                                   const std::string& file_name,
                                   const std::string& download_digest_sha256,
-                                  const std::string& threat_type);
+                                  const std::string& threat_type,
+                                  const std::string& mime_type,
+                                  const int64_t content_size);
 
   // Notifies listeners that the user bypassed a download warning.
   // - |url| is the download URL
@@ -132,7 +153,9 @@
       const GURL& url,
       const std::string& file_name,
       const std::string& download_digest_sha256,
-      const std::string& threat_type);
+      const std::string& threat_type,
+      const std::string& mime_type,
+      const int64_t content_size);
 
   void SetCloudPolicyClientForTesting(
       std::unique_ptr<policy::CloudPolicyClient> client);
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 79136c1..b0085a1e 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -4,6 +4,7 @@
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
 
 #include "base/bind.h"
+#include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/mock_callback.h"
 #include "base/values.h"
@@ -94,7 +95,7 @@
     SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnDangerousDownloadOpened(GURL("https://evil.com/malware.exe"),
                                     "/path/to/malware.exe",
-                                    "sha256_or_malware_exe");
+                                    "sha256_of_malware_exe", "exe", 1234);
   }
 
   void TriggerOnSecurityInterstitialShownEvent() {
@@ -109,6 +110,20 @@
                                           "PHISHING", -201);
   }
 
+  void TriggerOnDangerousDownloadWarningEvent() {
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
+        ->OnDangerousDownloadWarning(
+            GURL("https://maybevil.com/warning.exe"), "/path/to/warning.exe",
+            "sha256_of_warning_exe", "POTENTIALLY_UNWANTED", "exe", 567);
+  }
+
+  void TriggerOnDangerousDownloadWarningEventBypass() {
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
+        ->OnDangerousDownloadWarning(
+            GURL("https://bypassevil.com/bypass.exe"), "/path/to/bypass.exe",
+            "sha256_of_bypass_exe", "BYPASSED_WARNING", "exe", 890);
+  }
+
   void SetReportingPolicy(bool enabled) {
     TestingBrowserProcess::GetGlobal()->local_state()->SetBoolean(
         prefs::kUnsafeEventsReportingEnabled, enabled);
@@ -235,7 +250,7 @@
   EXPECT_EQ("/path/to/malware.exe",
             captured_args.FindKey("fileName")->GetString());
   EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
-  EXPECT_EQ("sha256_or_malware_exe",
+  EXPECT_EQ("sha256_of_malware_exe",
             captured_args.FindKey("downloadDigestSha256")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
@@ -254,6 +269,12 @@
   EXPECT_EQ(
       "/path/to/malware.exe",
       *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName));
+  EXPECT_EQ("exe", *event->FindStringKey(
+                       SafeBrowsingPrivateEventRouter::kKeyContentType));
+  EXPECT_EQ(1234, *event->FindIntKey(
+                      SafeBrowsingPrivateEventRouter::kKeyContentSize));
+  EXPECT_EQ(SafeBrowsingPrivateEventRouter::kTriggerFileDownload,
+            *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyTrigger));
 }
 
 TEST_F(SafeBrowsingPrivateEventRouterTest,
@@ -337,6 +358,83 @@
       *event->FindBoolKey(SafeBrowsingPrivateEventRouter::kKeyClickedThrough));
 }
 
+TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnDangerousDownloadWarning) {
+  SetUpRouters();
+  SafeBrowsingEventObserver event_observer(
+      api::safe_browsing_private::OnDangerousDownloadOpened::kEventName);
+  event_router_->AddEventObserver(&event_observer);
+
+  base::Value report;
+  EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
+      .WillOnce(CaptureArg(&report));
+
+  TriggerOnDangerousDownloadWarningEvent();
+  base::RunLoop().RunUntilIdle();
+
+  Mock::VerifyAndClearExpectations(client_);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* event_list =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventListKey);
+  ASSERT_NE(nullptr, event_list);
+  EXPECT_EQ(base::Value::Type::LIST, event_list->type());
+  base::Value::ListStorage& mutable_list = event_list->GetList();
+  ASSERT_EQ(1, (int)mutable_list.size());
+  base::Value wrapper = std::move(mutable_list[0]);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
+  base::Value* event = wrapper.FindKey(
+      SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
+  EXPECT_NE(nullptr, event);
+  EXPECT_EQ(
+      "/path/to/warning.exe",
+      *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName));
+  EXPECT_EQ("exe", *event->FindStringKey(
+                       SafeBrowsingPrivateEventRouter::kKeyContentType));
+  EXPECT_EQ(
+      567, *event->FindIntKey(SafeBrowsingPrivateEventRouter::kKeyContentSize));
+  EXPECT_EQ(
+      "POTENTIALLY_UNWANTED",
+      *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyThreatType));
+}
+
+TEST_F(SafeBrowsingPrivateEventRouterTest,
+       TestOnDangerousDownloadWarningBypass) {
+  SetUpRouters();
+  SafeBrowsingEventObserver event_observer(
+      api::safe_browsing_private::OnDangerousDownloadOpened::kEventName);
+  event_router_->AddEventObserver(&event_observer);
+
+  base::Value report;
+  EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
+      .WillOnce(CaptureArg(&report));
+
+  TriggerOnDangerousDownloadWarningEventBypass();
+  base::RunLoop().RunUntilIdle();
+
+  Mock::VerifyAndClearExpectations(client_);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* event_list =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventListKey);
+  ASSERT_NE(nullptr, event_list);
+  EXPECT_EQ(base::Value::Type::LIST, event_list->type());
+  base::Value::ListStorage& mutable_list = event_list->GetList();
+  ASSERT_EQ(1, (int)mutable_list.size());
+  base::Value wrapper = std::move(mutable_list[0]);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
+  base::Value* event = wrapper.FindKey(
+      SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
+  EXPECT_NE(nullptr, event);
+  EXPECT_EQ(
+      "/path/to/bypass.exe",
+      *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName));
+  EXPECT_EQ("exe", *event->FindStringKey(
+                       SafeBrowsingPrivateEventRouter::kKeyContentType));
+  EXPECT_EQ(
+      890, *event->FindIntKey(SafeBrowsingPrivateEventRouter::kKeyContentSize));
+  EXPECT_EQ(
+      "BYPASSED_WARNING",
+      *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyThreatType));
+}
+
 TEST_F(SafeBrowsingPrivateEventRouterTest, PolicyControlOnToOffIsDynamic) {
   SetUpRouters();
   SafeBrowsingEventObserver event_observer(
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index 71f4ba9..c630342 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -35,6 +35,7 @@
 
 #if defined(OS_MACOSX)
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
 #endif
@@ -254,9 +255,13 @@
         return;
       } else if (system_audio_permission == SystemPermission::kRestricted ||
                  system_audio_permission == SystemPermission::kDenied) {
+        content_settings::UpdateLocationBarUiForWebContents(web_contents);
         final_result =
             blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
         system_media_permissions::SystemAudioCapturePermissionBlocked();
+      } else {
+        DCHECK_EQ(system_audio_permission, SystemPermission::kAllowed);
+        content_settings::UpdateLocationBarUiForWebContents(web_contents);
       }
     }
     if (request.video_type ==
@@ -278,9 +283,13 @@
         return;
       } else if (system_video_permission == SystemPermission::kRestricted ||
                  system_video_permission == SystemPermission::kDenied) {
+        content_settings::UpdateLocationBarUiForWebContents(web_contents);
         final_result =
             blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED;
         system_media_permissions::SystemVideoCapturePermissionBlocked();
+      } else {
+        DCHECK_EQ(system_video_permission, SystemPermission::kAllowed);
+        content_settings::UpdateLocationBarUiForWebContents(web_contents);
       }
     }
   }
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index d3caa4d3..107e4b5 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -58,8 +58,12 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/test/browser_test_utils.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/back_forward_cache_util.h"
+#include "content/public/test/test_utils.h"
 #include "net/base/filename_util.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -89,6 +93,38 @@
   ~PasswordManagerBrowserTest() override = default;
 };
 
+// Test class for testing password manager with the BackForwardCache feature
+// enabled. More info about the BackForwardCache, see:
+// http://doc/1YrBKX_eFMA9KoYof-eVThT35jcTqWcH_rRxYbR5RapU
+class PasswordManagerBackForwardCacheBrowserTest
+    : public PasswordManagerBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    PasswordManagerBrowserTest ::SetUpOnMainThread();
+  }
+
+  bool IsGetCredentialsSuccessful() {
+    return "success" == content::EvalJs(WebContents()->GetMainFrame(), R"(
+      new Promise(resolve => {
+        navigator.credentials.get({password: true, unmediated: true })
+          .then(m => { resolve("success"); })
+          .catch(()=> { resolve("error"); });
+        });
+    )");
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        ::features::kBackForwardCache,
+        {{"TimeToLiveInBackForwardCacheInSeconds", "3600"}});
+    PasswordManagerBrowserTest::SetUpCommandLine(command_line);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 class MockHttpAuthObserver : public password_manager::HttpAuthObserver {
  public:
   MOCK_METHOD2(OnAutofillDataAvailable,
@@ -3924,5 +3960,68 @@
   EXPECT_EQ("confirmation_password_element", cofirmation_password_annotation);
 }
 
+IN_PROC_BROWSER_TEST_F(PasswordManagerBackForwardCacheBrowserTest,
+                       SavePasswordOnRestoredPage) {
+  // Navigate to a page with a password form.
+  NavigateToFile("/password/password_form.html");
+  content::RenderFrameHost* rfh = WebContents()->GetMainFrame();
+  content::RenderFrameDeletedObserver rfh_deleted_observer(rfh);
+
+  // Navigate away so that the password form page is stored in the cache.
+  EXPECT_TRUE(NavigateToURL(
+      WebContents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
+  EXPECT_FALSE(rfh_deleted_observer.deleted());
+  EXPECT_TRUE(content::IsInBackForwardCache(rfh));
+
+  // Restore the cached page.
+  WebContents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(WebContents()));
+  EXPECT_EQ(rfh, WebContents()->GetMainFrame());
+
+  // Fill out and submit the password form.
+  NavigationObserver observer(WebContents());
+  std::string fill_and_submit =
+      "document.getElementById('username_field').value = 'temp';"
+      "document.getElementById('password_field').value = 'random';"
+      "document.getElementById('input_submit_button').click()";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_submit));
+  observer.Wait();
+
+  // Save the password and check the store.
+  BubbleObserver bubble_observer(WebContents());
+  EXPECT_TRUE(bubble_observer.IsSavePromptShownAutomatically());
+  bubble_observer.AcceptSavePrompt();
+  WaitForPasswordStore();
+
+  CheckThatCredentialsStored("temp", "random");
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerBackForwardCacheBrowserTest,
+                       CallAPIOnRestoredPage) {
+  // Navigate to a page with a password form.
+  NavigateToFile("/password/password_form.html");
+  content::RenderFrameHost* rfh = WebContents()->GetMainFrame();
+  content::RenderFrameDeletedObserver rfh_deleted_observer(rfh);
+
+  // Make sure the password manager API works.
+  EXPECT_TRUE(IsGetCredentialsSuccessful());
+
+  // Navigate away so that the password form page is stored in the cache.
+  EXPECT_TRUE(NavigateToURL(
+      WebContents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
+  EXPECT_FALSE(rfh_deleted_observer.deleted());
+  EXPECT_TRUE(content::IsInBackForwardCache(rfh));
+
+  // Restore the cached page.
+  WebContents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(WebContents()));
+  EXPECT_EQ(rfh, WebContents()->GetMainFrame());
+
+  // Make sure the password manager API still works now that the page has been
+  // stored/restored.
+  // TODO(https://crbug.com/1012707): This is currently broken.
+  EXPECT_FALSE(IsGetCredentialsSuccessful());
+}
+
 }  // namespace
 }  // namespace password_manager
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
index 7ce95616..f0d5739 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/permissions/permission_features.h"
 #include "chrome/browser/permissions/permission_prompt_android.h"
 #include "chrome/browser/permissions/permission_request.h"
+#include "chrome/browser/permissions/permission_uma_util.h"
 #include "chrome/browser/permissions/permission_util.h"
 #include "chrome/browser/ui/android/infobars/grouped_permission_infobar.h"
 #include "chrome/grit/generated_resources.h"
@@ -18,7 +19,9 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/strings/grit/ui_strings.h"
 
-GroupedPermissionInfoBarDelegate::~GroupedPermissionInfoBarDelegate() {}
+GroupedPermissionInfoBarDelegate::~GroupedPermissionInfoBarDelegate() {
+  PermissionUmaUtil::RecordInfobarDetailsExpanded(details_expanded_);
+}
 
 // static
 infobars::InfoBar* GroupedPermissionInfoBarDelegate::Create(
@@ -81,6 +84,7 @@
 
 bool GroupedPermissionInfoBarDelegate::LinkClicked(
     WindowOpenDisposition disposition) {
+  details_expanded_ = true;
   return false;
 }
 
@@ -95,7 +99,9 @@
 GroupedPermissionInfoBarDelegate::GroupedPermissionInfoBarDelegate(
     const base::WeakPtr<PermissionPromptAndroid>& permission_prompt,
     InfoBarService* infobar_service)
-    : permission_prompt_(permission_prompt), infobar_service_(infobar_service) {
+    : permission_prompt_(permission_prompt),
+      infobar_service_(infobar_service),
+      details_expanded_(false) {
   DCHECK(permission_prompt_);
   DCHECK(infobar_service_);
 
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
index 546fbed40..38b568bc 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
@@ -68,6 +68,7 @@
 
   base::WeakPtr<PermissionPromptAndroid> permission_prompt_;
   InfoBarService* infobar_service_;
+  bool details_expanded_;
 
   DISALLOW_COPY_AND_ASSIGN(GroupedPermissionInfoBarDelegate);
 };
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index 50768c0..1412703 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -350,6 +350,11 @@
 }
 #endif
 
+void PermissionUmaUtil::RecordInfobarDetailsExpanded(bool expanded) {
+  base::UmaHistogramBoolean("Permissions.Prompt.Infobar.DetailsExpanded",
+                            expanded);
+}
+
 void PermissionUmaUtil::RecordPermissionAction(
     ContentSettingsType permission,
     PermissionAction action,
diff --git a/chrome/browser/permissions/permission_uma_util.h b/chrome/browser/permissions/permission_uma_util.h
index f0e58a93..825bbfb 100644
--- a/chrome/browser/permissions/permission_uma_util.h
+++ b/chrome/browser/permissions/permission_uma_util.h
@@ -110,6 +110,8 @@
 
   static void RecordWithBatteryBucket(const std::string& histogram);
 
+  static void RecordInfobarDetailsExpanded(bool expanded);
+
  private:
   friend class PermissionUmaUtilTest;
 
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 08948cf..800135310 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -364,12 +364,10 @@
     "$root_gen_dir/chrome/bluetooth_internals_resources.pak",
     "$root_gen_dir/chrome/omnibox_resources.pak",
     "$root_gen_dir/chrome/usb_internals_resources.pak",
-    "$root_gen_dir/components/sync_driver_resources.pak",
   ]
   deps = [
     "//chrome/browser/resources/bluetooth_internals:resources",
     "//chrome/browser/resources/omnibox:resources",
     "//chrome/browser/resources/usb_internals:resources",
-    "//components/sync/driver:resources",
   ]
 }
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.css b/chrome/browser/resources/chromeos/login/encryption_migration.css
index a98fd65..8b57fc8 100644
--- a/chrome/browser/resources/chromeos/login/encryption_migration.css
+++ b/chrome/browser/resources/chromeos/login/encryption_migration.css
@@ -2,15 +2,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-oobe-dialog {
-  height: 640px;
-  /* Subtracting LoginShelfView's height. */
-  max-height: calc(100vh - var(--shelf-area-height));
-  max-width: 100vw;
-  min-height: 0 !important;  /* Making <oobe-dialog> shrinkable. */
-  min-width: 0 !important;  /* Making <oobe-dialog> shrinkable. */
-  width: 768px;
-}
 
 oobe-dialog div {
   line-height: 1.54;  /* 20px height for 13px font. */
@@ -44,10 +35,6 @@
   background-color: var(--google-red-500);
 }
 
-[slot='footer'] {
-  overflow: hidden;
-}
-
 [slot='footer'] div {
   color: rgb(51, 51, 51);
   font-size: 13px;
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.html b/chrome/browser/resources/chromeos/login/encryption_migration.html
index f5b2b59..3fe8b90 100644
--- a/chrome/browser/resources/chromeos/login/encryption_migration.html
+++ b/chrome/browser/resources/chromeos/login/encryption_migration.html
@@ -31,7 +31,8 @@
         <template is="dom-if" if="[[isEnoughBattery]]">
           <div slot="footer" class="layout vertical center">
             <img srcset="images/security_update_1x.png 1x,
-                         images/security_update_2x.png 2x">
+                         images/security_update_2x.png 2x"
+                 class="oobe-illustration">
           </div>
         </template>
         <template is="dom-if" if="[[!isEnoughBattery]]">
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
index 31aa325f..ee5ac8ef 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
@@ -6,6 +6,7 @@
   'use strict';
 
   function initialize() {
+    cr.addWebUIListener('clear-focus', clearFocus);
     const syncConfirmationBrowserProxy =
         sync.confirmation.SyncConfirmationBrowserProxyImpl.getInstance();
     // Prefer using |document.body.offsetHeight| instead of
@@ -24,7 +25,6 @@
   }
 
   return {
-    clearFocus: clearFocus,
     initialize: initialize,
   };
 });
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation.js b/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation.js
index 12307c57..97f494a 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation.js
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_disabled_confirmation.js
@@ -43,6 +43,7 @@
   }
 
   function initialize() {
+    cr.addWebUIListener('clear-focus', clearFocus);
     document.addEventListener('keydown', onKeyDown);
     $('confirmButton').addEventListener('click', onConfirm);
     $('undoButton').addEventListener('click', onUndo);
@@ -67,11 +68,7 @@
     }
   }
 
-  // TODO(tangltom): clearFocus is called directly by the C++ handler.
-  // C++ handlers should not be calling JS functions by name anymore. They
-  // should be firing events with FireWebuiListener and have the page itself
-  // decide whether to listen or not listen to the event.
-  return {clearFocus: clearFocus, initialize: initialize};
+  return {initialize: initialize};
 });
 
 document.addEventListener('DOMContentLoaded', sync.confirmation.initialize);
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index 312cbede..f2213276 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <numeric>
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
@@ -14,15 +15,20 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
 #include "chrome/browser/policy/browser_dm_token_storage.h"
 #include "chrome/browser/policy/machine_level_user_cloud_policy_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/download_protection/check_client_download_request.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/policy/core/browser/url_blacklist_manager.h"
+#include "components/policy/core/browser/url_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/web_contents.h"
 #include "crypto/sha2.h"
+#include "net/base/mime_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_types.h"
 
@@ -192,7 +198,8 @@
     DataCallback callback,
     FileContents file_contents) {
   if (delegate_)
-    delegate_->SetSha256ForFile(path_, std::move(file_contents.sha256));
+    delegate_->SetFileInfo(path_, std::move(file_contents.sha256),
+                           file_contents.data.contents.length());
 
   std::move(callback).Run(file_contents.result, file_contents.data);
 }
@@ -205,6 +212,10 @@
 DeepScanningDialogDelegate::Result::Result(Result&& other) = default;
 DeepScanningDialogDelegate::Result::~Result() = default;
 
+DeepScanningDialogDelegate::FileInfo::FileInfo() = default;
+DeepScanningDialogDelegate::FileInfo::FileInfo(FileInfo&& other) = default;
+DeepScanningDialogDelegate::FileInfo::~FileInfo() = default;
+
 DeepScanningDialogDelegate::~DeepScanningDialogDelegate() = default;
 
 base::string16 DeepScanningDialogDelegate::GetTitle() {
@@ -253,13 +264,11 @@
   if (data->do_dlp_scan &&
       g_browser_process->local_state()->HasPrefPath(
           prefs::kDomainsToNotCheckComplianceOfUploadedContent)) {
-    const base::ListValue* domains = g_browser_process->local_state()->GetList(
+    const base::ListValue* filters = g_browser_process->local_state()->GetList(
         prefs::kDomainsToNotCheckComplianceOfUploadedContent);
-    data->do_dlp_scan = std::none_of(
-        domains->GetList().begin(), domains->GetList().end(),
-        [&](const base::Value& domain) {
-          return domain.is_string() && domain.GetString() == url.host();
-        });
+    url_matcher::URLMatcher matcher;
+    policy::url_util::AddAllowFilters(&matcher, filters);
+    data->do_dlp_scan = matcher.MatchURL(url).empty();
   }
 
   // See if malware checks are needed.
@@ -272,14 +281,12 @@
   if (data->do_malware_scan) {
     if (g_browser_process->local_state()->HasPrefPath(
             prefs::kDomainsToCheckForMalwareOfUploadedContent)) {
-      const base::ListValue* domains =
+      const base::ListValue* filters =
           g_browser_process->local_state()->GetList(
               prefs::kDomainsToCheckForMalwareOfUploadedContent);
-      data->do_malware_scan = std::any_of(
-          domains->GetList().begin(), domains->GetList().end(),
-          [&](const base::Value& domain) {
-            return domain.is_string() && domain.GetString() == url.host();
-          });
+      url_matcher::URLMatcher matcher;
+      policy::url_util::AddAllowFilters(&matcher, filters);
+      data->do_malware_scan = !matcher.MatchURL(url).empty();
     } else {
       data->do_malware_scan = false;
     }
@@ -357,16 +364,22 @@
   DCHECK(web_contents_);
   result_.text_results.resize(data_.text.size(), false);
   result_.paths_results.resize(data_.paths.size(), false);
-  sha256_.resize(data_.paths.size());
+  file_info_.resize(data_.paths.size());
 }
 
 void DeepScanningDialogDelegate::StringRequestCallback(
     BinaryUploadService::Result result,
     DeepScanningClientResponse response) {
-  MaybeReportDownloadDeepScanningVerdict(
+  MaybeReportDeepScanningVerdict(
       Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
-      web_contents_->GetLastCommittedURL(), "Text data", std::string(), result,
-      response);
+      web_contents_->GetLastCommittedURL(), "Text data", std::string(),
+      "text/plain",
+      extensions::SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload,
+      std::accumulate(data_.text.begin(), data_.text.end(), 0,
+                      [](int64_t acc, const base::string16& s) {
+                        return acc + s.size() * sizeof(base::char16);
+                      }),
+      result, response);
 
   text_request_complete_ = true;
   bool text_complies = (result == BinaryUploadService::Result::SUCCESS &&
@@ -385,10 +398,17 @@
   DCHECK(it != data_.paths.end());
   size_t index = std::distance(data_.paths.begin(), it);
 
-  MaybeReportDownloadDeepScanningVerdict(
+  // TODO(crbug.com/1013252): Obtain a more accurate MimeType by parsing the
+  // file content.
+  std::string mime_type;
+  net::GetMimeTypeFromFile(path, &mime_type);
+
+  MaybeReportDeepScanningVerdict(
       Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
-      web_contents_->GetLastCommittedURL(), path.AsUTF8Unsafe(), sha256_[index],
-      result, response);
+      web_contents_->GetLastCommittedURL(), path.AsUTF8Unsafe(),
+      file_info_[index].sha256, mime_type,
+      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
+      file_info_[index].size, result, response);
 
   bool dlp_ok = DlpTriggeredRulesOK(response.dlp_scan_verdict());
   bool malware_ok = response.malware_scan_verdict().verdict() !=
@@ -534,12 +554,14 @@
     std::move(callback_).Run(data_, result_);
 }
 
-void DeepScanningDialogDelegate::SetSha256ForFile(const base::FilePath& path,
-                                                  std::string sha256) {
+void DeepScanningDialogDelegate::SetFileInfo(const base::FilePath& path,
+                                             std::string sha256,
+                                             int64_t size) {
   auto it = std::find(data_.paths.begin(), data_.paths.end(), path);
   DCHECK(it != data_.paths.end());
   size_t index = std::distance(data_.paths.begin(), it);
-  sha256_[index] = std::move(sha256);
+  file_info_[index].sha256 = std::move(sha256);
+  file_info_[index].size = size;
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index 3be3dbb..49775a2 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -95,6 +95,19 @@
     std::vector<bool> paths_results;
   };
 
+  // File information used as an input to event report functions.
+  struct FileInfo {
+    FileInfo();
+    FileInfo(FileInfo&& other);
+    ~FileInfo();
+
+    // SHA256 hash for the given file.
+    std::string sha256;
+
+    // File size in bytes. -1 represents an unknown size.
+    uint64_t size;
+  };
+
   // Callback used with ShowForWebContents() that informs caller of verdict
   // of deep scans.
   using CompletionCallback =
@@ -204,18 +217,20 @@
   // |callback_| is cleared after being run.
   void RunCallback();
 
-  // Sets the SHA256 hash for the given file.
-  void SetSha256ForFile(const base::FilePath& path, std::string sha256);
+  // Sets the FileInfo the given file.
+  void SetFileInfo(const base::FilePath& path,
+                   std::string sha256,
+                   int64_t size);
 
   // The web contents that is attempting to access the data.
   content::WebContents* web_contents_ = nullptr;
 
   // Description of the data being scanned and the results of the scan.
-  // The elements of the vector |sha256_| hold the SHA256 hash of the file at
-  // same index in |data_.paths|.
+  // The elements of the vector |file_info_| hold the FileInfo of the file at
+  // the same index in |data_.paths|.
   const Data data_;
   Result result_;
-  std::vector<std::string> sha256_;
+  std::vector<FileInfo> file_info_;
 
   // Set to true once the scan of text has completed.  If the scan request has
   // no text requiring deep scanning, this is set to true immediately.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
index f2136e4..173aeed 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
@@ -30,6 +30,14 @@
 const char kDmToken[] = "dm_token";
 const char kTestUrl[] = "http://example.com";
 
+const char kTestHttpsSchemePatternUrl[] = "https://*";
+const char kTestChromeSchemePatternUrl[] = "chrome://*";
+const char kTestDevtoolsSchemePatternUrl[] = "devtools://*";
+
+const char kTestPathPatternUrl[] = "*/a/specific/path/";
+const char kTestPortPatternUrl[] = "*:1234";
+const char kTestQueryPatternUrl[] = "*?q=5678";
+
 class BaseTest : public testing::Test {
  public:
   BaseTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {
@@ -68,7 +76,13 @@
     updater->GetList().emplace_back(url.host());
   }
 
-  TestingProfile* profile() { return profile_; }
+  void AddUrlToList(const char* pref_name, const char* url) {
+    ListPrefUpdate updater(TestingBrowserProcess::GetGlobal()->local_state(),
+                           pref_name);
+    updater->GetList().emplace_back(url);
+  }
+
+  Profile* profile() { return profile_; }
 
  private:
   content::BrowserTaskEnvironment task_environment_;
@@ -163,6 +177,92 @@
   EXPECT_FALSE(data.do_malware_scan);
 }
 
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, DlpDisabledByListWithPatterns) {
+  EnableFeatures({kDeepScanningOfUploads});
+  SetDMToken(kDmToken);
+  SetDlpPolicy(CHECK_UPLOADS);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent, kTestUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestHttpsSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestChromeSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestDevtoolsSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestHttpsSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestPathPatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestPortPatternUrl);
+  AddUrlToList(prefs::kDomainsToNotCheckComplianceOfUploadedContent,
+               kTestQueryPatternUrl);
+
+  DeepScanningDialogDelegate::Data data;
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://example.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("https://google.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://google.com"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("chrome://version/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://version"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("devtools://devtools/bundled/inspector.html"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://devtools/bundled/inspector.html"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com/a/specific/path/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com/not/a/specific/path/"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com:1234"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com:4321"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com?q=5678"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com?q=8765"), &data));
+  EXPECT_TRUE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
 TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareNoPref) {
   EnableFeatures({kDeepScanningOfUploads});
   SetDMToken(kDmToken);
@@ -246,6 +346,90 @@
       profile()->GetOffTheRecordProfile(), url, &data));
 }
 
+TEST_F(DeepScanningDialogDelegateIsEnabledTest, MalwareEnabledWithPatterns) {
+  EnableFeatures({kDeepScanningOfUploads});
+  SetDMToken(kDmToken);
+  SetMalwarePolicy(SEND_UPLOADS_AND_DOWNLOADS);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent, kTestUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestHttpsSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestChromeSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestDevtoolsSchemePatternUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestPathPatternUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestPortPatternUrl);
+  AddUrlToList(prefs::kDomainsToCheckForMalwareOfUploadedContent,
+               kTestQueryPatternUrl);
+
+  DeepScanningDialogDelegate::Data data;
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://example.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("chrome://version/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://version/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("devtools://devtools/bundled/inspector.html"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://devtools/bundled/inspector.html"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("https://google.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("custom://google.com"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com/a/specific/path/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com/not/a/specific/path/"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com:1234"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com:4321"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+
+  EXPECT_TRUE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com?q=5678"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_TRUE(data.do_malware_scan);
+  EXPECT_FALSE(DeepScanningDialogDelegate::IsEnabled(
+      profile(), GURL("http://google.com?q=8765"), &data));
+  EXPECT_FALSE(data.do_dlp_scan);
+  EXPECT_FALSE(data.do_malware_scan);
+}
+
 class DeepScanningDialogDelegateAuditOnlyTest : public BaseTest {
  protected:
   void RunUntilDone() { run_loop_.Run(); }
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 8093791..2fb8722c 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
+#include "components/policy/core/browser/url_util.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
@@ -34,6 +35,7 @@
 #include "components/safe_browsing/features.h"
 #include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
 
@@ -88,16 +90,19 @@
 
 }  // namespace
 
-void MaybeReportDownloadDeepScanningVerdict(
-    Profile* profile,
-    const GURL& url,
-    const std::string& file_name,
-    const std::string& download_digest_sha256,
-    BinaryUploadService::Result result,
-    DeepScanningClientResponse response) {
+void MaybeReportDeepScanningVerdict(Profile* profile,
+                                    const GURL& url,
+                                    const std::string& file_name,
+                                    const std::string& download_digest_sha256,
+                                    const std::string& mime_type,
+                                    const std::string& trigger,
+                                    const int64_t content_size,
+                                    BinaryUploadService::Result result,
+                                    DeepScanningClientResponse response) {
   if (result == BinaryUploadService::Result::FILE_TOO_LARGE) {
     extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
-        ->OnLargeUnscannedFileEvent(url, file_name, download_digest_sha256);
+        ->OnLargeUnscannedFileEvent(url, file_name, download_digest_sha256,
+                                    mime_type, trigger, content_size);
   }
 
   if (result != BinaryUploadService::Result::SUCCESS)
@@ -111,14 +116,16 @@
         ->OnDangerousDeepScanningResult(
             url, file_name, download_digest_sha256,
             MalwareVerdictToThreatType(
-                response.malware_scan_verdict().verdict()));
+                response.malware_scan_verdict().verdict()),
+            mime_type, trigger, content_size);
   }
 
   if (response.dlp_scan_verdict().status() == DlpDeepScanningVerdict::SUCCESS) {
     if (!response.dlp_scan_verdict().triggered_rules().empty()) {
       extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
           ->OnSensitiveDataEvent(response.dlp_scan_verdict(), url, file_name,
-                                 download_digest_sha256);
+                                 download_digest_sha256, mime_type, trigger,
+                                 content_size);
     }
   }
 }
@@ -359,14 +366,9 @@
 
   const base::ListValue* domains = g_browser_process->local_state()->GetList(
       prefs::kDomainsToCheckComplianceOfDownloadedContent);
-  bool host_in_list =
-      std::any_of(domains->GetList().begin(), domains->GetList().end(),
-                  [this](const base::Value& domain) {
-                    return domain.is_string() &&
-                           domain.GetString() == item_->GetURL().host();
-                  });
-
-  return host_in_list;
+  url_matcher::URLMatcher matcher;
+  policy::url_util::AddAllowFilters(&matcher, domains);
+  return !matcher.MatchURL(item_->GetURL()).empty();
 }
 
 bool CheckClientDownloadRequest::ShouldUploadForMalwareScan(
@@ -412,10 +414,12 @@
   Profile* profile = Profile::FromBrowserContext(GetBrowserContext());
   if (profile) {
     std::string raw_digest_sha256 = item_->GetHash();
-    MaybeReportDownloadDeepScanningVerdict(
+    MaybeReportDeepScanningVerdict(
         profile, item_->GetURL(), item_->GetTargetFilePath().AsUTF8Unsafe(),
         base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
-        result, response);
+        item_->GetMimeType(),
+        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileDownload,
+        item_->GetTotalBytes(), result, response);
   }
 
   if (!ShouldDelayVerdicts())
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
index 33d1747..49a9e16 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -102,13 +102,15 @@
 
 // Helper function to examine a DeepScanningClientResponse and report the
 // appropriate events to the enterprise admin.
-void MaybeReportDownloadDeepScanningVerdict(
-    Profile* profile,
-    const GURL& url,
-    const std::string& file_name,
-    const std::string& download_digest_sha256,
-    BinaryUploadService::Result result,
-    DeepScanningClientResponse response);
+void MaybeReportDeepScanningVerdict(Profile* profile,
+                                    const GURL& url,
+                                    const std::string& file_name,
+                                    const std::string& download_digest_sha256,
+                                    const std::string& mime_type,
+                                    const std::string& trigger,
+                                    const int64_t content_size,
+                                    BinaryUploadService::Result result,
+                                    DeepScanningClientResponse response);
 
 }  // namespace safe_browsing
 
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 0f485b6..ccfa210 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -525,7 +525,8 @@
   extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
       ->OnDangerousDownloadOpened(
           item->GetURL(), item->GetTargetFilePath().AsUTF8Unsafe(),
-          base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()));
+          base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
+          item->GetMimeType(), item->GetTotalBytes());
 }
 
 bool DownloadProtectionService::MaybeBeginFeedbackForDownload(
diff --git a/chrome/browser/safe_browsing/download_protection/download_reporter.cc b/chrome/browser/safe_browsing/download_protection/download_reporter.cc
index e59ced2..356314c 100644
--- a/chrome/browser/safe_browsing/download_protection/download_reporter.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_reporter.cc
@@ -63,7 +63,8 @@
         ->OnDangerousDownloadWarning(
             download->GetURL(), download->GetTargetFilePath().AsUTF8Unsafe(),
             base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
-            DangerTypeToThreatType(download->GetDangerType()));
+            DangerTypeToThreatType(download->GetDangerType()),
+            download->GetMimeType(), download->GetTotalBytes());
   }
 }
 
@@ -79,7 +80,8 @@
         ->OnDangerousDownloadWarningBypassed(
             download->GetURL(), download->GetTargetFilePath().AsUTF8Unsafe(),
             base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
-            DangerTypeToThreatType(original_danger_type));
+            DangerTypeToThreatType(original_danger_type),
+            download->GetMimeType(), download->GetTotalBytes());
   }
 }
 
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.cc b/chrome/browser/supervised_user/supervised_user_url_filter.cc
index 3929ccc8..27a0a6f 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.cc
@@ -43,10 +43,11 @@
 #endif
 
 using content::BrowserThread;
-using net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES;
 using net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES;
+using net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES;
 using net::registry_controlled_domains::GetCanonicalHostRegistryLength;
-using policy::URLBlacklist;
+using policy::url_util::CreateConditionSet;
+using policy::url_util::FilterToComponents;
 using url_matcher::URLMatcher;
 using url_matcher::URLMatcherConditionSet;
 
@@ -148,17 +149,15 @@
   std::string path;
   std::string query;
   bool match_subdomains = true;
-  if (!URLBlacklist::FilterToComponents(pattern, &scheme, &host,
-                                        &match_subdomains, &port, &path,
-                                        &query)) {
+  if (!FilterToComponents(pattern, &scheme, &host, &match_subdomains, &port,
+                          &path, &query)) {
     LOG(ERROR) << "Invalid pattern " << pattern;
     return -1;
   }
 
   scoped_refptr<URLMatcherConditionSet> condition_set =
-      URLBlacklist::CreateConditionSet(
-          &contents_->url_matcher, ++matcher_id_,
-          scheme, host, match_subdomains, port, path, query, true);
+      CreateConditionSet(&contents_->url_matcher, ++matcher_id_, scheme, host,
+                         match_subdomains, port, path, query, true);
   all_conditions_.push_back(std::move(condition_set));
   return matcher_id_;
 }
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index c265e10..abe9d89 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -1002,14 +1002,8 @@
 
 // TODO(crbug.com/1012223): re-enable this test on all builders once flakiness
 // is addressed.
-#if defined(THREAD_SANITIZER)
-#define MAYBE_ApplyRemoteUpdateWithValidGUID \
-  DISABLED_ApplyRemoteUpdateWithValidGUID
-#else
-#define MAYBE_ApplyRemoteUpdateWithValidGUID ApplyRemoteUpdateWithValidGUID
-#endif
 IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
-                       MAYBE_ApplyRemoteUpdateWithValidGUID) {
+                       DISABLED_ApplyRemoteUpdateWithValidGUID) {
   // This test is only relevant for USS code path and when BookmarkNode GUID
   // replacement is enabled.
   if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f89dbfe8..09230df4 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -513,7 +513,6 @@
     "//components/subresource_filter/content/browser",
     "//components/subresource_filter/core/browser",
     "//components/sync",
-    "//components/sync/driver:resources",
     "//components/sync_preferences",
     "//components/sync_sessions",
     "//components/tracing:startup_tracing",
@@ -2298,6 +2297,8 @@
       "cocoa/touchbar/web_textfield_touch_bar_controller.mm",
       "cocoa/window_size_autosaver.h",
       "cocoa/window_size_autosaver.mm",
+      "content_settings/media_authorization_wrapper_test.h",
+      "content_settings/media_authorization_wrapper_test.mm",
       "views/apps/chrome_app_window_client_views_mac.mm",
       "views/certificate_viewer_mac_views.mm",
       "views/dropdown_bar_host_mac.mm",
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 4639f10..ed9227f 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -1272,10 +1272,10 @@
       base::UserMetricsAction("Media.ShowSystemMediaPermissionBubble"));
   int title_id = 0;
   if (MicrophoneAccessed() && CameraAccessed() &&
-      system_media_permissions::CheckSystemVideoCapturePermission() ==
-          system_media_permissions::SystemPermission::kDenied &&
-      system_media_permissions::CheckSystemAudioCapturePermission() ==
-          system_media_permissions::SystemPermission::kDenied) {
+      (system_media_permissions::CheckSystemVideoCapturePermission() ==
+           system_media_permissions::SystemPermission::kDenied ||
+       system_media_permissions::CheckSystemAudioCapturePermission() ==
+           system_media_permissions::SystemPermission::kDenied)) {
     title_id = IDS_CAMERA_MIC_TURNED_OFF_IN_MACOS;
     AddListItem(ContentSettingBubbleModel::ListItem(
         &vector_icons::kVideocamIcon, l10n_util::GetStringUTF16(IDS_CAMERA),
@@ -1314,6 +1314,8 @@
            (system_media_permissions::CheckSystemAudioCapturePermission() ==
                 system_media_permissions::SystemPermission::kDenied &&
             MicrophoneAccessed() && !MicrophoneBlocked())) &&
+          !(CameraAccessed() && CameraBlocked()) &&
+          !(MicrophoneAccessed() && MicrophoneBlocked()) &&
           base::FeatureList::IsEnabled(
               ::features::kMacSystemMediaPermissionsInfoUi));
 #else
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index 1b84b55..29fe7b2 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -143,6 +143,8 @@
 #if defined(OS_MACOSX)
   bool DidCameraAccessFailBecauseOfSystemLevelBlock();
   bool DidMicAccessFailBecauseOfSystemLevelBlock();
+  bool IsCameraAccessPendingOnSystemLevelPrompt();
+  bool IsMicAccessPendingOnSystemLevelPrompt();
 #endif  // defined(OS_MACOSX)
 
   std::unique_ptr<ContentSettingBubbleModel> CreateBubbleModelImpl(
@@ -572,21 +574,68 @@
 #if defined(OS_MACOSX)
   if (base::FeatureList::IsEnabled(
           ::features::kMacSystemMediaPermissionsInfoUi)) {
-    if (DidCameraAccessFailBecauseOfSystemLevelBlock() &&
-        DidMicAccessFailBecauseOfSystemLevelBlock()) {
-      set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
-      set_explanatory_string_id(IDS_CAMERA_BLOCKED_TITLE);
-      set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED));
+    // Don't show an icon when the user has not made a decision yet for
+    // the site level media permissions.
+    if (IsCameraAccessPendingOnSystemLevelPrompt() ||
+        IsMicAccessPendingOnSystemLevelPrompt()) {
+      return false;
+    }
+
+    set_explanatory_string_id(0);
+
+    if (IsCamAccessed() && IsMicAccessed()) {
+      if (IsCameraBlockedOnSiteLevel() || IsMicBlockedOnSiteLevel()) {
+        set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED));
+      } else if (DidCameraAccessFailBecauseOfSystemLevelBlock() ||
+                 DidMicAccessFailBecauseOfSystemLevelBlock()) {
+        set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED));
+        if (!content_settings->camera_was_just_granted_on_site_level() ||
+            !content_settings->mic_was_just_granted_on_site_level()) {
+          set_explanatory_string_id(IDS_CAMERA_TURNED_OFF);
+        }
+        // TODO(hkamila): Automatically trigger the new bubble, if the camera
+        // and/or mic was just granted on a site level, but blocked on a system
+        // level.
+      } else {
+        set_icon(vector_icons::kVideocamIcon, gfx::kNoneIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_ALLOWED));
+      }
       return true;
-    } else if (DidCameraAccessFailBecauseOfSystemLevelBlock()) {
-      set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
-      set_explanatory_string_id(IDS_CAMERA_BLOCKED_TITLE);
-      set_tooltip(l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED));
+    }
+
+    if (IsCamAccessed()) {
+      if (IsCameraBlockedOnSiteLevel()) {
+        set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED));
+      } else if (DidCameraAccessFailBecauseOfSystemLevelBlock()) {
+        set_icon(vector_icons::kVideocamIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED));
+        if (!content_settings->camera_was_just_granted_on_site_level()) {
+          set_explanatory_string_id(IDS_CAMERA_TURNED_OFF);
+        }
+      } else {
+        set_icon(vector_icons::kVideocamIcon, gfx::kNoneIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_CAMERA_ACCESSED));
+      }
       return true;
-    } else if (DidMicAccessFailBecauseOfSystemLevelBlock()) {
-      set_icon(vector_icons::kMicIcon, kBlockedBadgeIcon);
-      set_explanatory_string_id(IDS_MICROPHONE_BLOCKED_TITLE);
-      set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED));
+    }
+
+    if (IsMicAccessed()) {
+      if (IsMicBlockedOnSiteLevel()) {
+        set_icon(vector_icons::kMicIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED));
+      } else if (DidMicAccessFailBecauseOfSystemLevelBlock()) {
+        set_icon(vector_icons::kMicIcon, kBlockedBadgeIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED));
+        if (!content_settings->mic_was_just_granted_on_site_level()) {
+          set_explanatory_string_id(IDS_MIC_TURNED_OFF);
+        }
+      } else {
+        set_icon(vector_icons::kMicIcon, gfx::kNoneIcon);
+        set_tooltip(l10n_util::GetStringUTF16(IDS_MICROPHONE_ACCESSED));
+      }
       return true;
     }
   }
@@ -642,6 +691,19 @@
           system_media_permissions::CheckSystemAudioCapturePermission() ==
               system_media_permissions::SystemPermission::kDenied);
 }
+
+bool ContentSettingMediaImageModel::IsCameraAccessPendingOnSystemLevelPrompt() {
+  return (system_media_permissions::CheckSystemVideoCapturePermission() ==
+              system_media_permissions::SystemPermission::kNotDetermined &&
+          IsCamAccessed() && !IsCameraBlockedOnSiteLevel());
+}
+
+bool ContentSettingMediaImageModel::IsMicAccessPendingOnSystemLevelPrompt() {
+  return (system_media_permissions::CheckSystemAudioCapturePermission() ==
+              system_media_permissions::SystemPermission::kNotDetermined &&
+          IsMicAccessed() && !IsMicBlockedOnSiteLevel());
+}
+
 #endif  // defined(OS_MACOSX)
 
 std::unique_ptr<ContentSettingBubbleModel>
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.h b/chrome/browser/ui/content_settings/content_setting_image_model.h
index 43ce88e..bbcf242 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.h
@@ -92,9 +92,15 @@
   // we don't wish to show anything.
   int explanatory_string_id() const { return explanatory_string_id_; }
   const base::string16& get_tooltip() const { return tooltip_; }
+  const gfx::VectorIcon* get_icon_badge() const { return icon_badge_; }
 
   ImageType image_type() const { return image_type_; }
 
+  // Public for testing.
+  void set_explanatory_string_id(int text_id) {
+    explanatory_string_id_ = text_id;
+  }
+
  protected:
   explicit ContentSettingImageModel(ImageType type);
 
@@ -113,9 +119,6 @@
     icon_badge_ = &badge;
   }
 
-  void set_explanatory_string_id(int text_id) {
-    explanatory_string_id_ = text_id;
-  }
   void set_tooltip(const base::string16& tooltip) { tooltip_ = tooltip; }
 
  private:
diff --git a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
new file mode 100644
index 0000000..b55023c
--- /dev/null
+++ b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
@@ -0,0 +1,218 @@
+// 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/ui/content_settings/content_setting_image_model.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+#include "base/mac/mac_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/content_settings/media_authorization_wrapper_test.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icon_types.h"
+
+namespace gfx {
+struct VectorIcon;
+}
+
+namespace {
+
+bool HasIcon(const ContentSettingImageModel& model) {
+  return !model.GetIcon(gfx::kPlaceholderColor).IsEmpty();
+}
+
+void ExpectImageModelState(const ContentSettingImageModel& model,
+                           bool is_visible,
+                           bool has_icon,
+                           const base::string16& tooltip,
+                           int explanatory_string_id,
+                           const gfx::VectorIcon* icon_badge) {
+  EXPECT_EQ(model.is_visible(), is_visible);
+  EXPECT_EQ(HasIcon(model), has_icon);
+  EXPECT_EQ(model.get_tooltip(), tooltip);
+  EXPECT_EQ(model.explanatory_string_id(), explanatory_string_id);
+  EXPECT_EQ(model.get_icon_badge(), icon_badge);
+}
+
+class ContentSettingMediaImageModelTest
+    : public ChromeRenderViewHostTestHarness {
+ protected:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    TabSpecificContentSettings::CreateForWebContents(web_contents());
+    InfoBarService::CreateForWebContents(web_contents());
+  }
+
+  std::string GetDefaultAudioDevice() {
+    PrefService* prefs = profile()->GetPrefs();
+    return prefs->GetString(prefs::kDefaultAudioCaptureDevice);
+  }
+
+  std::string GetDefaultVideoDevice() {
+    PrefService* prefs = profile()->GetPrefs();
+    return prefs->GetString(prefs::kDefaultVideoCaptureDevice);
+  }
+};
+
+TEST_F(ContentSettingMediaImageModelTest, MediaUpdate) {
+  if (!base::mac::IsAtLeastOS10_14())
+    return;
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kMacSystemMediaPermissionsInfoUi);
+
+  TabSpecificContentSettings::CreateForWebContents(web_contents());
+  auto* content_settings =
+      TabSpecificContentSettings::FromWebContents(web_contents());
+  const GURL kTestOrigin("https://www.example.com");
+  auto content_setting_image_model =
+      ContentSettingImageModel::CreateForContentType(
+          ContentSettingImageModel::ImageType::MEDIASTREAM);
+  MediaAuthorizationWrapperTest auth_wrapper;
+  system_media_permissions::SetMediaAuthorizationWrapperForTesting(
+      &auth_wrapper);
+
+  // Camera allowed per site: Test for system level permissions.
+  {
+    content_settings->OnMediaStreamPermissionSet(
+        kTestOrigin, TabSpecificContentSettings::CAMERA_ACCESSED, std::string(),
+        GetDefaultVideoDevice(), std::string(), std::string());
+    auth_wrapper.SetMockMediaPermissionStatus(kAllowed);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(
+        *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+        l10n_util::GetStringUTF16(IDS_CAMERA_ACCESSED), 0, &gfx::kNoneIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kDenied);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(*content_setting_image_model, true /*is_visible*/,
+                          true /*has_icon*/,
+                          l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED),
+                          IDS_CAMERA_TURNED_OFF, &kBlockedBadgeIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kNotDetermined);
+    content_setting_image_model->Update(web_contents());
+    EXPECT_FALSE(content_setting_image_model->is_visible());
+  }
+
+  // Microphone allowed per site: Test for system level permissions.
+  {
+    content_settings->OnMediaStreamPermissionSet(
+        kTestOrigin, TabSpecificContentSettings::MICROPHONE_ACCESSED,
+        std::string(), GetDefaultVideoDevice(), std::string(), std::string());
+    auth_wrapper.SetMockMediaPermissionStatus(kAllowed);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(
+        *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+        l10n_util::GetStringUTF16(IDS_MICROPHONE_ACCESSED), 0, &gfx::kNoneIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kDenied);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(*content_setting_image_model, true /*is_visible*/,
+                          true /*has_icon*/,
+                          l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED),
+                          IDS_MIC_TURNED_OFF, &kBlockedBadgeIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kNotDetermined);
+    content_setting_image_model->Update(web_contents());
+    EXPECT_FALSE(content_setting_image_model->is_visible());
+  }
+
+  // Microphone & camera allowed per site: Test for system level permissions.
+  {
+    content_settings->OnMediaStreamPermissionSet(
+        kTestOrigin,
+        (TabSpecificContentSettings::MICROPHONE_ACCESSED |
+         TabSpecificContentSettings::CAMERA_ACCESSED),
+        std::string(), GetDefaultVideoDevice(), std::string(), std::string());
+    auth_wrapper.SetMockMediaPermissionStatus(kAllowed);
+    auth_wrapper.SetMockMediaPermissionStatus(kAllowed);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(
+        *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+        l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_ALLOWED), 0,
+        &gfx::kNoneIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kDenied);
+    auth_wrapper.SetMockMediaPermissionStatus(kDenied);
+    content_setting_image_model->Update(web_contents());
+    ExpectImageModelState(
+        *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+        l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED),
+        IDS_CAMERA_TURNED_OFF, &kBlockedBadgeIcon);
+    auth_wrapper.SetMockMediaPermissionStatus(kNotDetermined);
+    auth_wrapper.SetMockMediaPermissionStatus(kNotDetermined);
+    content_setting_image_model->Update(web_contents());
+    EXPECT_EQ(content_setting_image_model->is_visible(), false);
+  }
+
+  // Test that system permissions being allowed do not affect the image view,
+  // when the per site permission is denied.
+  for (const auto system_state : {kAllowed, kDenied}) {
+    SCOPED_TRACE(system_state);
+    auth_wrapper.SetMockMediaPermissionStatus(system_state);
+    auth_wrapper.SetMockMediaPermissionStatus(system_state);
+
+    // Camera blocked per site.
+    {
+      content_settings->OnMediaStreamPermissionSet(
+          kTestOrigin,
+          TabSpecificContentSettings::CAMERA_ACCESSED |
+              TabSpecificContentSettings::CAMERA_BLOCKED,
+          GetDefaultAudioDevice(), GetDefaultVideoDevice(), std::string(),
+          std::string());
+      content_setting_image_model->Update(web_contents());
+      ExpectImageModelState(
+          *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+          l10n_util::GetStringUTF16(IDS_CAMERA_BLOCKED), 0, &kBlockedBadgeIcon);
+    }
+
+    // Microphone blocked per site.
+    {
+      content_settings->OnMediaStreamPermissionSet(
+          kTestOrigin,
+          TabSpecificContentSettings::MICROPHONE_ACCESSED |
+              TabSpecificContentSettings::MICROPHONE_BLOCKED,
+          GetDefaultAudioDevice(), GetDefaultVideoDevice(), std::string(),
+          std::string());
+      content_setting_image_model->Update(web_contents());
+      ExpectImageModelState(*content_setting_image_model, true /*is_visible*/,
+                            true /*has_icon*/,
+                            l10n_util::GetStringUTF16(IDS_MICROPHONE_BLOCKED),
+                            0, &kBlockedBadgeIcon);
+    }
+
+    // Microphone & camera blocked per site
+    {
+      content_settings->OnMediaStreamPermissionSet(
+          kTestOrigin,
+          TabSpecificContentSettings::CAMERA_ACCESSED |
+              TabSpecificContentSettings::CAMERA_BLOCKED |
+              TabSpecificContentSettings::MICROPHONE_ACCESSED |
+              TabSpecificContentSettings::MICROPHONE_BLOCKED,
+          GetDefaultAudioDevice(), GetDefaultVideoDevice(), std::string(),
+          std::string());
+      content_setting_image_model->Update(web_contents());
+      ExpectImageModelState(
+          *content_setting_image_model, true /*is_visible*/, true /*has_icon*/,
+          l10n_util::GetStringUTF16(IDS_MICROPHONE_CAMERA_BLOCKED), 0,
+          &kBlockedBadgeIcon);
+    }
+  }
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h
new file mode 100644
index 0000000..8604458
--- /dev/null
+++ b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_CONTENT_SETTINGS_MEDIA_AUTHORIZATION_WRAPPER_TEST_H_
+#define CHROME_BROWSER_UI_CONTENT_SETTINGS_MEDIA_AUTHORIZATION_WRAPPER_TEST_H_
+
+#import <AVFoundation/AVFoundation.h>
+
+#include "base/callback.h"
+#include "base/task/task_traits.h"
+#include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
+
+enum AuthStatus {
+  kNotDetermined,
+  kRestricted,
+  kDenied,
+  kAllowed,
+};
+
+class MediaAuthorizationWrapperTest
+    : public system_media_permissions::MediaAuthorizationWrapper {
+ public:
+  MediaAuthorizationWrapperTest() = default;
+  ~MediaAuthorizationWrapperTest() final = default;
+  void SetMockMediaPermissionStatus(AuthStatus status);
+
+  // MediaAuthorizationWrapper:
+  NSInteger AuthorizationStatusForMediaType(NSString* media_type) override;
+  void RequestAccessForMediaType(NSString* media_type,
+                                 base::RepeatingClosure callback,
+                                 const base::TaskTraits& traits) final {}
+
+ private:
+  AuthStatus permission_status_ = kNotDetermined;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaAuthorizationWrapperTest);
+};
+
+#endif  // CHROME_BROWSER_UI_CONTENT_SETTINGS_MEDIA_AUTHORIZATION_WRAPPER_TEST_H_
diff --git a/chrome/browser/ui/content_settings/media_authorization_wrapper_test.mm b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.mm
new file mode 100644
index 0000000..eb97e1e
--- /dev/null
+++ b/chrome/browser/ui/content_settings/media_authorization_wrapper_test.mm
@@ -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/ui/content_settings/media_authorization_wrapper_test.h"
+
+void MediaAuthorizationWrapperTest::SetMockMediaPermissionStatus(
+    AuthStatus status) {
+  permission_status_ = status;
+}
+
+NSInteger MediaAuthorizationWrapperTest::AuthorizationStatusForMediaType(
+    NSString* media_type) {
+  return static_cast<NSInteger>(permission_status_);
+}
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 8d8fdf60..7480da4 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -327,9 +327,7 @@
     const GURL& url,
     security_state::SecurityLevel security_level,
     const security_state::VisibleSecurityState& visible_security_state)
-    : TabSpecificContentSettings::SiteDataObserver(
-          tab_specific_content_settings),
-      content::WebContentsObserver(web_contents),
+    : content::WebContentsObserver(web_contents),
       ui_(ui),
       show_info_bar_(false),
       site_url_(url),
@@ -341,6 +339,7 @@
       content_settings_(HostContentSettingsMapFactory::GetForProfile(profile)),
       chrome_ssl_host_state_delegate_(
           ChromeSSLHostStateDelegateFactory::GetForProfile(profile)),
+      tab_specific_content_settings_(tab_specific_content_settings),
       did_revoke_user_ssl_decisions_(false),
       profile_(profile),
       security_level_(security_state::NONE),
@@ -471,7 +470,7 @@
 
 void PageInfo::OnSitePermissionChanged(ContentSettingsType type,
                                        ContentSetting setting) {
-  tab_specific_content_settings()->ContentSettingChangedViaPageInfo(type);
+  tab_specific_content_settings_->ContentSettingChangedViaPageInfo(type);
 
   // Count how often a permission for a specific content type is changed using
   // the Page Info UI.
@@ -548,10 +547,6 @@
   PresentSitePermissions();
 }
 
-void PageInfo::OnSiteDataAccessed() {
-  PresentSiteData();
-}
-
 void PageInfo::OnUIClosing(bool* reload_prompt) {
   if (reload_prompt)
     *reload_prompt = false;
@@ -945,7 +940,7 @@
     }
 
     if (ShouldShowPermission(permission_info, site_url_, content_settings_,
-                             web_contents(), tab_specific_content_settings())) {
+                             web_contents(), tab_specific_content_settings_)) {
       permission_info_list.push_back(permission_info);
     }
   }
@@ -968,9 +963,9 @@
 void PageInfo::PresentSiteData() {
   CookieInfoList cookie_info_list;
   const LocalSharedObjectsContainer& allowed_objects =
-      tab_specific_content_settings()->allowed_local_shared_objects();
+      tab_specific_content_settings_->allowed_local_shared_objects();
   const LocalSharedObjectsContainer& blocked_objects =
-      tab_specific_content_settings()->blocked_local_shared_objects();
+      tab_specific_content_settings_->blocked_local_shared_objects();
 
   // Add first party cookie and site data counts.
   PageInfoUI::CookieInfo cookie_info;
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 25ca8058..65deb97f 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -46,8 +46,7 @@
 // information and allows users to change the permissions. |PageInfo|
 // objects must be created on the heap. They destroy themselves after the UI is
 // closed.
-class PageInfo : public TabSpecificContentSettings::SiteDataObserver,
-                 public content::WebContentsObserver {
+class PageInfo : public content::WebContentsObserver {
  public:
   // TODO(palmer): Figure out if it is possible to unify SiteConnectionStatus
   // and SiteIdentityStatus.
@@ -211,9 +210,6 @@
 
   const base::string16& organization_name() const { return organization_name_; }
 
-  // SiteDataObserver implementation.
-  void OnSiteDataAccessed() override;
-
  private:
   FRIEND_TEST_ALL_PREFIXES(PageInfoTest,
                            NonFactoryDefaultAndRecentlyChangedPermissionsShown);
@@ -323,6 +319,12 @@
   // decisions by users.
   ChromeSSLHostStateDelegate* chrome_ssl_host_state_delegate_;
 
+  // The TabSpecificContentSettings for this site, used to propagate changes
+  // from the UI back to the model. This is held as a raw pointer because the
+  // lifetime of TabSpecificContentSettings is tightly bound to that of the
+  // observed WebContents.
+  TabSpecificContentSettings* tab_specific_content_settings_;
+
   bool did_revoke_user_ssl_decisions_;
 
   Profile* profile_;
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 0581278..ec8391d 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -399,14 +399,6 @@
   EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
 }
 
-TEST_F(PageInfoTest, OnSiteDataAccessed) {
-  EXPECT_CALL(*mock_ui(), SetPermissionInfoStub());
-  EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
-  EXPECT_CALL(*mock_ui(), SetCookieInfo(_)).Times(2);
-
-  page_info()->OnSiteDataAccessed();
-}
-
 TEST_F(PageInfoTest, OnChosenObjectDeleted) {
   // Connect the UsbChooserContext with FakeUsbDeviceManager.
   device::FakeUsbDeviceManager usb_device_manager;
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
index 617bddd..44c39030 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
@@ -400,6 +400,8 @@
 }
 
 TEST_F(ManagePasswordsBubbleModelTest, SuppressSignInPromo) {
+  prefs()->SetBoolean(password_manager::prefs::kSignInPasswordPromoRevive,
+                      true);
   prefs()->SetBoolean(password_manager::prefs::kWasSignInPasswordPromoClicked,
                       true);
   PretendPasswordWaiting();
diff --git a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
index 164ef60..f8640d1 100644
--- a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
+++ b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
@@ -50,6 +50,10 @@
   DCHECK(dialog_controller_);
 
   DialogDelegate::set_draggable(true);
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_CHROME_CLEANUP_REBOOT_PROMPT_RESTART_BUTTON_LABEL));
 
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
@@ -92,18 +96,6 @@
 }
 
 // DialogDelegate overrides.
-
-base::string16 ChromeCleanerRebootDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  DCHECK(button == ui::DIALOG_BUTTON_OK || button == ui::DIALOG_BUTTON_CANCEL);
-  DCHECK(dialog_controller_);
-
-  return button == ui::DIALOG_BUTTON_OK
-             ? l10n_util::GetStringUTF16(
-                   IDS_CHROME_CLEANUP_REBOOT_PROMPT_RESTART_BUTTON_LABEL)
-             : DialogDelegate::GetDialogButtonLabel(button);
-}
-
 bool ChromeCleanerRebootDialog::Accept() {
   HandleDialogInteraction(DialogInteractionResult::kAccept);
   return true;
diff --git a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
index 3917b97..c190c7c 100644
--- a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
+++ b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
@@ -40,7 +40,6 @@
   views::View* GetInitiallyFocusedView() override;
 
   // views::DialogDelegate overrides.
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index 8ad14d1..c6c9e70 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -284,11 +284,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 CollectedCookiesViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(IDS_DONE);
-}
-
 bool CollectedCookiesViews::Accept() {
   // If the user closes our parent tab while we're still open, this method will
   // (eventually) be called in response to a WebContentsDestroyed() call from
@@ -386,6 +381,9 @@
     : web_contents_(web_contents) {
   constrained_window::ShowWebModalDialogViews(this, web_contents);
   chrome::RecordDialogCreation(chrome::DialogIdentifier::COLLECTED_COOKIES);
+
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   l10n_util::GetStringUTF16(IDS_DONE));
 }
 
 void CollectedCookiesViews::Init() {
diff --git a/chrome/browser/ui/views/collected_cookies_views.h b/chrome/browser/ui/views/collected_cookies_views.h
index 6071584e..3fded1c 100644
--- a/chrome/browser/ui/views/collected_cookies_views.h
+++ b/chrome/browser/ui/views/collected_cookies_views.h
@@ -52,7 +52,6 @@
   // views::DialogDelegate:
   base::string16 GetWindowTitle() const override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool Accept() override;
   ui::ModalType GetModalType() const override;
   bool ShouldShowCloseButton() const override;
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index b669ec0..54fbfbd6 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -403,6 +403,17 @@
       content_setting_bubble_model_(std::move(content_setting_bubble_model)) {
   chrome::RecordDialogCreation(
       chrome::DialogIdentifier::CONTENT_SETTING_CONTENTS);
+
+  // Although other code in this class treats content_setting_bubble_model_ as
+  // though it's optional, in fact it can only become null if
+  // WebContentsDestroyed() is called, which can't happen until the constructor
+  // has run - so it is never null here.
+  DCHECK(content_setting_bubble_model_);
+  const base::string16& done_text =
+      content_setting_bubble_model_->bubble_content().done_button_text;
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      done_text.empty() ? l10n_util::GetStringUTF16(IDS_DONE) : done_text);
 }
 
 ContentSettingBubbleContents::~ContentSettingBubbleContents() {
@@ -627,16 +638,6 @@
   return ui::DIALOG_BUTTON_OK;
 }
 
-base::string16 ContentSettingBubbleContents::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (!content_setting_bubble_model_)
-    return base::string16();
-
-  const base::string16& done_text =
-      content_setting_bubble_model_->bubble_content().done_button_text;
-  return done_text.empty() ? l10n_util::GetStringUTF16(IDS_DONE) : done_text;
-}
-
 void ContentSettingBubbleContents::StyleLearnMoreButton() {
   DCHECK(learn_more_button_);
   SkColor text_color = GetNativeTheme()->GetSystemColor(
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index d93e88d..ea5d06e 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -71,7 +71,6 @@
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void OnThemeChanged() override;
 
  private:
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
index bad0a12..80cedc8 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -86,10 +86,6 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-bool ExtensionsMenuView::ShouldSnapFrameWidth() const {
-  return true;
-}
-
 gfx::Size ExtensionsMenuView::CalculatePreferredSize() const {
   const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
                         DISTANCE_BUBBLE_PREFERRED_WIDTH) -
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.h b/chrome/browser/ui/views/extensions/extensions_menu_view.h
index 0090b8d..7c352f76 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.h
@@ -44,7 +44,6 @@
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
   int GetDialogButtons() const override;
-  bool ShouldSnapFrameWidth() const override;
   // TODO(crbug.com/1003072): This override is copied from PasswordItemsView to
   // contrain the width. It would be nice to have a unified way of getting the
   // preferred size to not duplicate the code.
diff --git a/chrome/browser/ui/views/external_protocol_dialog.cc b/chrome/browser/ui/views/external_protocol_dialog.cc
index 3ce7b12..d7e0efe 100644
--- a/chrome/browser/ui/views/external_protocol_dialog.cc
+++ b/chrome/browser/ui/views/external_protocol_dialog.cc
@@ -57,11 +57,6 @@
   return false;
 }
 
-base::string16 ExternalProtocolDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return delegate_->GetDialogButtonLabel(button);
-}
-
 base::string16 ExternalProtocolDialog::GetWindowTitle() const {
   return delegate_->GetTitleText();
 }
@@ -104,6 +99,13 @@
     WebContents* web_contents)
     : delegate_(std::move(delegate)), creation_time_(base::TimeTicks::Now()) {
   DialogDelegate::set_default_button(ui::DIALOG_BUTTON_CANCEL);
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      delegate_->GetDialogButtonLabel(ui::DIALOG_BUTTON_OK));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      delegate_->GetDialogButtonLabel(ui::DIALOG_BUTTON_CANCEL));
+
   views::MessageBoxView::InitParams params(delegate_->GetMessageText());
   message_box_view_ = new views::MessageBoxView(params);
 
diff --git a/chrome/browser/ui/views/external_protocol_dialog.h b/chrome/browser/ui/views/external_protocol_dialog.h
index 3b3abf4..332f9895 100644
--- a/chrome/browser/ui/views/external_protocol_dialog.h
+++ b/chrome/browser/ui/views/external_protocol_dialog.h
@@ -36,7 +36,6 @@
   // views::DialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
   bool ShouldShowCloseButton() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   base::string16 GetWindowTitle() const override;
   bool Cancel() override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 3f61b4f..3533fc5 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -894,12 +894,12 @@
     ProfileMenuClickTest_WithUnconsentedPrimaryAccount::kOrderedActionableItems
         [];
 
-// TODO(crbug.com/1012167): Flaky.
 IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_WithUnconsentedPrimaryAccount,
-                       DISABLED_SetupAndRunTest) {
+                       SetupAndRunTest) {
   signin::MakeAccountAvailableWithCookies(identity_manager(),
                                           &test_url_loader_factory_,
                                           "user@example.com", "gaia_id");
+  ASSERT_TRUE(sync_harness()->AwaitSyncTransportActive());
   // Check that the setup was successful.
   ASSERT_FALSE(identity_manager()->HasPrimaryAccount());
   ASSERT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount());
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
index 321a72c..38d0d90 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
@@ -168,6 +168,7 @@
     std::move(stop_callback_).Run();
   RemoveInfobarsForAllTabs();
   SetContentsBorderVisible(shared_tab_, false);
+  tab_capture_indicator_ui_.reset();
   shared_tab_ = nullptr;
 }
 
@@ -180,7 +181,6 @@
   BrowserList* browser_list = BrowserList::GetInstance();
   if (browser_list->empty())
     browser_list->RemoveObserver(this);
-
   browser->tab_strip_model()->RemoveObserver(this);
 }
 
@@ -193,17 +193,6 @@
       if (infobars_.find(contents.contents) == infobars_.end())
         CreateInfobarForWebContents(contents.contents);
     }
-  } else if (change.type() == TabStripModelChange::kRemoved) {
-    auto* remove = change.GetRemove();
-    if (remove->will_be_deleted) {
-      bool remove_shared_tab = false;
-      for (const auto& contents : remove->contents) {
-        remove_shared_tab |= contents.contents == shared_tab_;
-        infobars_.erase(contents.contents);
-      }
-      if (remove_shared_tab)
-        StopSharing();
-    }
   }
 
   if (selection.active_tab_changed()) {
@@ -227,18 +216,18 @@
     CreateInfobarForWebContents(contents);
 }
 
-void TabSharingUIViews::OnInfoBarRemoved(infobars::InfoBar* info_bar,
+void TabSharingUIViews::OnInfoBarRemoved(infobars::InfoBar* infobar,
                                          bool animate) {
   auto infobars_entry = std::find_if(infobars_.begin(), infobars_.end(),
-                                     [info_bar](const auto& infobars_entry) {
-                                       return infobars_entry.second == info_bar;
+                                     [infobar](const auto& infobars_entry) {
+                                       return infobars_entry.second == infobar;
                                      });
   if (infobars_entry == infobars_.end())
     return;
 
-  info_bar->owner()->RemoveObserver(this);
+  infobar->owner()->RemoveObserver(this);
   infobars_.erase(infobars_entry);
-  if (infobars_entry->first == shared_tab_)
+  if (InfoBarService::WebContentsFromInfoBar(infobar) == shared_tab_)
     StopSharing();
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
index 36e63271..a160b41 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
@@ -34,6 +34,11 @@
       delegate_(std::move(delegate)),
       anchored_to_action_(anchored_to_action) {
   DialogDelegate::set_default_button(delegate_->GetDefaultDialogButton());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                   delegate_->GetActionButtonText());
+  DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                   delegate_->GetDismissButtonText());
+
   DCHECK(anchor_view);
   set_close_on_deactivate(delegate_->ShouldCloseOnDeactivate());
   chrome::RecordDialogCreation(chrome::DialogIdentifier::TOOLBAR_ACTIONS_BAR);
@@ -180,12 +185,6 @@
   return buttons;
 }
 
-base::string16 ToolbarActionsBarBubbleViews::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return button == ui::DIALOG_BUTTON_OK ? delegate_->GetActionButtonText()
-                                        : delegate_->GetDismissButtonText();
-}
-
 void ToolbarActionsBarBubbleViews::ButtonPressed(views::Button* sender,
                                                  const ui::Event& event) {
   DCHECK(!delegate_notified_of_close_);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
index 7bc51f8b..1985ffc 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
@@ -47,7 +47,6 @@
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   void Init() override;
 
   // views::ButtonListener:
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc
index de451b17..f9594134 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.cc
@@ -29,6 +29,14 @@
 }  // namespace
 
 ConfirmSignoutDialog::ConfirmSignoutDialog() {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(
+          IDS_ADD_SUPERVISION_EXIT_DIALOG_SIGNOUT_BUTTON_LABEL));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      l10n_util::GetStringUTF16(
+          IDS_ADD_SUPERVISION_EXIT_DIALOG_CANCEL_BUTTON_LABEL));
   SetLayoutManager(std::make_unique<views::FillLayout>());
   SetBorder(views::CreateEmptyBorder(
       views::LayoutProvider::Get()->GetDialogInsetsForContentType(
@@ -66,16 +74,6 @@
          ui::DialogButton::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 ConfirmSignoutDialog::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  if (button == ui::DialogButton::DIALOG_BUTTON_OK) {
-    return l10n_util::GetStringUTF16(
-        IDS_ADD_SUPERVISION_EXIT_DIALOG_SIGNOUT_BUTTON_LABEL);
-  }
-  return l10n_util::GetStringUTF16(
-      IDS_ADD_SUPERVISION_EXIT_DIALOG_CANCEL_BUTTON_LABEL);
-}
-
 // static
 views::Widget* ConfirmSignoutDialog::current_instance_ = nullptr;
 
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h
index f23a9cb..c8d7a820 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/confirm_signout_dialog.h
@@ -29,7 +29,6 @@
   // views::DialogDelegate:
   bool Accept() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
 
   static void Show();
   static bool IsShowing();
diff --git a/chrome/browser/ui/webui/chromeos/login/OWNERS b/chrome/browser/ui/webui/chromeos/login/OWNERS
index a18954d..5dbb46f 100644
--- a/chrome/browser/ui/webui/chromeos/login/OWNERS
+++ b/chrome/browser/ui/webui/chromeos/login/OWNERS
@@ -1,10 +1,10 @@
-# (in PST)
+# primary (in CET)
+antrim@chromium.org
+rsorokin@chromium.org
+
+# secondary (in PST)
 achuith@chromium.org
 alemate@chromium.org
 tbarzic@chromium.org
 
-# (in CET)
-antrim@chromium.org
-per-file *active_directory*=rsorokin@chromium.org
-
 # COMPONENT: UI>Shell>OOBE
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index b1c9f31..c5383c86 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -788,11 +788,10 @@
           Profile::FromWebUI(web_ui()));
   content::StoragePartition* partition =
       signin_partition_manager->GetCurrentStoragePartition();
-  net::CookieOptions cookie_options;
-  cookie_options.set_include_httponly();
 
   partition->GetCookieManagerForBrowserProcess()->GetCookieList(
-      GaiaUrls::GetInstance()->gaia_url(), cookie_options,
+      GaiaUrls::GetInstance()->gaia_url(),
+      net::CookieOptions::MakeAllInclusive(),
       base::BindOnce(&EnrollmentScreenHandler::OnGetCookiesForCompleteLogin,
                      weak_ptr_factory_.GetWeakPtr(), user));
 }
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
index 26744719..841ba4a6 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -211,6 +211,8 @@
 
 void SyncConfirmationHandler::HandleInitializedWithSize(
     const base::ListValue* args) {
+  AllowJavascript();
+
   if (!browser_)
     return;
 
@@ -237,5 +239,5 @@
   // TODO(anthonyvd): Figure out why this is needed on Mac and not other
   // platforms and if there's a way to start unfocused while avoiding this
   // workaround.
-  web_ui()->CallJavascriptFunctionUnsafe("sync.confirmation.clearFocus");
+  FireWebUIListener("clear-focus");
 }
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index 5f686f0..635e261 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -225,8 +225,9 @@
   handler()->HandleInitializedWithSize(&args);
 
   ExpectAccountImageChanged(*web_ui()->call_data()[0]);
-  EXPECT_EQ("sync.confirmation.clearFocus",
+  EXPECT_EQ("cr.webUIListenerCallback",
             web_ui()->call_data()[1]->function_name());
+  EXPECT_EQ("clear-focus", web_ui()->call_data()[1]->arg1()->GetString());
 }
 
 TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReadyLater) {
@@ -236,8 +237,9 @@
 
   EXPECT_EQ(2U, web_ui()->call_data().size());
   ExpectAccountImageChanged(*web_ui()->call_data()[0]);
-  EXPECT_EQ("sync.confirmation.clearFocus",
+  EXPECT_EQ("cr.webUIListenerCallback",
             web_ui()->call_data()[1]->function_name());
+  EXPECT_EQ("clear-focus", web_ui()->call_data()[1]->arg1()->GetString());
 
   identity_test_env()->SimulateSuccessfulFetchOfAccountInfo(
       account_info_.account_id, account_info_.email, account_info_.gaia, "",
diff --git a/chrome/browser/ui/webui/sync_internals_ui.cc b/chrome/browser/ui/webui/sync_internals_ui.cc
index ed5f6af..d4d1835 100644
--- a/chrome/browser/ui/webui/sync_internals_ui.cc
+++ b/chrome/browser/ui/webui/sync_internals_ui.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/sync_internals_message_handler.h"
 #include "chrome/common/url_constants.h"
-#include "components/grit/sync_driver_resources.h"
+#include "components/grit/components_resources.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 292660a2..3b43e41 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -19,6 +19,10 @@
 
 WebApp::~WebApp() = default;
 
+WebApp::WebApp(const WebApp& web_app) = default;
+
+WebApp& WebApp::operator=(WebApp&& web_app) = default;
+
 void WebApp::AddSource(Source::Type source) {
   sources_[source] = true;
 }
@@ -74,14 +78,18 @@
   icons_ = std::move(icons);
 }
 
-void WebApp::SetSyncData(const SyncData& sync_data) {
-  sync_data_ = sync_data;
+void WebApp::SetSyncData(SyncData sync_data) {
+  sync_data_ = std::move(sync_data);
 }
 
 WebApp::SyncData::SyncData() = default;
 
 WebApp::SyncData::~SyncData() = default;
 
+WebApp::SyncData::SyncData(const SyncData& sync_data) = default;
+
+WebApp::SyncData& WebApp::SyncData::operator=(SyncData&& sync_data) = default;
+
 std::ostream& operator<<(std::ostream& out, const WebApp& app) {
   const std::string theme_color =
       app.theme_color_.has_value()
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index a2eeccb2..3f0e59b 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/optional.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
@@ -25,6 +24,14 @@
   explicit WebApp(const AppId& app_id);
   ~WebApp();
 
+  // Copyable and move-assignable to support Copy-on-Write with Commit.
+  WebApp(const WebApp& web_app);
+  WebApp& operator=(WebApp&& web_app);
+
+  // Explicitly disallow other copy ctors and assign operators.
+  WebApp(WebApp&&) = delete;
+  WebApp& operator=(const WebApp&) = delete;
+
   const AppId& app_id() const { return app_id_; }
 
   const std::string& name() const { return name_; }
@@ -58,6 +65,10 @@
   struct SyncData {
     SyncData();
     ~SyncData();
+    // Copyable and move-assignable to support Copy-on-Write with Commit.
+    SyncData(const SyncData& sync_data);
+    SyncData& operator=(SyncData&& sync_data);
+
     std::string name;
     base::Optional<SkColor> theme_color;
   };
@@ -81,14 +92,14 @@
   void SetIsSyncPlaceholder(bool is_sync_placeholder);
   void SetIcons(Icons icons);
 
-  void SetSyncData(const SyncData& sync_data);
+  void SetSyncData(SyncData sync_data);
 
  private:
   friend class WebAppDatabase;
   friend bool operator==(const WebApp&, const WebApp&);
   friend std::ostream& operator<<(std::ostream&, const WebApp&);
 
-  const AppId app_id_;
+  AppId app_id_;
 
   // This set always contains at least one source.
   using Sources = std::bitset<Source::kMaxValue>;
@@ -107,8 +118,6 @@
   Icons icons_;
 
   SyncData sync_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebApp);
 };
 
 // For logging and debug purposes.
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 63e4c678..5ccdbf8 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -62,14 +62,14 @@
     write_batch->WriteData(web_app->app_id(), proto->SerializeAsString());
   }
 
-  for (const AppId& app_id : update_data.apps_to_delete)
-    write_batch->DeleteData(app_id);
-
-  for (const WebApp* web_app : update_data.apps_to_update) {
+  for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_update) {
     auto proto = CreateWebAppProto(*web_app);
     write_batch->WriteData(web_app->app_id(), proto->SerializeAsString());
   }
 
+  for (const AppId& app_id : update_data.apps_to_delete)
+    write_batch->DeleteData(app_id);
+
   store_->CommitWriteBatch(
       std::move(write_batch),
       base::BindOnce(&WebAppDatabase::OnDataWritten,
@@ -216,7 +216,7 @@
     parsed_sync_data.name = sync_data.name();
   if (sync_data.has_theme_color())
     parsed_sync_data.theme_color = sync_data.theme_color();
-  web_app->SetSyncData(parsed_sync_data);
+  web_app->SetSyncData(std::move(parsed_sync_data));
 
   WebApp::Icons icons;
   for (int i = 0; i < local_data.icons_size(); ++i) {
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index 73612fb..9c5008e 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -87,7 +87,7 @@
     WebApp::SyncData sync_data;
     sync_data.name = "Sync" + name;
     sync_data.theme_color = synced_theme_color;
-    app->SetSyncData(sync_data);
+    app->SetSyncData(std::move(sync_data));
 
     return app;
   }
@@ -185,8 +185,8 @@
   const int num_apps = 10;
   const std::string base_url = "https://example.com/path";
 
-  RegistryUpdateData::AppsToCreate apps_to_create;
-  RegistryUpdateData::AppsToDelete apps_to_delete;
+  RegistryUpdateData::Apps apps_to_create;
+  std::vector<AppId> apps_to_delete;
   Registry expected_registry;
 
   for (int i = 0; i < num_apps; ++i) {
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index fbda0793..f572d327f 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -134,7 +134,7 @@
   WebApp::SyncData sync_data;
   sync_data.name = base::UTF16ToUTF8(web_app_info.title);
   sync_data.theme_color = web_app_info.theme_color;
-  web_app->SetSyncData(sync_data);
+  web_app->SetSyncData(std::move(sync_data));
 
   icon_manager_->WriteData(
       std::move(app_id), std::make_unique<WebApplicationInfo>(web_app_info),
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index c5a6398..e5e98e5c 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -629,4 +629,47 @@
   EXPECT_TRUE(ids.empty());
 }
 
+TEST_F(WebAppRegistrarTest, CopyOnWrite) {
+  controller().Init();
+
+  const GURL launch_url("https://example.com");
+  const AppId app_id = GenerateAppIdFromURL(launch_url);
+  const WebApp* app = nullptr;
+  {
+    auto new_app = CreateWebApp(launch_url.spec());
+    app = new_app.get();
+    RegisterApp(std::move(new_app));
+  }
+
+  {
+    std::unique_ptr<WebAppRegistryUpdate> update = sync_bridge().BeginUpdate();
+
+    WebApp* app_copy = update->UpdateApp(app_id);
+    EXPECT_TRUE(app_copy);
+    EXPECT_NE(app_copy, app);
+
+    app_copy->SetName("New Name");
+    EXPECT_EQ(app_copy->name(), "New Name");
+    EXPECT_EQ(app->name(), "Name");
+
+    app_copy->AddSource(Source::kPolicy);
+    app_copy->RemoveSource(Source::kSync);
+
+    EXPECT_FALSE(app_copy->IsSynced());
+    EXPECT_TRUE(app_copy->HasAnySources());
+
+    EXPECT_TRUE(app->IsSynced());
+    EXPECT_TRUE(app->HasAnySources());
+
+    SyncBridgeCommitUpdate(std::move(update));
+  }
+
+  // Pointer value stays the same.
+  EXPECT_EQ(app, registrar().GetAppById(app_id));
+
+  EXPECT_EQ(app->name(), "New Name");
+  EXPECT_FALSE(app->IsSynced());
+  EXPECT_TRUE(app->HasAnySources());
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_registry_update.cc b/chrome/browser/web_applications/web_app_registry_update.cc
index 9eb7aa2c..97a08f9 100644
--- a/chrome/browser/web_applications/web_app_registry_update.cc
+++ b/chrome/browser/web_applications/web_app_registry_update.cc
@@ -20,10 +20,10 @@
          apps_to_update.empty();
 }
 
-WebAppRegistryUpdate::WebAppRegistryUpdate(
-    WebAppRegistrarMutable* mutable_registrar)
-    : mutable_registrar_(mutable_registrar) {
-  DCHECK(mutable_registrar_);
+WebAppRegistryUpdate::WebAppRegistryUpdate(const WebAppRegistrar* registrar,
+                                           util::PassKey<WebAppSyncBridge>)
+    : registrar_(registrar) {
+  DCHECK(registrar_);
   update_data_ = std::make_unique<RegistryUpdateData>();
 }
 
@@ -32,7 +32,7 @@
 void WebAppRegistryUpdate::CreateApp(std::unique_ptr<WebApp> web_app) {
   DCHECK(update_data_);
   DCHECK(!web_app->app_id().empty());
-  DCHECK(!mutable_registrar_->GetAppById(web_app->app_id()));
+  DCHECK(!registrar_->GetAppById(web_app->app_id()));
   DCHECK(!base::Contains(update_data_->apps_to_create, web_app));
 
   update_data_->apps_to_create.push_back(std::move(web_app));
@@ -41,7 +41,7 @@
 void WebAppRegistryUpdate::DeleteApp(const AppId& app_id) {
   DCHECK(update_data_);
   DCHECK(!app_id.empty());
-  DCHECK(mutable_registrar_->GetAppById(app_id));
+  DCHECK(registrar_->GetAppById(app_id));
   DCHECK(!base::Contains(update_data_->apps_to_delete, app_id));
 
   update_data_->apps_to_delete.push_back(app_id);
@@ -49,11 +49,21 @@
 
 WebApp* WebAppRegistryUpdate::UpdateApp(const AppId& app_id) {
   DCHECK(update_data_);
-  WebApp* app = mutable_registrar_->GetAppByIdMutable(app_id);
-  if (app)
-    update_data_->apps_to_update.insert(app);
+  const WebApp* original_app = registrar_->GetAppById(app_id);
+  if (!original_app)
+    return nullptr;
 
-  return app;
+  for (auto& app_to_update : update_data_->apps_to_update) {
+    if (app_to_update->app_id() == app_id)
+      return app_to_update.get();
+  }
+
+  // Make a copy on write.
+  auto app_copy = std::make_unique<WebApp>(*original_app);
+  WebApp* app_copy_ptr = app_copy.get();
+  update_data_->apps_to_update.push_back(std::move(app_copy));
+
+  return app_copy_ptr;
 }
 
 std::unique_ptr<RegistryUpdateData> WebAppRegistryUpdate::TakeUpdateData() {
diff --git a/chrome/browser/web_applications/web_app_registry_update.h b/chrome/browser/web_applications/web_app_registry_update.h
index 41d379bb..4b5fc09 100644
--- a/chrome/browser/web_applications/web_app_registry_update.h
+++ b/chrome/browser/web_applications/web_app_registry_update.h
@@ -9,14 +9,14 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "base/util/type_safety/pass_key.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 
 namespace web_app {
 
 class WebApp;
-class WebAppRegistrarMutable;
+class WebAppRegistrar;
 class WebAppSyncBridge;
 
 // A raw registry update data.
@@ -24,14 +24,11 @@
   RegistryUpdateData();
   ~RegistryUpdateData();
 
-  using AppsToCreate = std::vector<std::unique_ptr<WebApp>>;
-  AppsToCreate apps_to_create;
+  using Apps = std::vector<std::unique_ptr<WebApp>>;
+  Apps apps_to_create;
+  Apps apps_to_update;
 
-  using AppsToDelete = std::vector<AppId>;
-  AppsToDelete apps_to_delete;
-
-  using AppsToUpdate = base::flat_set<const WebApp*>;
-  AppsToUpdate apps_to_update;
+  std::vector<AppId> apps_to_delete;
 
   bool IsEmpty() const;
 
@@ -43,7 +40,8 @@
 // WebAppRegistryUpdate is a part of WebAppSyncBridge class.
 class WebAppRegistryUpdate {
  public:
-  explicit WebAppRegistryUpdate(WebAppRegistrarMutable* mutable_registrar);
+  WebAppRegistryUpdate(const WebAppRegistrar* registrar,
+                       util::PassKey<WebAppSyncBridge>);
   ~WebAppRegistryUpdate();
 
   // Register a new app.
@@ -58,7 +56,7 @@
 
  private:
   std::unique_ptr<RegistryUpdateData> update_data_;
-  WebAppRegistrarMutable* const mutable_registrar_;
+  const WebAppRegistrar* const registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(WebAppRegistryUpdate);
 };
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index ded9d9a..851a3ee 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -13,6 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "base/logging.h"
 #include "base/optional.h"
+#include "base/util/type_safety/pass_key.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_database.h"
@@ -81,7 +82,7 @@
   parsed_sync_data.name = sync_data.name();
   if (sync_data.has_theme_color())
     parsed_sync_data.theme_color = sync_data.theme_color();
-  app->SetSyncData(parsed_sync_data);
+  app->SetSyncData(std::move(parsed_sync_data));
 }
 
 bool AreAppsLocallyInstalledByDefault() {
@@ -133,7 +134,8 @@
 std::unique_ptr<WebAppRegistryUpdate> WebAppSyncBridge::BeginUpdate() {
   DCHECK(!is_in_update_);
   is_in_update_ = true;
-  return std::make_unique<WebAppRegistryUpdate>(registrar_);
+  return std::make_unique<WebAppRegistryUpdate>(
+      registrar_, util::PassKey<WebAppSyncBridge>());
 }
 
 void WebAppSyncBridge::CommitUpdate(
@@ -197,11 +199,11 @@
   for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create)
     DCHECK(!registrar_->GetAppById(web_app->app_id()));
 
+  for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_update)
+    DCHECK(registrar_->GetAppById(web_app->app_id()));
+
   for (const AppId& app_id : update_data.apps_to_delete)
     DCHECK(registrar_->GetAppById(app_id));
-
-  for (const WebApp* web_app : update_data.apps_to_update)
-    DCHECK(registrar_->GetAppById(web_app->app_id()));
 #endif
 }
 
@@ -217,8 +219,13 @@
     registrar_->registry().emplace(std::move(app_id), std::move(web_app));
   }
 
-  // update_data->apps_to_update are ignored here because we do in-place
-  // updates.
+  for (std::unique_ptr<WebApp>& web_app : update_data->apps_to_update) {
+    WebApp* original_web_app = registrar_->GetAppByIdMutable(web_app->app_id());
+    DCHECK(original_web_app);
+    // Commit previously created copy into original. Preserve original web_app
+    // object pointer value (the object's identity) to support stored pointers.
+    *original_web_app = std::move(*web_app);
+  }
 
   for (const AppId& app_id : update_data->apps_to_delete) {
     auto it = registrar_->registry().find(app_id);
@@ -252,11 +259,12 @@
 
   // An app may obtain or may loose IsSynced flag without being deleted. We
   // should conservatively include or exclude the app from the synced apps
-  // subset.
+  // subset. TODO(loyso): Use previous state of the app (and CoW) to detect
+  // if IsSynced flag changed.
   //
   // TODO(loyso): Send an update to sync server only if any sync-specific
   // data was changed. Implement some dirty flags in WebApp setter methods.
-  for (const WebApp* app : update_data.apps_to_update) {
+  for (const std::unique_ptr<WebApp>& app : update_data.apps_to_update) {
     if (app->IsSynced()) {
       change_processor()->Put(app->app_id(), CreateSyncEntityData(*app),
                               metadata_change_list);
@@ -266,7 +274,8 @@
   }
 
   // We should unconditionally delete from sync (in case IsSynced flag was
-  // removed during the update).
+  // removed during the update). TODO(loyso): Use previous state of the app (and
+  // CoW) to detect if IsSynced flag changed.
   for (const AppId& app_id : update_data.apps_to_delete) {
     const WebApp* app = registrar_->GetAppById(app_id);
     DCHECK(app);
@@ -328,7 +337,7 @@
   // app_id is storage key.
   const AppId& app_id = change.storage_key();
 
-  WebApp* existing_web_app = registrar_->GetAppByIdMutable(app_id);
+  const WebApp* existing_web_app = registrar_->GetAppByIdMutable(app_id);
 
   // Handle deletion first.
   if (change.type() == syncer::EntityChange::ACTION_DELETE) {
@@ -336,10 +345,12 @@
       DLOG(ERROR) << "ApplySyncDataChange error: no app to delete";
       return;
     }
-    existing_web_app->RemoveSource(Source::kSync);
+    // Do copy on write:
+    auto app_copy = std::make_unique<WebApp>(*existing_web_app);
+    app_copy->RemoveSource(Source::kSync);
 
-    if (existing_web_app->HasAnySources())
-      update_local_data->apps_to_update.insert(existing_web_app);
+    if (app_copy->HasAnySources())
+      update_local_data->apps_to_update.push_back(std::move(app_copy));
     else
       update_local_data->apps_to_delete.push_back(app_id);
 
@@ -352,10 +363,12 @@
 
   if (existing_web_app) {
     // Any entities that appear in both sets must be merged.
-    ApplySyncDataToApp(specifics, existing_web_app);
+    // Do copy on write:
+    auto app_copy = std::make_unique<WebApp>(*existing_web_app);
+    ApplySyncDataToApp(specifics, app_copy.get());
     // Preserve web_app->is_locally_installed user's choice here.
 
-    update_local_data->apps_to_update.insert(existing_web_app);
+    update_local_data->apps_to_update.push_back(std::move(app_copy));
   } else {
     // Any remote entities that don’t exist locally must be written to local
     // storage.
diff --git a/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.cc b/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.cc
index bf5b4bf..4c5d0f2e 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.cc
+++ b/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.cc
@@ -10,14 +10,14 @@
 
 EngineCleanupResultsImpl::EngineCleanupResultsImpl(
     InterfaceMetadataObserver* metadata_observer)
-    : binding_(this), metadata_observer_(metadata_observer) {}
+    : metadata_observer_(metadata_observer) {}
 
 EngineCleanupResultsImpl::~EngineCleanupResultsImpl() = default;
 
 void EngineCleanupResultsImpl::BindToCallbacks(
-    mojom::EngineCleanupResultsAssociatedPtrInfo* ptr_info,
+    mojo::PendingAssociatedRemote<mojom::EngineCleanupResults>* cleanup_results,
     DoneCallback done_callback) {
-  binding_.Bind(mojo::MakeRequest(ptr_info));
+  receiver_.Bind(cleanup_results->InitWithNewEndpointAndPassReceiver());
   // There's no need to call set_connection_error_handler on this since it's an
   // associated interface. Any errors will be handled on the main EngineCommands
   // interface.
diff --git a/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.h b/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.h
index d12e5837..4eab8be 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.h
+++ b/chrome/chrome_cleaner/engines/broker/engine_cleanup_results_impl.h
@@ -7,7 +7,8 @@
 
 #include "chrome/chrome_cleaner/engines/broker/interface_metadata_observer.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace chrome_cleaner {
 
@@ -19,14 +20,15 @@
 
   using DoneCallback = base::OnceCallback<void(uint32_t result_code)>;
 
-  void BindToCallbacks(mojom::EngineCleanupResultsAssociatedPtrInfo* ptr_info,
+  void BindToCallbacks(mojo::PendingAssociatedRemote<
+                           mojom::EngineCleanupResults>* cleanup_results,
                        DoneCallback done_callback);
 
   // mojom::EngineCleanupResults
   void Done(uint32_t result_code) override;
 
  private:
-  mojo::AssociatedBinding<mojom::EngineCleanupResults> binding_;
+  mojo::AssociatedReceiver<mojom::EngineCleanupResults> receiver_{this};
   DoneCallback done_callback_;
   InterfaceMetadataObserver* metadata_observer_ = nullptr;
 };
diff --git a/chrome/chrome_cleaner/engines/broker/engine_client.cc b/chrome/chrome_cleaner/engines/broker/engine_client.cc
index 7e588d6a..6a0ddc12 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_client.cc
+++ b/chrome/chrome_cleaner/engines/broker/engine_client.cc
@@ -22,11 +22,11 @@
 #include "base/synchronization/waitable_event.h"
 #include "chrome/chrome_cleaner/buildflags.h"
 #include "chrome/chrome_cleaner/constants/quarantine_constants.h"
+#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/logging/logging_service_api.h"
 #include "chrome/chrome_cleaner/mojom/engine_file_requests.mojom.h"
 #include "chrome/chrome_cleaner/mojom/engine_requests.mojom.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
-#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
-#include "chrome/chrome_cleaner/logging/logging_service_api.h"
 #include "chrome/chrome_cleaner/os/layered_service_provider_wrapper.h"
 #include "chrome/chrome_cleaner/os/system_util_cleaner.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
@@ -34,6 +34,9 @@
 #include "chrome/chrome_cleaner/zip_archiver/sandboxed_zip_archiver.h"
 #include "components/chrome_cleaner/public/constants/result_codes.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 using base::WaitableEvent;
 
@@ -47,7 +50,7 @@
 using ResultCallback = base::OnceCallback<void(uint32_t)>;
 
 // Wraps a CallbackWithDeleteHelper around |callback| which is to be passed to
-// |engine_commands_ptr_|. If the connection dies before |callback| is invoked,
+// |engine_commands_|. If the connection dies before |callback| is invoked,
 // Mojo will delete it without running it. In that case call it with default
 // arguments to ensure that side effects (such as unblocking a WaitableEvent)
 // still happen.
@@ -119,7 +122,7 @@
       registry_logging_callback_(logging_callback),
       connection_error_callback_(connection_error_callback),
       mojo_task_runner_(mojo_task_runner),
-      engine_commands_ptr_(std::make_unique<mojom::EngineCommandsPtr>()),
+      engine_commands_(std::make_unique<mojo::Remote<mojom::EngineCommands>>()),
       interface_metadata_observer_(std::move(metadata_observer)) {
   DCHECK(mojo_task_runner_);
   InitializeReadOnlyCallbacks();
@@ -185,7 +188,7 @@
   mojo_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
-          [](std::unique_ptr<mojom::EngineCommandsPtr> commands,
+          [](std::unique_ptr<mojo::Remote<mojom::EngineCommands>> commands,
              std::unique_ptr<EngineScanResultsImpl> scan_results,
              std::unique_ptr<EngineCleanupResultsImpl> cleanup_results,
              std::unique_ptr<EngineFileRequestsImpl> file_requests,
@@ -206,8 +209,7 @@
             // sandbox_cleaner_requests in order to avoid invalid references.
             metadata_observer.reset();
           },
-          base::Passed(&engine_commands_ptr_),
-          base::Passed(&scan_results_impl_),
+          base::Passed(&engine_commands_), base::Passed(&scan_results_impl_),
           base::Passed(&cleanup_results_impl_),
           base::Passed(&sandbox_file_requests_),
           base::Passed(&sandbox_requests_),
@@ -219,19 +221,20 @@
   return kIncreasedWatchdogTimeoutInSeconds;
 }
 
-void EngineClient::PostBindEngineCommandsPtr(
+void EngineClient::PostBindEngineCommandsRemote(
     mojo::ScopedMessagePipeHandle pipe) {
   mojo_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&EngineClient::BindEngineCommandsPtr,
+      FROM_HERE, base::BindOnce(&EngineClient::BindEngineCommandsRemote,
                                 base::RetainedRef(this), std::move(pipe),
                                 base::BindOnce(connection_error_callback_,
                                                SandboxType::kEngine)));
 }
 
-void EngineClient::BindEngineCommandsPtr(mojo::ScopedMessagePipeHandle pipe,
-                                         base::OnceClosure error_handler) {
-  engine_commands_ptr_->Bind(mojom::EngineCommandsPtrInfo(std::move(pipe), 0));
-  engine_commands_ptr_->set_connection_error_handler(std::move(error_handler));
+void EngineClient::BindEngineCommandsRemote(mojo::ScopedMessagePipeHandle pipe,
+                                            base::OnceClosure error_handler) {
+  engine_commands_->Bind(
+      mojo::PendingRemote<mojom::EngineCommands>(std::move(pipe), 0));
+  engine_commands_->set_disconnect_handler(std::move(error_handler));
 }
 
 void EngineClient::MaybeLogResultCode(EngineClient::Operation operation,
@@ -299,7 +302,7 @@
     LOG(ERROR) << "Couldn't get development log directory for sandboxed engine";
 #endif
 
-  (*engine_commands_ptr_)
+  (*engine_commands_)
       ->Initialize(std::move(file_requests_info), logging_path,
                    CallbackWithErrorHandling(std::move(result_callback)));
 }
@@ -352,9 +355,9 @@
 
   // Create a binding to the EngineScanResults interface that will receive
   // results and pass them on to |found_callback| and |done_callback|.
-  mojom::EngineScanResultsAssociatedPtrInfo scan_results_info;
+  mojo::PendingAssociatedRemote<mojom::EngineScanResults> scan_results;
 
-  scan_results_impl_->BindToCallbacks(&scan_results_info, found_callback,
+  scan_results_impl_->BindToCallbacks(&scan_results, found_callback,
                                       std::move(done_callback));
   if (interface_metadata_observer_)
     interface_metadata_observer_->ObserveCall(CURRENT_FILE_AND_METHOD);
@@ -364,10 +367,10 @@
   // EngineResultCode::kSuccess, scan_results_impl_->FoundUwS (which in turn
   // calls |found_callback|) and scan_results_impl_->Done (which in turn calls
   // |done_callback|) with further results.
-  (*engine_commands_ptr_)
+  (*engine_commands_)
       ->StartScan(enabled_uws, enabled_locations, include_details,
                   std::move(file_requests_info),
-                  std::move(engine_requests_info), std::move(scan_results_info),
+                  std::move(engine_requests_info), std::move(scan_results),
                   CallbackWithErrorHandling(std::move(result_callback)));
 }
 
@@ -418,18 +421,18 @@
 
   // Create a binding to the EngineCleanupResults interface that will
   // receive results and pass them on to |done_callback|.
-  mojom::EngineCleanupResultsAssociatedPtrInfo cleanup_results_info;
-  cleanup_results_impl_->BindToCallbacks(&cleanup_results_info,
+  mojo::PendingAssociatedRemote<mojom::EngineCleanupResults> cleanup_results;
+  cleanup_results_impl_->BindToCallbacks(&cleanup_results,
                                          std::move(done_callback));
 
   if (interface_metadata_observer_)
     interface_metadata_observer_->ObserveCall(CURRENT_FILE_AND_METHOD);
 
-  (*engine_commands_ptr_)
+  (*engine_commands_)
       ->StartCleanup(enabled_uws, std::move(file_requests_info),
                      std::move(engine_requests_info),
                      std::move(cleaner_engine_requests_info),
-                     std::move(cleanup_results_info),
+                     std::move(cleanup_results),
                      CallbackWithErrorHandling(std::move(result_callback)));
 }
 
@@ -452,7 +455,7 @@
 void EngineClient::FinalizeAsync(FinalizeCallback result_callback) {
   if (interface_metadata_observer_)
     interface_metadata_observer_->ObserveCall(CURRENT_FILE_AND_METHOD);
-  (*engine_commands_ptr_)
+  (*engine_commands_)
       ->Finalize(CallbackWithErrorHandling(std::move(result_callback)));
 }
 
diff --git a/chrome/chrome_cleaner/engines/broker/engine_client.h b/chrome/chrome_cleaner/engines/broker/engine_client.h
index e852a4c6..245f219a 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_client.h
+++ b/chrome/chrome_cleaner/engines/broker/engine_client.h
@@ -24,12 +24,14 @@
 #include "chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.h"
 #include "chrome/chrome_cleaner/engines/broker/interface_metadata_observer.h"
 #include "chrome/chrome_cleaner/engines/common/engine_result_codes.h"
-#include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
 #include "chrome/chrome_cleaner/ipc/sandbox.h"
+#include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
 #include "chrome/chrome_cleaner/settings/settings_types.h"
 #include "chrome/chrome_cleaner/zip_archiver/zip_archiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace chrome_cleaner {
@@ -86,11 +88,11 @@
   // client.
   uint32_t ScanningWatchdogTimeoutInSeconds() const;
 
-  mojom::EngineCommandsPtr* engine_commands_ptr() const {
-    return engine_commands_ptr_.get();
+  mojo::Remote<mojom::EngineCommands>* engine_commands_remote() const {
+    return engine_commands_.get();
   }
 
-  // Posts a task to the mojo thread to bind an EngineCommandsPtr to |pipe|.
+  // Posts a task to the mojo thread to bind an EngineCommands remote to |pipe|.
   // |error_handler| will be called for errors on this connection.
   //
   // TODO(joenotcharles): When the EngineClient interface is updated to be
@@ -106,7 +108,7 @@
   // could happen during shutdown when ScannerImpl has been deleted but
   // EngineClient is still being kept alive because a StartScanAsync task
   // that's still queued has a reference to it.)
-  virtual void PostBindEngineCommandsPtr(mojo::ScopedMessagePipeHandle pipe);
+  virtual void PostBindEngineCommandsRemote(mojo::ScopedMessagePipeHandle pipe);
 
   using FoundUwSCallback = EngineScanResultsImpl::FoundUwSCallback;
   using DoneCallback = EngineScanResultsImpl::DoneCallback;
@@ -160,8 +162,8 @@
   using StartCleanupCallback = mojom::EngineCommands::StartCleanupCallback;
   using FinalizeCallback = mojom::EngineCommands::FinalizeCallback;
 
-  void BindEngineCommandsPtr(mojo::ScopedMessagePipeHandle pipe,
-                             base::OnceClosure error_handler);
+  void BindEngineCommandsRemote(mojo::ScopedMessagePipeHandle pipe,
+                                base::OnceClosure error_handler);
 
   void InitializeReadOnlyCallbacks();
   bool InitializeCleaningCallbacks();
@@ -211,7 +213,7 @@
 
   // Proxy object that implements the EngineCommands interface by sending the
   // commands over IPC to the sandbox target process.
-  std::unique_ptr<mojom::EngineCommandsPtr> engine_commands_ptr_;
+  std::unique_ptr<mojo::Remote<mojom::EngineCommands>> engine_commands_;
 
   // Handler for scan results returned over the Mojo pipe.
   std::unique_ptr<EngineScanResultsImpl> scan_results_impl_;
diff --git a/chrome/chrome_cleaner/engines/broker/engine_client_mock.h b/chrome/chrome_cleaner/engines/broker/engine_client_mock.h
index 64d43ba..aab4b0a 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_client_mock.h
+++ b/chrome/chrome_cleaner/engines/broker/engine_client_mock.h
@@ -21,13 +21,14 @@
   // cleanup code for tests.
   MockEngineClient();
 
-  // To test |PostBindEngineCommandsPtr|, mock the method
-  // |MockedPostBindEngineCommandsPtr|. This is needed because |pipe| is
+  // To test |PostBindEngineCommandsRemote|, mock the method
+  // |MockedPostBindEngineCommandsRemote|. This is needed because |pipe| is
   // move-only and gmock generates a call to the copy constructor.
-  void PostBindEngineCommandsPtr(mojo::ScopedMessagePipeHandle pipe) override {
-    MockedPostBindEngineCommandsPtr(&pipe);
+  void PostBindEngineCommandsRemote(
+      mojo::ScopedMessagePipeHandle pipe) override {
+    MockedPostBindEngineCommandsRemote(&pipe);
   }
-  MOCK_METHOD1(MockedPostBindEngineCommandsPtr,
+  MOCK_METHOD1(MockedPostBindEngineCommandsRemote,
                void(mojo::ScopedMessagePipeHandle* pipe));
 
   MOCK_CONST_METHOD0(GetEnabledUwS, std::vector<UwSId>());
diff --git a/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.cc b/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.cc
index 93a0227..9def3298 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.cc
+++ b/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.cc
@@ -10,15 +10,15 @@
 
 EngineScanResultsImpl::EngineScanResultsImpl(
     InterfaceMetadataObserver* metadata_observer)
-    : binding_(this), metadata_observer_(metadata_observer) {}
+    : metadata_observer_(metadata_observer) {}
 
 EngineScanResultsImpl::~EngineScanResultsImpl() = default;
 
 void EngineScanResultsImpl::BindToCallbacks(
-    mojom::EngineScanResultsAssociatedPtrInfo* ptr_info,
+    mojo::PendingAssociatedRemote<mojom::EngineScanResults>* scan_results,
     FoundUwSCallback found_uws_callback,
     DoneCallback done_callback) {
-  binding_.Bind(mojo::MakeRequest(ptr_info));
+  receiver_.Bind(scan_results->InitWithNewEndpointAndPassReceiver());
   // There's no need to call set_connection_error_handler on this since it's an
   // associated interface. Any errors will be handled on the main EngineCommands
   // interface.
diff --git a/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.h b/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.h
index 7442ed5b..b4b26e6 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.h
+++ b/chrome/chrome_cleaner/engines/broker/engine_scan_results_impl.h
@@ -11,7 +11,8 @@
 #include "chrome/chrome_cleaner/engines/broker/interface_metadata_observer.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace chrome_cleaner {
 
@@ -27,9 +28,10 @@
       base::RepeatingCallback<void(UwSId pup_id, const PUPData::PUP& pup)>;
   using DoneCallback = base::OnceCallback<void(uint32_t result_code)>;
 
-  void BindToCallbacks(mojom::EngineScanResultsAssociatedPtrInfo* ptr_info,
-                       FoundUwSCallback found_uws_callback,
-                       DoneCallback done_callback);
+  void BindToCallbacks(
+      mojo::PendingAssociatedRemote<mojom::EngineScanResults>* scan_results,
+      FoundUwSCallback found_uws_callback,
+      DoneCallback done_callback);
 
   // mojom::EngineScanResults
 
@@ -37,7 +39,7 @@
   void Done(uint32_t result_code) override;
 
  private:
-  mojo::AssociatedBinding<mojom::EngineScanResults> binding_;
+  mojo::AssociatedReceiver<mojom::EngineScanResults> receiver_{this};
   FoundUwSCallback found_uws_callback_;
   DoneCallback done_callback_;
   InterfaceMetadataObserver* metadata_observer_ = nullptr;
diff --git a/chrome/chrome_cleaner/engines/broker/sandbox_setup.cc b/chrome/chrome_cleaner/engines/broker/sandbox_setup.cc
index f6157c0b..0bbe616 100644
--- a/chrome/chrome_cleaner/engines/broker/sandbox_setup.cc
+++ b/chrome/chrome_cleaner/engines/broker/sandbox_setup.cc
@@ -52,10 +52,10 @@
   // leaks deliberately since this is only for testing and it needs to outlive
   // the EngineClient object.
   //
-  // When using a sandbox the ptr is bound to the broker end of a pipe in
-  // EngineClient::PostBindEngineCommandsPtr and the impl is bound to the
-  // target end in EngineMojoSandboxTargetHooks::BindEngineCommandsRequest.
-  // This binds the ptr directly to the impl. There's no need for an error
+  // When using a sandbox, the remote is bound to the broker end of a pipe in
+  // EngineClient::PostBindEngineCommandsRemote and the impl is bound to the
+  // target end in EngineMojoSandboxTargetHooks::BindEngineCommandsReceiver.
+  // This binds the remote directly to the impl. There's no need for an error
   // handling callback because there's no pipe that can have errors.
   mojo_task_runner->PostTask(
       FROM_HERE,
@@ -63,10 +63,10 @@
           [](scoped_refptr<EngineClient> engine_client,
              scoped_refptr<EngineDelegate> engine_delegate,
              scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-            new EngineCommandsImpl(
-                engine_delegate,
-                mojo::MakeRequest(engine_client->engine_commands_ptr()),
-                task_runner, base::DoNothing::Repeatedly());
+            new EngineCommandsImpl(engine_delegate,
+                                   engine_client->engine_commands_remote()
+                                       ->BindNewPipeAndPassReceiver(),
+                                   task_runner, base::DoNothing::Repeatedly());
           },
           engine_client, CreateEngineDelegate(engine_name), mojo_task_runner));
 
@@ -97,7 +97,7 @@
   mojo::ScopedMessagePipeHandle mojo_pipe =
       SetupSandboxMessagePipe(policy, command_line);
 
-  engine_client_->PostBindEngineCommandsPtr(std::move(mojo_pipe));
+  engine_client_->PostBindEngineCommandsRemote(std::move(mojo_pipe));
 
   // Propagate engine selection switches to the sandbox target.
   command_line->AppendSwitchNative(
diff --git a/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc b/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
index f13c70f3..04df59b 100644
--- a/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
+++ b/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
@@ -73,7 +73,7 @@
     mojo::ScopedMessagePipeHandle pipe_handle =
         SetupSandboxMessagePipe(policy, command_line);
 
-    engine_client_->PostBindEngineCommandsPtr(std::move(pipe_handle));
+    engine_client_->PostBindEngineCommandsRemote(std::move(pipe_handle));
 
     return RESULT_CODE_SUCCESS;
   }
diff --git a/chrome/chrome_cleaner/engines/target/BUILD.gn b/chrome/chrome_cleaner/engines/target/BUILD.gn
index ac24f4a..905752e 100644
--- a/chrome/chrome_cleaner/engines/target/BUILD.gn
+++ b/chrome/chrome_cleaner/engines/target/BUILD.gn
@@ -42,6 +42,7 @@
     "//chrome/chrome_cleaner/pup_data:pup_data_base",
     "//chrome/chrome_cleaner/strings",
     "//components/chrome_cleaner/public/constants:constants",
+    "//mojo/public/cpp/bindings",
     "//sandbox/win:sandbox",
   ]
 
@@ -110,9 +111,11 @@
     "//chrome/chrome_cleaner/engines/common",
     "//chrome/chrome_cleaner/engines/common:resources_header",
     "//chrome/chrome_cleaner/ipc:ipc_test_util",
+    "//chrome/chrome_cleaner/mojom:engine_sandbox_interface",
     "//chrome/chrome_cleaner/os:cleaner_os",
     "//chrome/chrome_cleaner/os:common_os",
     "//chrome/chrome_cleaner/pup_data:pup_data_base",
+    "//mojo/public/cpp/bindings",
   ]
 }
 
diff --git a/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.cc b/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.cc
index bf23594..bfc806c4 100644
--- a/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.cc
+++ b/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.cc
@@ -12,13 +12,14 @@
 namespace chrome_cleaner {
 
 EngineCleanupResultsProxy::EngineCleanupResultsProxy(
-    mojom::EngineCleanupResultsAssociatedPtr cleanup_results_ptr,
+    mojo::PendingAssociatedRemote<mojom::EngineCleanupResults> cleanup_results,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : cleanup_results_ptr_(std::move(cleanup_results_ptr)),
-      task_runner_(task_runner) {}
+    : task_runner_(task_runner) {
+  cleanup_results_.Bind(std::move(cleanup_results));
+}
 
-void EngineCleanupResultsProxy::UnbindCleanupResultsPtr() {
-  cleanup_results_ptr_.reset();
+void EngineCleanupResultsProxy::UnbindCleanupResults() {
+  cleanup_results_.reset();
 }
 
 void EngineCleanupResultsProxy::CleanupDone(uint32_t result) {
@@ -30,11 +31,11 @@
 EngineCleanupResultsProxy::~EngineCleanupResultsProxy() = default;
 
 void EngineCleanupResultsProxy::OnDone(uint32_t result) {
-  if (!cleanup_results_ptr_.is_bound()) {
+  if (!cleanup_results_.is_bound()) {
     LOG(ERROR) << "Cleanup result reported after the engine was shut down";
     return;
   }
-  cleanup_results_ptr_->Done(result);
+  cleanup_results_->Done(result);
 }
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.h b/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.h
index 55f3eb60..4a62b00a 100644
--- a/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.h
+++ b/chrome/chrome_cleaner/engines/target/engine_cleanup_results_proxy.h
@@ -10,6 +10,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace chrome_cleaner {
 
@@ -18,14 +20,15 @@
     : public base::RefCountedThreadSafe<EngineCleanupResultsProxy> {
  public:
   EngineCleanupResultsProxy(
-      mojom::EngineCleanupResultsAssociatedPtr cleanup_results_ptr,
+      mojo::PendingAssociatedRemote<mojom::EngineCleanupResults>
+          cleanup_results,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
     return task_runner_;
   }
 
-  void UnbindCleanupResultsPtr();
+  void UnbindCleanupResults();
 
   // Sends a cleanup done signal to the broken process. Will be called on an
   // arbitrary thread from the sandboxed engine.
@@ -35,12 +38,12 @@
   friend class base::RefCountedThreadSafe<EngineCleanupResultsProxy>;
   ~EngineCleanupResultsProxy();
 
-  // Invokes cleanup_results_ptr_->Done from the IPC thread.
+  // Invokes cleanup_results_->Done from the IPC thread.
   void OnDone(uint32_t result);
 
   // An EngineCleanupResults that will send the results over the Mojo
   // connection.
-  mojom::EngineCleanupResultsAssociatedPtr cleanup_results_ptr_;
+  mojo::AssociatedRemote<mojom::EngineCleanupResults> cleanup_results_;
 
   // A task runner for the IPC thread.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chrome/chrome_cleaner/engines/target/engine_commands_impl.cc b/chrome/chrome_cleaner/engines/target/engine_commands_impl.cc
index 9140cff..944b5cc8 100644
--- a/chrome/chrome_cleaner/engines/target/engine_commands_impl.cc
+++ b/chrome/chrome_cleaner/engines/target/engine_commands_impl.cc
@@ -20,14 +20,10 @@
 
 using mojom::CleanerEngineRequestsAssociatedPtr;
 using mojom::CleanerEngineRequestsAssociatedPtrInfo;
-using mojom::EngineCleanupResultsAssociatedPtr;
-using mojom::EngineCleanupResultsAssociatedPtrInfo;
 using mojom::EngineFileRequestsAssociatedPtr;
 using mojom::EngineFileRequestsAssociatedPtrInfo;
 using mojom::EngineRequestsAssociatedPtr;
 using mojom::EngineRequestsAssociatedPtrInfo;
-using mojom::EngineScanResultsAssociatedPtr;
-using mojom::EngineScanResultsAssociatedPtrInfo;
 
 namespace {
 
@@ -54,13 +50,13 @@
 
 EngineCommandsImpl::EngineCommandsImpl(
     scoped_refptr<EngineDelegate> engine_delegate,
-    mojom::EngineCommandsRequest request,
+    mojo::PendingReceiver<mojom::EngineCommands> receiver,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     base::OnceClosure error_handler)
     : engine_delegate_(engine_delegate),
-      binding_(this, std::move(request)),
+      receiver_(this, std::move(receiver)),
       task_runner_(task_runner) {
-  binding_.set_connection_error_handler(std::move(error_handler));
+  receiver_.set_disconnect_handler(std::move(error_handler));
 }
 
 EngineCommandsImpl::~EngineCommandsImpl() = default;
@@ -95,7 +91,7 @@
     bool include_details,
     mojom::EngineFileRequestsAssociatedPtrInfo file_requests,
     mojom::EngineRequestsAssociatedPtrInfo sandboxed_engine_requests,
-    EngineScanResultsAssociatedPtrInfo scan_results_info,
+    mojo::PendingAssociatedRemote<mojom::EngineScanResults> scan_results,
     StartScanCallback callback) {
   ScopedCrashStageRecorder crash_stage(__func__);
 
@@ -114,10 +110,8 @@
 
   // Create an EngineScanResults proxy to send results back over the
   // Mojo connection.
-  EngineScanResultsAssociatedPtr scan_results_ptr;
-  scan_results_ptr.Bind(std::move(scan_results_info));
   scoped_refptr<EngineScanResultsProxy> scan_results_proxy =
-      base::MakeRefCounted<EngineScanResultsProxy>(std::move(scan_results_ptr),
+      base::MakeRefCounted<EngineScanResultsProxy>(std::move(scan_results),
                                                    task_runner_);
 
   uint32_t result_code = engine_delegate_->StartScan(
@@ -132,7 +126,7 @@
     mojom::EngineRequestsAssociatedPtrInfo sandboxed_engine_requests,
     mojom::CleanerEngineRequestsAssociatedPtrInfo
         sandboxed_cleaner_engine_requests,
-    mojom::EngineCleanupResultsAssociatedPtrInfo clean_results_info,
+    mojo::PendingAssociatedRemote<mojom::EngineCleanupResults> cleanup_results,
     StartCleanupCallback callback) {
   ScopedCrashStageRecorder crash_stage(__func__);
 
@@ -158,11 +152,9 @@
 
   // Create an EngineCleanupResults proxy to send results back over the
   // Mojo connection.
-  mojom::EngineCleanupResultsAssociatedPtr cleanup_results_ptr;
-  cleanup_results_ptr.Bind(std::move(clean_results_info));
   scoped_refptr<EngineCleanupResultsProxy> cleanup_results_proxy =
       base::MakeRefCounted<EngineCleanupResultsProxy>(
-          std::move(cleanup_results_ptr), task_runner_);
+          std::move(cleanup_results), task_runner_);
 
   uint32_t result_code = engine_delegate_->StartCleanup(
       enabled_uws, file_requests_proxy, engine_requests_proxy,
diff --git a/chrome/chrome_cleaner/engines/target/engine_commands_impl.h b/chrome/chrome_cleaner/engines/target/engine_commands_impl.h
index 5b20243..e0fcf32 100644
--- a/chrome/chrome_cleaner/engines/target/engine_commands_impl.h
+++ b/chrome/chrome_cleaner/engines/target/engine_commands_impl.h
@@ -16,14 +16,16 @@
 #include "chrome/chrome_cleaner/mojom/engine_requests.mojom.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chrome_cleaner {
 
 class EngineCommandsImpl : public mojom::EngineCommands {
  public:
   EngineCommandsImpl(scoped_refptr<EngineDelegate> engine_delegate,
-                     mojom::EngineCommandsRequest request,
+                     mojo::PendingReceiver<mojom::EngineCommands> receiver,
                      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                      base::OnceClosure error_handler);
   ~EngineCommandsImpl() override;
@@ -39,7 +41,7 @@
       bool include_details,
       mojom::EngineFileRequestsAssociatedPtrInfo file_requests,
       mojom::EngineRequestsAssociatedPtrInfo sandboxed_engine_requests,
-      mojom::EngineScanResultsAssociatedPtrInfo scan_results_info,
+      mojo::PendingAssociatedRemote<mojom::EngineScanResults> scan_results,
       StartScanCallback callback) override;
   void StartCleanup(
       const std::vector<UwSId>& enabled_uws,
@@ -47,7 +49,8 @@
       mojom::EngineRequestsAssociatedPtrInfo sandboxed_engine_requests,
       mojom::CleanerEngineRequestsAssociatedPtrInfo
           sandboxed_cleaner_engine_requests,
-      mojom::EngineCleanupResultsAssociatedPtrInfo cleanup_results_info,
+      mojo::PendingAssociatedRemote<mojom::EngineCleanupResults>
+          cleanup_results,
       StartCleanupCallback callback) override;
   void Finalize(FinalizeCallback callback) override;
 
@@ -58,7 +61,7 @@
       uint32_t result_code);
 
   scoped_refptr<EngineDelegate> engine_delegate_;
-  mojo::Binding<mojom::EngineCommands> binding_;
+  mojo::Receiver<mojom::EngineCommands> receiver_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
 
diff --git a/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.cc b/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.cc
index 96dd7a2..b0e1fcd 100644
--- a/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.cc
+++ b/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.cc
@@ -13,13 +13,14 @@
 namespace chrome_cleaner {
 
 EngineScanResultsProxy::EngineScanResultsProxy(
-    mojom::EngineScanResultsAssociatedPtr scan_results_ptr,
+    mojo::PendingAssociatedRemote<mojom::EngineScanResults> scan_results,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : scan_results_ptr_(std::move(scan_results_ptr)),
-      task_runner_(task_runner) {}
+    : task_runner_(task_runner) {
+  scan_results_.Bind(std::move(scan_results));
+}
 
-void EngineScanResultsProxy::UnbindScanResultsPtr() {
-  scan_results_ptr_.reset();
+void EngineScanResultsProxy::UnbindScanResults() {
+  scan_results_.reset();
 }
 
 void EngineScanResultsProxy::FoundUwS(UwSId pup_id, const PUPData::PUP& pup) {
@@ -35,22 +36,22 @@
 
 EngineScanResultsProxy::~EngineScanResultsProxy() = default;
 
-// Invokes scan_results_ptr_->FoundUwS from the IPC thread.
+// Invokes scan_results_->FoundUwS from the IPC thread.
 void EngineScanResultsProxy::OnFoundUwS(UwSId pup_id, const PUPData::PUP& pup) {
-  if (!scan_results_ptr_.is_bound()) {
+  if (!scan_results_.is_bound()) {
     LOG(ERROR) << "Found UwS reported after the engine was shut down";
     return;
   }
-  scan_results_ptr_->FoundUwS(pup_id, pup);
+  scan_results_->FoundUwS(pup_id, pup);
 }
 
-// Invokes scan_results_ptr_->Done from the IPC thread.
+// Invokes scan_results_->Done from the IPC thread.
 void EngineScanResultsProxy::OnDone(uint32_t result) {
-  if (!scan_results_ptr_.is_bound()) {
+  if (!scan_results_.is_bound()) {
     LOG(ERROR) << "Scan result reported after the engine was shut down";
     return;
   }
-  scan_results_ptr_->Done(result);
+  scan_results_->Done(result);
 }
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.h b/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.h
index 4d5472f..de39da2 100644
--- a/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.h
+++ b/chrome/chrome_cleaner/engines/target/engine_scan_results_proxy.h
@@ -9,6 +9,8 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace chrome_cleaner {
 
@@ -17,14 +19,14 @@
     : public base::RefCountedThreadSafe<EngineScanResultsProxy> {
  public:
   EngineScanResultsProxy(
-      mojom::EngineScanResultsAssociatedPtr scan_results_ptr,
+      mojo::PendingAssociatedRemote<mojom::EngineScanResults> scan_results,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
     return task_runner_;
   }
 
-  void UnbindScanResultsPtr();
+  void UnbindScanResults();
 
   // Notifies the broker process that UwS was found. Will be called on an
   // arbitrary thread from the sandboxed engine.
@@ -47,7 +49,7 @@
   void OnDone(uint32_t result);
 
   // An EngineScanResults that will send the results over the Mojo connection.
-  mojom::EngineScanResultsAssociatedPtr scan_results_ptr_;
+  mojo::AssociatedRemote<mojom::EngineScanResults> scan_results_;
 
   // A task runner for the IPC thread.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chrome/chrome_cleaner/engines/target/sandbox_setup.cc b/chrome/chrome_cleaner/engines/target/sandbox_setup.cc
index 59a7bcd..ea61938 100644
--- a/chrome/chrome_cleaner/engines/target/sandbox_setup.cc
+++ b/chrome/chrome_cleaner/engines/target/sandbox_setup.cc
@@ -19,7 +19,9 @@
 #include "chrome/chrome_cleaner/engines/target/libraries.h"
 #include "chrome/chrome_cleaner/ipc/mojo_sandbox_hooks.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/os/early_exit.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace chrome_cleaner {
 
@@ -31,7 +33,8 @@
                                MojoTaskRunner* mojo_task_runner);
   ~EngineMojoSandboxTargetHooks() override;
 
-  void BindEngineCommandsRequest(mojom::EngineCommandsRequest request);
+  void BindEngineCommandsReceiver(
+      mojo::PendingReceiver<mojom::EngineCommands> receiver);
 
   // SandboxTargetHooks
 
@@ -66,22 +69,23 @@
 ResultCode EngineMojoSandboxTargetHooks::TargetDroppedPrivileges(
     const base::CommandLine& command_line) {
   // Connect to the Mojo message pipe from the parent process.
-  mojom::EngineCommandsRequest request(ExtractSandboxMessagePipe(command_line));
+  mojo::PendingReceiver<mojom::EngineCommands> receiver(
+      ExtractSandboxMessagePipe(command_line));
 
   // This loop will run forever. Once the communication channel with the broker
   // process is broken, mojo error handler will abort this process.
   base::RunLoop run_loop;
   mojo_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&EngineMojoSandboxTargetHooks::BindEngineCommandsRequest,
-                     base::Unretained(this), base::Passed(&request)));
+      base::BindOnce(&EngineMojoSandboxTargetHooks::BindEngineCommandsReceiver,
+                     base::Unretained(this), base::Passed(&receiver)));
 
   run_loop.Run();
   return RESULT_CODE_SUCCESS;
 }
 
-void EngineMojoSandboxTargetHooks::BindEngineCommandsRequest(
-    mojom::EngineCommandsRequest request) {
+void EngineMojoSandboxTargetHooks::BindEngineCommandsReceiver(
+    mojo::PendingReceiver<mojom::EngineCommands> receiver) {
   // If the connection dies, the parent process has terminated unexpectedly.
   // Exit immediately. The child process should be killed automatically if the
   // parent dies, so this is just a fallback. The exit code is arbitrary since
@@ -89,7 +93,7 @@
   auto error_handler = base::BindOnce(&EarlyExit, 1);
 
   engine_commands_impl_ = std::make_unique<EngineCommandsImpl>(
-      std::move(engine_delegate_), std::move(request), mojo_task_runner_,
+      std::move(engine_delegate_), std::move(receiver), mojo_task_runner_,
       std::move(error_handler));
 }
 
diff --git a/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.cc b/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.cc
index df66d2a5..711d417 100644
--- a/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.cc
+++ b/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.cc
@@ -11,6 +11,8 @@
 #include "chrome/chrome_cleaner/engines/common/engine_result_codes.h"
 #include "chrome/chrome_cleaner/os/early_exit.h"
 #include "chrome/chrome_cleaner/os/initializer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace chrome_cleaner {
 
@@ -102,24 +104,24 @@
 
   mojo::ScopedMessagePipeHandle message_pipe_handle =
       CreateMessagePipeFromCommandLine();
-  mojom::EngineCommandsRequest engine_commands_request(
+  mojo::PendingReceiver<mojom::EngineCommands> engine_commands_receiver(
       std::move(message_pipe_handle));
   base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
   mojo_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SandboxChildProcess::BindEngineCommandsRequest,
+      base::BindOnce(&SandboxChildProcess::BindEngineCommandsReceiver,
                      base::Unretained(this),
-                     base::Passed(&engine_commands_request), &event));
+                     base::Passed(&engine_commands_receiver), &event));
   event.Wait();
 }
 
-void SandboxChildProcess::SandboxChildProcess::BindEngineCommandsRequest(
-    mojom::EngineCommandsRequest request,
+void SandboxChildProcess::SandboxChildProcess::BindEngineCommandsReceiver(
+    mojo::PendingReceiver<mojom::EngineCommands> receiver,
     base::WaitableEvent* event) {
   fake_engine_delegate_ = base::MakeRefCounted<FakeEngineDelegate>(event);
   engine_commands_impl_ = std::make_unique<EngineCommandsImpl>(
-      fake_engine_delegate_, std::move(request), mojo_task_runner_,
+      fake_engine_delegate_, std::move(receiver), mojo_task_runner_,
       /*error_handler=*/base::BindOnce(&EarlyExit, kConnectionErrorExitCode));
 }
 
diff --git a/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.h b/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.h
index 089b9e92..55cc3f8 100644
--- a/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.h
+++ b/chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.h
@@ -24,9 +24,14 @@
 #include "chrome/chrome_cleaner/engines/target/engine_file_requests_proxy.h"
 #include "chrome/chrome_cleaner/engines/target/engine_requests_proxy.h"
 #include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
+#include "chrome/chrome_cleaner/mojom/engine_sandbox.mojom.h"
 #include "chrome/chrome_cleaner/os/file_remover.h"
 #include "chrome/chrome_cleaner/os/layered_service_provider_wrapper.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chrome_cleaner {
 
@@ -46,7 +51,8 @@
       InterfaceMetadataObserver* metadata_observer = nullptr)
       : BaseClass(std::move(mojo_task_runner)),
         requests_to_setup_(requests_to_setup),
-        engine_commands_ptr_(std::make_unique<mojom::EngineCommandsPtr>()),
+        engine_commands_(
+            std::make_unique<mojo::Remote<mojom::EngineCommands>>()),
         file_requests_impl_(
             std::make_unique<EngineFileRequestsImpl>(this->mojo_task_runner(),
                                                      metadata_observer)) {
@@ -76,56 +82,54 @@
 
  protected:
   void CreateImpl(mojo::ScopedMessagePipeHandle mojo_pipe) override {
-    engine_commands_ptr_->Bind(
-        mojom::EngineCommandsPtrInfo(std::move(mojo_pipe), 0));
+    engine_commands_->Bind(
+        mojo::PendingRemote<mojom::EngineCommands>(std::move(mojo_pipe), 0));
 
     mojom::EngineFileRequestsAssociatedPtrInfo file_requests_info;
     file_requests_impl_->Bind(&file_requests_info);
 
     // Bind to empty callbacks as we don't care about the result.
     mojom::EngineRequestsAssociatedPtrInfo scanner_info;
-    mojom::EngineScanResultsAssociatedPtrInfo scanner_results_info;
+    mojo::PendingAssociatedRemote<mojom::EngineScanResults> scanner_results;
     if (requests_to_setup_ == CallbacksToSetup::kScanAndCleanupRequests ||
         requests_to_setup_ == CallbacksToSetup::kCleanupRequests) {
       scanner_impl_->Bind(&scanner_info);
       scan_results_impl_->BindToCallbacks(
-          &scanner_results_info,
+          &scanner_results,
           base::BindRepeating(
               base::DoNothing::Repeatedly<UwSId, const PUPData::PUP&>()),
           base::BindOnce(base::DoNothing::Once<uint32_t>()));
     }
 
     mojom::CleanerEngineRequestsAssociatedPtrInfo cleaner_info;
-    mojom::EngineCleanupResultsAssociatedPtrInfo cleaner_results_info;
+    mojo::PendingAssociatedRemote<mojom::EngineCleanupResults> cleanup_results;
     if (requests_to_setup_ == CallbacksToSetup::kCleanupRequests) {
       cleaner_impl_->Bind(&cleaner_info);
       cleanup_results_impl_->BindToCallbacks(
-          &cleaner_results_info,
-          base::BindOnce(base::DoNothing::Once<uint32_t>()));
+          &cleanup_results, base::BindOnce(base::DoNothing::Once<uint32_t>()));
     }
 
     // Now call the target process to signal that setup is finished.
     auto operation_started = base::BindOnce([](uint32_t unused_result_code) {});
     if (requests_to_setup_ == CallbacksToSetup::kFileRequests) {
-      (*engine_commands_ptr_)
+      (*engine_commands_)
           ->Initialize(std::move(file_requests_info), base::FilePath(),
                        std::move(operation_started));
 
     } else if (requests_to_setup_ ==
                CallbacksToSetup::kScanAndCleanupRequests) {
-      (*engine_commands_ptr_)
+      (*engine_commands_)
           ->StartScan(/*enabled_uws=*/std::vector<UwSId>{},
                       /*enabled_locations=*/std::vector<UwS::TraceLocation>{},
                       /*include_details=*/false, std::move(file_requests_info),
-                      std::move(scanner_info), std::move(scanner_results_info),
+                      std::move(scanner_info), std::move(scanner_results),
                       std::move(operation_started));
 
     } else if (requests_to_setup_ == CallbacksToSetup::kCleanupRequests) {
-      (*engine_commands_ptr_)
+      (*engine_commands_)
           ->StartCleanup(/*enabled_uws=*/std::vector<UwSId>(),
                          std::move(file_requests_info), std::move(scanner_info),
-                         std::move(cleaner_info),
-                         std::move(cleaner_results_info),
+                         std::move(cleaner_info), std::move(cleanup_results),
                          std::move(operation_started));
     }
   }
@@ -133,7 +137,7 @@
   void DestroyImpl() override {
     // Reset everything in the reverse order. Reset the associated pointers
     // first since they will error if they are closed after
-    // |engine_commands_ptr|.
+    // |engine_commands_|.
     cleanup_results_impl_.reset();
     cleaner_impl_.reset();
 
@@ -141,12 +145,12 @@
     scanner_impl_.reset();
 
     file_requests_impl_.reset();
-    engine_commands_ptr_.reset();
+    engine_commands_.reset();
   }
 
   CallbacksToSetup requests_to_setup_;
 
-  std::unique_ptr<mojom::EngineCommandsPtr> engine_commands_ptr_;
+  std::unique_ptr<mojo::Remote<mojom::EngineCommands>> engine_commands_;
   std::unique_ptr<EngineFileRequestsImpl> file_requests_impl_;
 
   std::unique_ptr<EngineRequestsImpl> scanner_impl_;
@@ -160,8 +164,9 @@
  public:
   explicit SandboxChildProcess(scoped_refptr<MojoTaskRunner> mojo_task_runner);
 
-  void BindEngineCommandsRequest(mojom::EngineCommandsRequest request,
-                                 base::WaitableEvent* event);
+  void BindEngineCommandsReceiver(
+      mojo::PendingReceiver<mojom::EngineCommands> receiver,
+      base::WaitableEvent* event);
 
   scoped_refptr<EngineFileRequestsProxy> GetFileRequestsProxy();
   scoped_refptr<EngineRequestsProxy> GetEngineRequestsProxy();
diff --git a/chrome/chrome_cleaner/mojom/engine_sandbox.mojom b/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
index 890bdb3..69c719a1 100644
--- a/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
+++ b/chrome/chrome_cleaner/mojom/engine_sandbox.mojom
@@ -52,12 +52,14 @@
   // and |results| is not used. Otherwise a success result code is returned and
   // any further errors will be reported by calling Done on the |results|
   // interface.
-  StartScan(array<uint32> enabled_uws,
-            array<TraceLocation> enabled_trace_locations,
-            bool include_details,
-            associated EngineFileRequests file_requests,
-            associated EngineRequests sandboxed_engine_requests,
-            associated EngineScanResults results) => (uint32 result_code);
+  StartScan(
+    array<uint32> enabled_uws,
+    array<TraceLocation> enabled_trace_locations,
+    bool include_details,
+    associated EngineFileRequests file_requests,
+    associated EngineRequests sandboxed_engine_requests,
+    pending_associated_remote<EngineScanResults> results) =>
+      (uint32 result_code);
 
   // Starts cleaning up the user's system. |enabled_uws| contains a list of UwS
   // IDs to cleanup.
@@ -66,12 +68,13 @@
   // and |results| is not used. Otherwise a success result code is returned and
   // any further errors will be reported by calling Done on the |results|
   // interface.
-  StartCleanup(array<uint32> enabled_uws,
-               associated EngineFileRequests file_requests,
-               associated EngineRequests sandboxed_engine_requests,
-               associated CleanerEngineRequests
-                 sandboxed_cleaner_engine_requests,
-               associated EngineCleanupResults results) => (uint32 result_code);
+  StartCleanup(
+    array<uint32> enabled_uws,
+    associated EngineFileRequests file_requests,
+    associated EngineRequests sandboxed_engine_requests,
+    associated CleanerEngineRequests sandboxed_cleaner_engine_requests,
+    pending_associated_remote<EngineCleanupResults> results) =>
+      (uint32 result_code);
 
   // Runs the engine's finalization routine.
   Finalize() => (uint32 result_code);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 50272d3ec..0c5dcbf1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1365,7 +1365,6 @@
       "origin_policy/origin_policy_browsertest.cc",
       "ppapi/ppapi_browsertest.cc",
       "ppapi/ppapi_filechooser_browsertest.cc",
-      "trustedtypes/trusted_types_browsertest.cc",
       "v8/wasm_trap_handler_browsertest.cc",
     ]
 
@@ -3825,6 +3824,7 @@
       "../browser/ui/chrome_bubble_manager_unittest.cc",
       "../browser/ui/content_settings/content_setting_bubble_model_unittest.cc",
       "../browser/ui/content_settings/content_setting_image_model_unittest.cc",
+      "../browser/ui/content_settings/content_setting_media_image_model_unittest.mm",
       "../browser/ui/cookie_controls/cookie_controls_controller_unittest.cc",
       "../browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc",
       "../browser/ui/extensions/extension_action_view_controller_unittest.cc",
diff --git a/chrome/test/trustedtypes/trusted_types_browsertest.cc b/chrome/test/trustedtypes/trusted_types_browsertest.cc
deleted file mode 100644
index 2147232..0000000
--- a/chrome/test/trustedtypes/trusted_types_browsertest.cc
+++ /dev/null
@@ -1,216 +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 "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/common/content_features.h"
-#include "net/http/http_status_code.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"
-
-namespace {
-// Origin Trial tokens are bound to a specific origin (incl. port), so we need
-// to force our test server to run on the same port that the test token has
-// been generated for.
-const int kServerPort = 54321;
-
-// We (thankfully) cannot generate origin trial tokens with the production key
-// There is an origin trial 'test key' is documented of sorts, but does not
-// have a standard API.
-// Ref: docs/origin_trials_integration.md
-// Ref: src/third_party/blink/common/origin_trials/trial_token_unittest.cc
-constexpr char kOriginTrialTestPublicKey[] =
-    "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=";
-
-// Origin Trial Token for TrustedDOMTypes generated with:
-// $ tools/origin_trials/generate_token.py  \
-//     https://127.0.0.1:54321/  \
-//     "TrustedDOMTypes"  \
-//     --expire-timestamp=2000000000
-// (Token will expire ca. ~2033. See content/test/data/origin_trials/basic.html)
-constexpr char kOriginTrialToken[] =
-    "AnRnI2yGt1XQTaKUvbAQ8nRas1bXSDIWwfjeEaDKtXvHgid7wigd4IMm4DkBWsFWM+"
-    "Cww0rgYOpQpBWPBPN8xQwAAABZeyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NTQzMjEi"
-    "LCAiZmVhdHVyZSI6ICJUcnVzdGVkRE9NVHlwZXMiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
-constexpr char kTrustedTypesCSP[] = "trusted-types *";
-constexpr char kDefaultResponseTemplate[] = R"(
-<html>
-<head>
-  <title>(starting)</title>
-  META
-  <script id="target"></script>
-  <script>
-    const tt_available = window.TrustedTypes ? "enabled" : "disabled";
-    const target = document.getElementById("target");
-    let tt_enforced = "dont know yet";
-    try {
-      target.textContent = "2+2;";
-      tt_enforced = "ignored";
-    } catch (e) {
-      tt_enforced = "enforced";
-    }
-    document.title = `Trusted Types: ${tt_available} and ${tt_enforced}`;
-  </script>
-</head>
-<body>
-  <p>Hello World!</p>
-  <p>This test sets the document title.</p>
-</body>
-</html>
-)";
-
-// The expected test page titles when Trusted Types are disabled or enabled:
-constexpr char kTitleEnabled[] = "Trusted Types: enabled and enforced";
-constexpr char kTitleDisabled[] = "Trusted Types: disabled and ignored";
-constexpr char kTitleAvailable[] = "Trusted Types: enabled and ignored";
-
-// Generate a test response, based on kDefaultResponseTemplate.
-// If the request path contains:
-// - "otheader"/"otmeta": Put Origin Trial in header/<meta> element.
-//   (Use the token from kOriginTrialToken.)
-// - "cspheader"/"cspmeta": Put CSP into header/<meta> element.
-//   (Use the CSP from kTrustedTypesCSP.
-// Return 404 for all paths not ending in ".html".
-std::unique_ptr<net::test_server::HttpResponse> TrustedTypesTestHandler(
-    const net::test_server::HttpRequest& request) {
-  std::unique_ptr<net::test_server::BasicHttpResponse> response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-  std::string url = request.GetURL().spec();
-  if (!base::EndsWith(url, ".html", base::CompareCase::SENSITIVE)) {
-    response->set_code(net::HTTP_NOT_FOUND);
-    return response;
-  }
-
-  std::string meta;
-  if (url.find("otheader") != std::string::npos) {
-    response->AddCustomHeader("origin-trial", kOriginTrialToken);
-  } else if (url.find("otmeta") != std::string::npos) {
-    meta.append(std::string() + R"(<meta http-equiv="origin-trial" content=")" +
-                kOriginTrialToken + R"(">)");
-  }
-  if (url.find("cspheader") != std::string::npos) {
-    response->AddCustomHeader("Content-Security-Policy", kTrustedTypesCSP);
-  } else if (url.find("cspmeta") != std::string::npos) {
-    meta.append(std::string() +
-                R"(<meta http-equiv="Content-Security-Policy" content=")" +
-                kTrustedTypesCSP + R"(">)");
-  }
-
-  std::string contents = kDefaultResponseTemplate;
-  base::ReplaceFirstSubstringAfterOffset(&contents, 0, "META", meta);
-  response->set_content(contents);
-  response->set_content_type("text/html");
-  response->set_code(net::HTTP_OK);
-  return response;
-}
-
-}  // namespace
-
-// TrustedTypesBrowserTest tests activation of Trusted Types via CSP and Origin
-// Trial. (The tests for the actual TT functionality are found in
-// external/wpt/trusted-types/*.)
-class TrustedTypesBrowserTest : public InProcessBrowserTest {
- public:
-  TrustedTypesBrowserTest() = default;
-  ~TrustedTypesBrowserTest() override = default;
-
-  void SetUpInProcessBrowserTestFixture() override {
-    server_ = std::make_unique<net::test_server::EmbeddedTestServer>(
-        net::test_server::EmbeddedTestServer::TYPE_HTTPS);
-    server_->RegisterRequestHandler(base::Bind(&TrustedTypesTestHandler));
-    EXPECT_TRUE(server()->Start(kServerPort));
-  }
-
-  void TearDownInProcessBrowserTestFixture() override { server_.reset(); }
-
-  net::test_server::EmbeddedTestServer* server() { return server_.get(); }
-
-  base::string16 NavigateToAndReturnTitle(const char* url) {
-    EXPECT_TRUE(server());
-    ui_test_utils::NavigateToURL(browser(), GURL(server()->GetURL(url)));
-    base::string16 title;
-    ui_test_utils::GetCurrentTabTitle(browser(), &title);
-    return title;
-  }
-
-  void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
-    InProcessBrowserTest::SetUpDefaultCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kOriginTrialPublicKey,
-                                    kOriginTrialTestPublicKey);
-  }
-
-  void SetUp() override {
-    // We need to explicitly disable the feature, so that our test cases can
-    // verify whether enabling it actually works.
-    feature_list_.InitAndDisableFeature(features::kTrustedDOMTypes);
-    InProcessBrowserTest::SetUp();
-  }
-
- private:
-  std::unique_ptr<net::test_server::EmbeddedTestServer> server_;
-  base::test::ScopedFeatureList feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrustedTypesBrowserTest);
-};
-
-// Our test cases are effectively a 3x3 matrix of origin trial token (absent|in
-// header|in <meta>) and content security policy (absent|in header|in <meta>).
-// The test fixture will generate the appropriate page based on the URL path.
-
-// crbug.com/1003738: Mark tests as MANUAL until that issue is fixed.
-// (See: BrowserTestBase::ShouldSkipManualTests)
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest, MANUAL_PagePlain) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleDisabled),
-            NavigateToAndReturnTitle("/page.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest, MANUAL_PageWithTokenInHeader) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleAvailable),
-            NavigateToAndReturnTitle("/page-otheader.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest, MANUAL_PageWithTokenInMeta) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleAvailable),
-            NavigateToAndReturnTitle("/page-otmeta.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest, MANUAL_PageWithCSPInHeaderX) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleDisabled),
-            NavigateToAndReturnTitle("/page-cspheader.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest,
-                       MANUAL_PageWithCSPAndTokenInHeader) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleEnabled),
-            NavigateToAndReturnTitle("/page-cspheader-otheader.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest,
-                       MANUAL_PageWithCSPInHeaderAndTokenInMeta) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleEnabled),
-            NavigateToAndReturnTitle("/page-cspheader-otmeta.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest, MANUAL_PageWithCSPInMetaX) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleDisabled),
-            NavigateToAndReturnTitle("/page-cspmeta.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest,
-                       MANUAL_PageWithCSPInMetaAndTokenInHeader) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleEnabled),
-            NavigateToAndReturnTitle("/page-cspmeta-otheader.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(TrustedTypesBrowserTest,
-                       MANUAL_PageWithCSPAndTokenInMeta) {
-  EXPECT_EQ(base::ASCIIToUTF16(kTitleEnabled),
-            NavigateToAndReturnTitle("/page-cspmeta-otmeta.html"));
-}
diff --git a/chromecast/browser/webview/webview_controller.cc b/chromecast/browser/webview/webview_controller.cc
index b5855cc..5c074fb 100644
--- a/chromecast/browser/webview/webview_controller.cc
+++ b/chromecast/browser/webview/webview_controller.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/web_preferences.h"
@@ -69,6 +70,11 @@
 
 WebviewController::~WebviewController() {
   cast_web_contents_->RemoveObserver(this);
+
+  if (surface_) {
+    surface_->RemoveSurfaceObserver(this);
+    surface_->SetEmbeddedSurfaceId(base::RepeatingCallback<viz::SurfaceId()>());
+  }
 }
 
 std::unique_ptr<content::NavigationThrottle>
@@ -212,13 +218,42 @@
   cast_web_contents_->ClosePage();
 }
 
+viz::SurfaceId WebviewController::GetSurfaceId() {
+  auto* rwhv = contents_->GetRenderWidgetHostView();
+  if (!rwhv)
+    return viz::SurfaceId();
+  auto frame_sink_id = rwhv->GetRenderWidgetHost()->GetFrameSinkId();
+  auto local_surface_id =
+      rwhv->GetNativeView()->GetLocalSurfaceIdAllocation().local_surface_id();
+  return viz::SurfaceId(frame_sink_id, local_surface_id);
+}
+
 void WebviewController::AttachTo(aura::Window* window, int window_id) {
   auto* contents_window = contents_->GetNativeView();
-  window->SetLayoutManager(new WebviewLayoutManager(contents_window));
+  window->SetLayoutManager(new WebviewLayoutManager(window, contents_window));
   contents_window->set_id(window_id);
   contents_window->SetBounds(gfx::Rect(window->bounds().size()));
-  contents_window->Show();
-  window->AddChild(contents_->GetNativeView());
+  // The aura window is hidden to avoid being shown via the usual layer method,
+  // instead it is shows via a SurfaceDrawQuad by exo.
+  contents_window->Hide();
+  window->AddChild(contents_window);
+
+  exo::Surface* surface = exo::Surface::AsSurface(window);
+  CHECK(surface) << "Attaching Webview to non-EXO surface window";
+  CHECK(!surface_) << "Attaching already attached WebView";
+
+  surface_ = surface;
+  surface_->AddSurfaceObserver(this);
+
+  // Unretained is safe because we unset this in the destructor.
+  surface_->SetEmbeddedSurfaceId(
+      base::Bind(&WebviewController::GetSurfaceId, base::Unretained(this)));
+}
+
+void WebviewController::OnSurfaceDestroying(exo::Surface* surface) {
+  DCHECK_EQ(surface, surface_);
+  surface->RemoveSurfaceObserver(this);
+  surface_ = nullptr;
 }
 
 void WebviewController::ProcessInputEvent(const webview::InputEvent& ev) {
diff --git a/chromecast/browser/webview/webview_controller.h b/chromecast/browser/webview/webview_controller.h
index 7efbbfd2..cddd688 100644
--- a/chromecast/browser/webview/webview_controller.h
+++ b/chromecast/browser/webview/webview_controller.h
@@ -11,6 +11,8 @@
 #include "base/supports_user_data.h"
 #include "chromecast/browser/cast_web_contents.h"
 #include "chromecast/browser/webview/proto/webview.pb.h"
+#include "components/exo/surface.h"
+#include "components/exo/surface_observer.h"
 #include "ui/events/gestures/gesture_recognizer_impl.h"
 #include "url/gurl.h"
 
@@ -36,7 +38,8 @@
 // This owns a WebContents and CastWebContents and processes proto commands
 // to allow the web contents to be controlled and embedded.
 class WebviewController : public CastWebContents::Delegate,
-                          public CastWebContents::Observer {
+                          public CastWebContents::Observer,
+                          public exo::SurfaceObserver {
  public:
   class Client {
    public:
@@ -90,6 +93,7 @@
   void HandleUpdateSettings(const webview::UpdateSettingsRequest& request);
   void HandleSetAutoMediaPlaybackPolicy(
       const webview::SetAutoMediaPlaybackPolicyRequest& request);
+  viz::SurfaceId GetSurfaceId();
 
   bool Check(bool condition, const char* error);
 
@@ -101,6 +105,9 @@
   // CastWebContents::Observer
   void ResourceLoadFailed(CastWebContents* cast_web_contents) override;
 
+  // exo::SurfaceObserver
+  void OnSurfaceDestroying(exo::Surface* surface) override;
+
   Client* client_;  // Not owned.
   std::unique_ptr<content::WebContents> contents_;
   std::unique_ptr<CastWebContents> cast_web_contents_;
@@ -110,6 +117,8 @@
 
   ui::GestureRecognizerImpl gesture_recognizer_;
 
+  exo::Surface* surface_ = nullptr;
+
   // The navigation throttle for the current navigation event, if any.
   // Is set only:
   //    When has_navigation_delegate is true, and
diff --git a/chromecast/browser/webview/webview_layout_manager.cc b/chromecast/browser/webview/webview_layout_manager.cc
index d781b86..1318b20 100644
--- a/chromecast/browser/webview/webview_layout_manager.cc
+++ b/chromecast/browser/webview/webview_layout_manager.cc
@@ -8,14 +8,14 @@
 
 namespace chromecast {
 
-WebviewLayoutManager::WebviewLayoutManager(aura::Window* web_contents_window)
-    : web_contents_window_(web_contents_window) {}
+WebviewLayoutManager::WebviewLayoutManager(aura::Window* parent,
+                                           aura::Window* web_contents_window)
+    : parent_(parent), web_contents_window_(web_contents_window) {}
 
 WebviewLayoutManager::~WebviewLayoutManager() {}
 
 void WebviewLayoutManager::OnWindowResized() {
-  web_contents_window_->SetBounds(
-      gfx::Rect(web_contents_window_->parent()->bounds().size()));
+  web_contents_window_->SetBounds(gfx::Rect(parent_->bounds().size()));
 }
 
 void WebviewLayoutManager::OnWindowAddedToLayout(aura::Window* child) {}
diff --git a/chromecast/browser/webview/webview_layout_manager.h b/chromecast/browser/webview/webview_layout_manager.h
index 6127b02..e26a250 100644
--- a/chromecast/browser/webview/webview_layout_manager.h
+++ b/chromecast/browser/webview/webview_layout_manager.h
@@ -13,7 +13,7 @@
 // Resizes the provided window to be the parent window's size.
 class WebviewLayoutManager : public aura::LayoutManager {
  public:
-  explicit WebviewLayoutManager(aura::Window* web_contents_window);
+  WebviewLayoutManager(aura::Window* parent, aura::Window* web_contents_window);
   ~WebviewLayoutManager() override;
 
   void OnWindowResized() override;
@@ -26,6 +26,7 @@
                       const gfx::Rect& requested_bounds) override;
 
  private:
+  aura::Window* parent_;
   aura::Window* web_contents_window_;
 
   DISALLOW_COPY_AND_ASSIGN(WebviewLayoutManager);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 355d4c9..8e44cf9 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12584.0.0
\ No newline at end of file
+12585.0.0
\ No newline at end of file
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 7068d12..1381fb7 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -14,39 +14,28 @@
 
 namespace autofill {
 
-AutofillField::AutofillField()
-    : server_type_(NO_SERVER_DATA),
-      heuristic_type_(UNKNOWN_TYPE),
-      overall_type_(AutofillType(NO_SERVER_DATA)),
-      html_type_(HTML_TYPE_UNSPECIFIED),
-      html_mode_(HTML_MODE_NONE),
-      phone_part_(IGNORED),
-      credit_card_number_offset_(0),
-      previously_autofilled_(false),
-      only_fill_when_focused_(false),
-      generation_type_(AutofillUploadContents::Field::NO_GENERATION),
-      generated_password_changed_(false),
-      vote_type_(AutofillUploadContents::Field::NO_INFORMATION) {}
+AutofillField::AutofillField() = default;
+
+AutofillField::AutofillField(FieldSignature field_signature)
+    : field_signature_(field_signature) {}
 
 AutofillField::AutofillField(const FormFieldData& field,
                              const base::string16& unique_name)
     : FormFieldData(field),
       unique_name_(unique_name),
-      server_type_(NO_SERVER_DATA),
-      heuristic_type_(UNKNOWN_TYPE),
-      overall_type_(AutofillType(NO_SERVER_DATA)),
-      html_type_(HTML_TYPE_UNSPECIFIED),
-      html_mode_(HTML_MODE_NONE),
-      phone_part_(IGNORED),
-      credit_card_number_offset_(0),
-      previously_autofilled_(false),
-      only_fill_when_focused_(false),
-      parseable_name_(field.name),
-      generation_type_(AutofillUploadContents::Field::NO_GENERATION),
-      generated_password_changed_(false),
-      vote_type_(AutofillUploadContents::Field::NO_INFORMATION) {}
+      parseable_name_(field.name) {
+  field_signature_ =
+      CalculateFieldSignatureByNameAndType(name, form_control_type);
+}
 
-AutofillField::~AutofillField() {}
+AutofillField::~AutofillField() = default;
+
+std::unique_ptr<AutofillField> AutofillField::CreateForPasswordManagerUpload(
+    FieldSignature field_signature) {
+  std::unique_ptr<AutofillField> field;
+  field.reset(new AutofillField(field_signature));
+  return field;
+}
 
 void AutofillField::set_heuristic_type(ServerFieldType type) {
   if (type >= 0 && type < MAX_VALID_FIELD_TYPE &&
@@ -178,7 +167,9 @@
 }
 
 FieldSignature AutofillField::GetFieldSignature() const {
-  return CalculateFieldSignatureByNameAndType(name, form_control_type);
+  return field_signature_
+             ? *field_signature_
+             : CalculateFieldSignatureByNameAndType(name, form_control_type);
 }
 
 std::string AutofillField::FieldSignatureAsStr() const {
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index 80c5d03d..30856f9 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -43,6 +43,12 @@
   AutofillField(const FormFieldData& field, const base::string16& unique_name);
   virtual ~AutofillField();
 
+  // Creates AutofillField that has bare minimum information for uploading
+  // votes, namely a field signature. Warning: do not use for Autofill code,
+  // since it is likely missing some fields.
+  static std::unique_ptr<AutofillField> CreateForPasswordManagerUpload(
+      FieldSignature field_signature);
+
   const base::string16& unique_name() const { return unique_name_; }
 
   ServerFieldType heuristic_type() const { return heuristic_type_; }
@@ -178,14 +184,17 @@
   void NormalizePossibleTypesValidities();
 
  private:
+  explicit AutofillField(FieldSignature field_signature);
+
   // Whether the heuristics or server predict a credit card field.
   bool IsCreditCardPrediction() const;
 
+  base::Optional<FieldSignature> field_signature_;
   // The unique name of this field, generated by Autofill.
   base::string16 unique_name_;
 
   // The type of the field, as determined by the Autofill server.
-  ServerFieldType server_type_;
+  ServerFieldType server_type_ = NO_SERVER_DATA;
 
   // The possible types of the field, as determined by the Autofill server,
   // including |server_type_| as the first item.
@@ -197,7 +206,7 @@
   base::Optional<PasswordRequirementsSpec> password_requirements_;
 
   // The type of the field, as determined by the local heuristics.
-  ServerFieldType heuristic_type_;
+  ServerFieldType heuristic_type_ = UNKNOWN_TYPE;
 
   // The type of the field. Overrides all other types (html_type_,
   // heuristic_type_, server_type_).
@@ -206,11 +215,11 @@
   AutofillType overall_type_;
 
   // The type of the field, as specified by the site author in HTML.
-  HtmlFieldType html_type_;
+  HtmlFieldType html_type_ = HTML_TYPE_UNSPECIFIED;
 
   // The "mode" of the field, as specified by the site author in HTML.
   // Currently this is used to distinguish between billing and shipping fields.
-  HtmlFieldMode html_mode_;
+  HtmlFieldMode html_mode_ = HTML_MODE_NONE;
 
   // The set of possible types for this field.
   ServerFieldTypeSet possible_types_;
@@ -219,7 +228,7 @@
   ServerFieldTypeValidityStatesMap possible_types_validities_;
 
   // Used to track whether this field is a phone prefix or suffix.
-  PhonePart phone_part_;
+  PhonePart phone_part_ = IGNORED;
 
   // A low-entropy hash of the field's initial value before user-interactions or
   // automatic fillings. This field is used to detect static placeholders.
@@ -227,13 +236,13 @@
 
   // Used to hold the position of the first digit to be copied as a substring
   // from credit card number.
-  size_t credit_card_number_offset_;
+  size_t credit_card_number_offset_ = 0;
 
   // Whether the field was autofilled then later edited.
-  bool previously_autofilled_;
+  bool previously_autofilled_ = false;
 
   // Whether the field should be filled when it is not the highlighted field.
-  bool only_fill_when_focused_;
+  bool only_fill_when_focused_ = false;
 
   // The parseable name attribute, with unnecessary information removed (such as
   // a common prefix shared with other fields). Will be used for heuristics
@@ -241,15 +250,17 @@
   base::string16 parseable_name_;
 
   // The type of password generation event, if it happened.
-  AutofillUploadContents::Field::PasswordGenerationType generation_type_;
+  AutofillUploadContents::Field::PasswordGenerationType generation_type_ =
+      AutofillUploadContents::Field::NO_GENERATION;
 
   // Whether the generated password was changed by user.
-  bool generated_password_changed_;
+  bool generated_password_changed_ = false;
 
   // The vote type, if the autofill type is USERNAME or any password vote.
   // Otherwise, the field is ignored. |vote_type_| provides context as to what
   // triggered the vote.
-  AutofillUploadContents::Field::VoteType vote_type_;
+  AutofillUploadContents::Field::VoteType vote_type_ =
+      AutofillUploadContents::Field::NO_INFORMATION;
 
   DISALLOW_COPY_AND_ASSIGN(AutofillField);
 };
diff --git a/components/autofill/core/browser/autofill_type.h b/components/autofill/core/browser/autofill_type.h
index 09ffd1b..0e65d1f 100644
--- a/components/autofill/core/browser/autofill_type.h
+++ b/components/autofill/core/browser/autofill_type.h
@@ -24,7 +24,7 @@
 // and for associating form fields with form values in the Web Database.
 class AutofillType {
  public:
-  explicit AutofillType(ServerFieldType field_type);
+  explicit AutofillType(ServerFieldType field_type = NO_SERVER_DATA);
   AutofillType(HtmlFieldType field_type, HtmlFieldMode mode);
   AutofillType(const AutofillType& autofill_type) = default;
   AutofillType& operator=(const AutofillType& autofill_type) = default;
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 85be7732..bf4c348 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -564,25 +564,13 @@
       name_attribute_(form.name_attribute),
       form_name_(form.name),
       button_titles_(form.button_titles),
-      submission_event_(SubmissionIndicatorEvent::NONE),
       source_url_(form.url),
       target_url_(form.action),
       main_frame_origin_(form.main_frame_origin),
-      autofill_count_(0),
-      active_field_count_(0),
-      upload_required_(USE_UPLOAD_RATES),
-      has_author_specified_types_(false),
-      has_author_specified_sections_(false),
-      has_author_specified_upi_vpa_hint_(false),
-      was_parsed_for_autocomplete_attributes_(false),
-      has_password_field_(false),
       is_form_tag_(form.is_form_tag),
       is_formless_checkout_(form.is_formless_checkout),
       all_fields_are_passwords_(!form.fields.empty()),
       form_parsed_timestamp_(base::TimeTicks::Now()),
-      passwords_were_revealed_(false),
-      password_symbol_vote_(0),
-      developer_engagement_metrics_(0),
       unique_renderer_id_(form.unique_renderer_id) {
   // Copy the form fields.
   std::map<base::string16, size_t> unique_names;
@@ -609,7 +597,15 @@
   ProcessExtractedFields();
 }
 
-FormStructure::~FormStructure() {}
+FormStructure::FormStructure(
+    FormSignature form_signature,
+    const std::vector<FieldSignature>& field_signatures)
+    : form_signature_(form_signature) {
+  for (const auto& signature : field_signatures)
+    fields_.push_back(AutofillField::CreateForPasswordManagerUpload(signature));
+}
+
+FormStructure::~FormStructure() = default;
 
 void FormStructure::DetermineHeuristicTypes(LogManager* log_manager) {
   const auto determine_heuristic_types_start_time = base::TimeTicks::Now();
@@ -909,6 +905,14 @@
   return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE);
 }
 
+std::unique_ptr<FormStructure> FormStructure::CreateForPasswordManagerUpload(
+    FormSignature form_signature,
+    const std::vector<FieldSignature>& field_signatures) {
+  std::unique_ptr<FormStructure> form;
+  form.reset(new FormStructure(form_signature, field_signatures));
+  return form;
+}
+
 std::string FormStructure::FormSignatureAsStr() const {
   return base::NumberToString(form_signature());
 }
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index d65bc8e..4117c49 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -109,6 +109,13 @@
   // Returns whether sending autofill field metadata to the server is enabled.
   static bool IsAutofillFieldMetadataEnabled();
 
+  // Creates FormStructure that has bare minimum information for uploading
+  // votes, namely form and field signatures. Warning: do not use for Autofill
+  // code, since it is likely missing some fields.
+  static std::unique_ptr<FormStructure> CreateForPasswordManagerUpload(
+      FormSignature form_signature,
+      const std::vector<FieldSignature>& field_signatures);
+
   // Return the form signature as string.
   std::string FormSignatureAsStr() const;
 
@@ -408,6 +415,9 @@
     size_t current_section_ptr = 0;
   };
 
+  FormStructure(FormSignature form_signature,
+                const std::vector<FieldSignature>& field_signatures);
+
   // A function to fine tune the credit cards related predictions. For example:
   // lone credit card fields in an otherwise non-credit-card related form is
   // unlikely to be correct, the function will override that prediction.
@@ -520,7 +530,8 @@
 
   // The type of the event that was taken as an indication that the form has
   // been successfully submitted.
-  mojom::SubmissionIndicatorEvent submission_event_;
+  mojom::SubmissionIndicatorEvent submission_event_ =
+      mojom::SubmissionIndicatorEvent::NONE;
 
   // The source URL.
   GURL source_url_;
@@ -532,50 +543,50 @@
   url::Origin main_frame_origin_;
 
   // The number of fields able to be auto-filled.
-  size_t autofill_count_;
+  size_t autofill_count_ = 0;
 
   // A vector of all the input fields in the form.
   std::vector<std::unique_ptr<AutofillField>> fields_;
 
   // The number of fields that are part of the form signature and that are
   // included in queries to the Autofill server.
-  size_t active_field_count_;
+  size_t active_field_count_ = 0;
 
   // Whether the server expects us to always upload, never upload, or default
   // to the stored upload rates.
-  UploadRequired upload_required_;
+  UploadRequired upload_required_ = USE_UPLOAD_RATES;
 
   // Whether the form includes any field types explicitly specified by the site
   // author, via the |autocompletetype| attribute.
-  bool has_author_specified_types_;
+  bool has_author_specified_types_ = false;
 
   // Whether the form includes any sections explicitly specified by the site
   // author, via the autocomplete attribute.
-  bool has_author_specified_sections_;
+  bool has_author_specified_sections_ = false;
 
   // Whether the form includes a field that explicitly sets it autocomplete
   // type to "upi-vpa".
-  bool has_author_specified_upi_vpa_hint_;
+  bool has_author_specified_upi_vpa_hint_ = false;
 
   // Whether the form was parsed for autocomplete attribute, thus assigning
   // the real values of |has_author_specified_types_| and
   // |has_author_specified_sections_|.
-  bool was_parsed_for_autocomplete_attributes_;
+  bool was_parsed_for_autocomplete_attributes_ = false;
 
   // True if the form contains at least one password field.
-  bool has_password_field_;
+  bool has_password_field_ = false;
 
   // True if the form is a <form>.
-  bool is_form_tag_;
+  bool is_form_tag_ = true;
 
   // True if the form is made of unowned fields (i.e., not within a <form> tag)
   // in what appears to be a checkout flow. This attribute is only calculated
   // and used if features::kAutofillRestrictUnownedFieldsToFormlessCheckout is
   // enabled, to prevent heuristics from running on formless non-checkout.
-  bool is_formless_checkout_;
+  bool is_formless_checkout_ = false;
 
   // True if all form fields are password fields.
-  bool all_fields_are_passwords_;
+  bool all_fields_are_passwords_ = false;
 
   // The unique signature for this form, composed of the target url domain,
   // the form name, and the form field names in a 64-bit hash.
@@ -589,7 +600,7 @@
 
   // True iff the form is a password form and the user has seen the password
   // value before accepting the prompt to save. Used for crowdsourcing.
-  bool passwords_were_revealed_;
+  bool passwords_were_revealed_ = false;
 
   // The vote about password attributes (e.g. whether the password has a numeric
   // character).
@@ -599,7 +610,7 @@
   // field contains nosified information about a special symbol in a
   // user-created password stored as ASCII code. The default value of 0
   // indicates that no symbol was set.
-  int password_symbol_vote_;
+  int password_symbol_vote_ = 0;
 
   // Noisified password length for crowdsourcing. If |password_attributes_vote_|
   // has no value, |password_length_vote_| should be ignored.
@@ -608,7 +619,7 @@
   // Used to record whether developer has used autocomplete markup or
   // UPI-VPA hints, This is a bitmask of DeveloperEngagementMetric and set in
   // DetermineHeuristicTypes().
-  int developer_engagement_metrics_;
+  int developer_engagement_metrics_ = 0;
 
   mojom::SubmissionSource submission_source_ = mojom::SubmissionSource::NONE;
 
@@ -628,7 +639,6 @@
 };
 
 LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form);
-
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_H_
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 68a41e1..f0b086f 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -7123,4 +7123,19 @@
   EXPECT_FALSE(FormStructure(form).ShouldBeUploaded());
 }
 
+// Checks that CreateForPasswordManagerUpload builds FormStructure
+// which is encodable (i.e. ready for uploading).
+TEST_F(FormStructureTest, CreateForPasswordManagerUpload) {
+  std::unique_ptr<FormStructure> form =
+      FormStructure::CreateForPasswordManagerUpload(
+          1234 /* form_signature */, {1, 10, 100} /* field_signatures */);
+  AutofillUploadContents upload;
+  EXPECT_EQ(1234u, form->form_signature());
+  ASSERT_EQ(3u, form->field_count());
+  ASSERT_EQ(100u, form->field(2)->GetFieldSignature());
+  EXPECT_TRUE(form->EncodeUploadRequest(
+      {} /* available_field_types */, false /* form_was_autofilled */,
+      "" /*login_form_signature*/, true /*observed_submission*/, &upload));
+}
+
 }  // namespace autofill
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 64dfc22..5ae1b6b 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -23,6 +23,7 @@
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -540,6 +541,12 @@
   return window_->GetProperty(kClientSurfaceIdKey);
 }
 
+void Surface::SetEmbeddedSurfaceId(
+    base::RepeatingCallback<viz::SurfaceId()> surface_id_callback) {
+  get_current_surface_id_ = std::move(surface_id_callback);
+  first_embedded_surface_id_ = viz::SurfaceId();
+}
+
 void Surface::SetAcquireFence(std::unique_ptr<gfx::GpuFence> gpu_fence) {
   TRACE_EVENT1("exo", "Surface::SetAcquireFence", "fence_fd",
                gpu_fence ? gpu_fence->GetGpuFenceHandle().native_fd.fd : -1);
@@ -1013,14 +1020,33 @@
       buffer_transform_.TransformRectReverse(&uv_crop);
     }
 
-    // Texture quad is only needed if buffer is not fully transparent.
-    if (state_.alpha) {
+    SkColor background_color = SK_ColorTRANSPARENT;
+    if (current_resource_has_alpha_ && are_contents_opaque)
+      background_color = SK_ColorBLACK;  // Avoid writing alpha < 1
+
+    // If this surface is being replaced by a SurfaceId emit a SurfaceDrawQuad.
+    if (get_current_surface_id_) {
+      auto current_surface_id = get_current_surface_id_.Run();
+      if (current_surface_id.is_valid()) {
+        if (!first_embedded_surface_id_.is_valid())
+          first_embedded_surface_id_ = current_surface_id;
+        viz::SurfaceDrawQuad* surface_quad =
+            render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
+        surface_quad->SetNew(
+            quad_state, quad_rect, quad_rect,
+            viz::SurfaceRange(first_embedded_surface_id_, current_surface_id),
+            background_color,
+            /*stretch_content_to_fill_bounds=*/true,
+            /*ignores_input_event=*/false);
+      } else {
+        // If there is no valid surface, reset the start of the range.
+        first_embedded_surface_id_ = viz::SurfaceId();
+      }
+    } else if (state_.alpha) {
+      // Texture quad is only needed if buffer is not fully transparent.
       viz::TextureDrawQuad* texture_quad =
           render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
       float vertex_opacity[4] = {1.0, 1.0, 1.0, 1.0};
-      SkColor background_color = SK_ColorTRANSPARENT;
-      if (current_resource_has_alpha_ && are_contents_opaque)
-        background_color = SK_ColorBLACK;  // Avoid writing alpha < 1
       texture_quad->SetNew(
           quad_state, quad_rect, quad_rect,
           /* needs_blending=*/!are_contents_opaque, current_resource_.id,
diff --git a/components/exo/surface.h b/components/exo/surface.h
index 2feb1f3..20f9bca 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -170,6 +170,12 @@
   void SetClientSurfaceId(int32_t client_surface_id);
   int32_t GetClientSurfaceId() const;
 
+  // Enable embedding of an arbitrary viz surface in this exo surface.
+  // If the callback is valid, a SurfaceDrawQuad will be emitted targeting
+  // the returned SurfaceId each frame.
+  void SetEmbeddedSurfaceId(
+      base::RepeatingCallback<viz::SurfaceId()> surface_id_callback);
+
   // Request that the attached surface buffer at the next commit is associated
   // with a gpu fence to be signaled when the buffer is ready for use.
   void SetAcquireFence(std::unique_ptr<gfx::GpuFence> gpu_fence);
@@ -440,6 +446,9 @@
   std::unique_ptr<ash::OutputProtectionDelegate> output_protection_;
 #endif  // defined(OS_CHROMEOS)
 
+  viz::SurfaceId first_embedded_surface_id_;
+  base::RepeatingCallback<viz::SurfaceId()> get_current_surface_id_;
+
   DISALLOW_COPY_AND_ASSIGN(Surface);
 };
 
diff --git a/components/password_manager/core/browser/password_bubble_experiment.cc b/components/password_manager/core/browser/password_bubble_experiment.cc
index aeff633e..37a354d 100644
--- a/components/password_manager/core/browser/password_bubble_experiment.cc
+++ b/components/password_manager/core/browser/password_bubble_experiment.cc
@@ -28,6 +28,9 @@
 
   registry->RegisterIntegerPref(
       password_manager::prefs::kNumberSignInPasswordPromoShown, 0);
+
+  registry->RegisterBooleanPref(
+      password_manager::prefs::kSignInPasswordPromoRevive, false);
 }
 
 int GetSmartBubbleDismissalThreshold() {
@@ -69,6 +72,13 @@
       sync_service->GetUserSettings()->IsFirstSetupComplete()) {
     return false;
   }
+  if (!prefs->GetBoolean(password_manager::prefs::kSignInPasswordPromoRevive)) {
+    // Reset the counters so that the promo is shown again.
+    prefs->SetBoolean(password_manager::prefs::kSignInPasswordPromoRevive,
+                      true);
+    prefs->ClearPref(password_manager::prefs::kWasSignInPasswordPromoClicked);
+    prefs->ClearPref(password_manager::prefs::kNumberSignInPasswordPromoShown);
+  }
   // Don't show the promo more than 3 times.
   constexpr int kThreshold = 3;
   return !prefs->GetBoolean(
diff --git a/components/password_manager/core/browser/password_bubble_experiment_unittest.cc b/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
index bbc8b6b..33c4dc39 100644
--- a/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
+++ b/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
@@ -86,6 +86,20 @@
   }
 }
 
+TEST_F(PasswordManagerPasswordBubbleExperimentTest, ReviveSignInPasswordPromo) {
+  sync_service()->SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
+  sync_service()->SetFirstSetupComplete(false);
+  sync_service()->SetTransportState(
+      syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION);
+  prefs()->SetBoolean(password_manager::prefs::kWasSignInPasswordPromoClicked,
+                      true);
+  prefs()->SetInteger(password_manager::prefs::kNumberSignInPasswordPromoShown,
+                      10);
+
+  // The state is to be reset.
+  EXPECT_TRUE(ShouldShowChromeSignInPasswordPromo(prefs(), sync_service()));
+}
+
 TEST_F(PasswordManagerPasswordBubbleExperimentTest, IsSmartLockUser) {
   constexpr struct {
     syncer::ModelType type;
diff --git a/components/password_manager/core/common/password_manager_pref_names.cc b/components/password_manager/core/common/password_manager_pref_names.cc
index 0d3ea4f..8a49e58 100644
--- a/components/password_manager/core/common/password_manager_pref_names.cc
+++ b/components/password_manager/core/common/password_manager_pref_names.cc
@@ -38,6 +38,9 @@
 const char kNumberSignInPasswordPromoShown[] =
     "profile.number_sign_in_password_promo_shown";
 
+const char kSignInPasswordPromoRevive[] =
+    "profile.sign_in_password_promo_revive";
+
 const char kSyncPasswordHash[] = "profile.sync_password_hash";
 
 const char kSyncPasswordLengthAndHashSalt[] =
diff --git a/components/password_manager/core/common/password_manager_pref_names.h b/components/password_manager/core/common/password_manager_pref_names.h
index f360324..acb72ff 100644
--- a/components/password_manager/core/common/password_manager_pref_names.h
+++ b/components/password_manager/core/common/password_manager_pref_names.h
@@ -63,6 +63,10 @@
 // Number of times the Chrome Sign in promo popped up.
 extern const char kNumberSignInPasswordPromoShown[];
 
+// True if the counters for the sign in promo were reset for M79.
+// Safe to remove for M82.
+extern const char kSignInPasswordPromoRevive[];
+
 // String that represents the sync password hash.
 extern const char kSyncPasswordHash[];
 
diff --git a/components/policy/core/browser/url_blacklist_manager.cc b/components/policy/core/browser/url_blacklist_manager.cc
index 81ad10e..4ab74723 100644
--- a/components/policy/core/browser/url_blacklist_manager.cc
+++ b/components/policy/core/browser/url_blacklist_manager.cc
@@ -46,6 +46,10 @@
 
 namespace policy {
 
+using url_util::CreateConditionSet;
+using url_util::FilterComponents;
+using url_util::FilterToComponents;
+
 namespace {
 
 // List of schemes of URLs that should not be blocked by the "*" wildcard in
@@ -65,12 +69,6 @@
   "chrome-search",
 };
 
-const char kDevToolsLegacyScheme[] = "chrome-devtools";
-const char kDevToolsScheme[] = "devtools";
-
-// Maximum filters per policy. Filters over this index are ignored.
-const size_t kMaxFiltersPerPolicy = 1000;
-
 // Returns a blacklist based on the given |block| and |allow| pattern lists.
 std::unique_ptr<URLBlacklist> BuildBlacklist(const base::ListValue* block,
                                              const base::ListValue* allow) {
@@ -80,54 +78,6 @@
   return blacklist;
 }
 
-// Tokenise the parameter |query| and add appropriate query element matcher
-// conditions to the |query_conditions|.
-void ProcessQueryToConditions(
-    url_matcher::URLMatcherConditionFactory* condition_factory,
-    const std::string& query,
-    bool allow,
-    std::set<URLQueryElementMatcherCondition>* query_conditions) {
-  url::Component query_left = url::MakeRange(0, query.length());
-  url::Component key;
-  url::Component value;
-  // Depending on the filter type being black-list or white-list, the matcher
-  // choose any or every match. The idea is a URL should be black-listed if
-  // there is any occurrence of the key value pair. It should be white-listed
-  // only if every occurrence of the key is followed by the value. This avoids
-  // situations such as a user appending a white-listed video parameter in the
-  // end of the query and watching a video of their choice (the last parameter
-  // is ignored by some web servers like youtube's).
-  URLQueryElementMatcherCondition::Type match_type =
-      allow ? URLQueryElementMatcherCondition::MATCH_ALL
-            : URLQueryElementMatcherCondition::MATCH_ANY;
-
-  while (ExtractQueryKeyValue(query.data(), &query_left, &key, &value)) {
-    URLQueryElementMatcherCondition::QueryElementType query_element_type =
-        value.len ? URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE
-                  : URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY;
-    URLQueryElementMatcherCondition::QueryValueMatchType query_value_match_type;
-    if (!value.len && key.len && query[key.end() - 1] == '*') {
-      --key.len;
-      query_value_match_type =
-          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX;
-    } else if (value.len && query[value.end() - 1] == '*') {
-      --value.len;
-      query_value_match_type =
-          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX;
-    } else {
-      query_value_match_type =
-          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT;
-    }
-    query_conditions->insert(
-        URLQueryElementMatcherCondition(query.substr(key.begin, key.len),
-                                        query.substr(value.begin, value.len),
-                                        query_value_match_type,
-                                        query_element_type,
-                                        match_type,
-                                        condition_factory));
-  }
-}
-
 bool BypassBlacklistWildcardForURL(const GURL& url) {
   const std::string& scheme = url.scheme();
   for (size_t i = 0; i < base::size(kBypassBlacklistWildcardForSchemes); ++i) {
@@ -139,74 +89,16 @@
 
 }  // namespace
 
-struct URLBlacklist::FilterComponents {
-  FilterComponents() : port(0), match_subdomains(true), allow(true) {}
-  ~FilterComponents() = default;
-  FilterComponents(const FilterComponents&) = delete;
-  FilterComponents(FilterComponents&&) = default;
-  FilterComponents& operator=(const FilterComponents&) = delete;
-  FilterComponents& operator=(FilterComponents&&) = default;
-
-  // Returns true if |this| represents the "*" filter in the blacklist.
-  bool IsBlacklistWildcard() const {
-    return !allow && host.empty() && scheme.empty() && path.empty() &&
-           query.empty() && port == 0 && number_of_key_value_pairs == 0 &&
-           match_subdomains;
-  }
-
-  std::string scheme;
-  std::string host;
-  uint16_t port;
-  std::string path;
-  std::string query;
-  int number_of_key_value_pairs;
-  bool match_subdomains;
-  bool allow;
-};
-
 URLBlacklist::URLBlacklist() : id_(0), url_matcher_(new URLMatcher) {}
 
 URLBlacklist::~URLBlacklist() {}
 
-void URLBlacklist::AddFilters(bool allow, const base::ListValue* list) {
-  URLMatcherConditionSet::Vector all_conditions;
-  size_t size = std::min(kMaxFiltersPerPolicy, list->GetSize());
-  std::string pattern;
-  scoped_refptr<URLMatcherConditionSet> condition_set;
-  for (size_t i = 0; i < size; ++i) {
-    bool success = list->GetString(i, &pattern);
-    DCHECK(success);
-    FilterComponents components;
-    components.allow = allow;
-    if (!FilterToComponents(pattern,
-                            &components.scheme,
-                            &components.host,
-                            &components.match_subdomains,
-                            &components.port,
-                            &components.path,
-                            &components.query)) {
-      LOG(ERROR) << "Invalid pattern " << pattern;
-      continue;
-    }
-
-    condition_set = CreateConditionSet(
-        url_matcher_.get(), ++id_, components.scheme, components.host,
-        components.match_subdomains, components.port, components.path,
-        components.query, allow);
-    components.number_of_key_value_pairs =
-        condition_set->query_conditions().size();
-    all_conditions.push_back(std::move(condition_set));
-    filters_[id_] = std::move(components);
-  }
-  url_matcher_->AddConditionSets(all_conditions);
-}
-
 void URLBlacklist::Block(const base::ListValue* filters) {
-  AddFilters(false, filters);
+  url_util::AddFilters(url_matcher_.get(), false, &id_, filters, &filters_);
 }
 
 void URLBlacklist::Allow(const base::ListValue* filters) {
-  AddFilters(true, filters);
+  url_util::AddFilters(url_matcher_.get(), true, &id_, filters, &filters_);
 }
 
 bool URLBlacklist::IsURLBlocked(const GURL& url) const {
@@ -248,167 +140,6 @@
 }
 
 // static
-bool URLBlacklist::FilterToComponents(const std::string& filter,
-                                      std::string* scheme,
-                                      std::string* host,
-                                      bool* match_subdomains,
-                                      uint16_t* port,
-                                      std::string* path,
-                                      std::string* query) {
-  DCHECK(scheme);
-  DCHECK(host);
-  DCHECK(match_subdomains);
-  DCHECK(port);
-  DCHECK(path);
-  DCHECK(query);
-  url::Parsed parsed;
-  std::string lc_filter = base::ToLowerASCII(filter);
-  const std::string url_scheme = url_formatter::SegmentURL(filter, &parsed);
-
-  // This is for backward compatibility between 'chrome-devtools' and 'devtools'
-  // schemes. url_formatter::SegmentURL will return 'devtools' if the filter's
-  // scheme is the deprecated 'chrome-devtools'. To comply with that
-  // transformation, since both schemes are equivalent, if the filter's scheme
-  // was 'chrome-devtools', it should be replaced by 'devtools'.
-  if (url_scheme == kDevToolsScheme &&
-      lc_filter.find(kDevToolsLegacyScheme) == 0) {
-    lc_filter.replace(0, 15, kDevToolsScheme);
-  }
-
-  // Check if it's a scheme wildcard pattern. We support both versions
-  // (scheme:* and scheme://*) the later being consistent with old filter
-  // definitions.
-  if (lc_filter == url_scheme + ":*" || lc_filter == url_scheme + "://*") {
-    scheme->assign(url_scheme);
-    host->clear();
-    *match_subdomains = true;
-    *port = 0;
-    path->clear();
-    query->clear();
-    return true;
-  }
-
-  if (url_scheme == url::kFileScheme) {
-    base::FilePath file_path;
-    if (!net::FileURLToFilePath(GURL(filter), &file_path))
-      return false;
-
-    *scheme = url::kFileScheme;
-    host->clear();
-    *match_subdomains = true;
-    *port = 0;
-    *path = file_path.AsUTF8Unsafe();
-#if defined(FILE_PATH_USES_WIN_SEPARATORS)
-    // Separators have to be canonicalized on Windows.
-    std::replace(path->begin(), path->end(), '\\', '/');
-    *path = "/" + *path;
-#endif
-    return true;
-  }
-
-  // According to documentation host can't be empty.
-  if (!parsed.host.is_nonempty())
-    return false;
-
-  if (parsed.scheme.is_nonempty())
-    scheme->assign(url_scheme);
-  else
-    scheme->clear();
-
-  host->assign(filter, parsed.host.begin, parsed.host.len);
-  *host = base::ToLowerASCII(*host);
-  // Special '*' host, matches all hosts.
-  if (*host == "*") {
-    host->clear();
-    *match_subdomains = true;
-  } else if (host->at(0) == '.') {
-    // A leading dot in the pattern syntax means that we don't want to match
-    // subdomains.
-    host->erase(0, 1);
-    *match_subdomains = false;
-  } else {
-    url::RawCanonOutputT<char> output;
-    url::CanonHostInfo host_info;
-    url::CanonicalizeHostVerbose(filter.c_str(), parsed.host, &output,
-                                 &host_info);
-    if (host_info.family == url::CanonHostInfo::NEUTRAL) {
-      // We want to match subdomains. Add a dot in front to make sure we only
-      // match at domain component boundaries.
-      *host = "." + *host;
-      *match_subdomains = true;
-    } else {
-      *match_subdomains = false;
-    }
-  }
-
-  if (parsed.port.is_nonempty()) {
-    int int_port;
-    if (!base::StringToInt(filter.substr(parsed.port.begin, parsed.port.len),
-                           &int_port)) {
-      return false;
-    }
-    if (int_port <= 0 || int_port > std::numeric_limits<uint16_t>::max())
-      return false;
-    *port = int_port;
-  } else {
-    // Match any port.
-    *port = 0;
-  }
-
-  if (parsed.path.is_nonempty())
-    path->assign(filter, parsed.path.begin, parsed.path.len);
-  else
-    path->clear();
-
-  if (parsed.query.is_nonempty())
-    query->assign(filter, parsed.query.begin, parsed.query.len);
-  else
-    query->clear();
-
-  return true;
-}
-
-// static
-scoped_refptr<URLMatcherConditionSet> URLBlacklist::CreateConditionSet(
-    URLMatcher* url_matcher,
-    int id,
-    const std::string& scheme,
-    const std::string& host,
-    bool match_subdomains,
-    uint16_t port,
-    const std::string& path,
-    const std::string& query,
-    bool allow) {
-  URLMatcherConditionFactory* condition_factory =
-      url_matcher->condition_factory();
-  std::set<URLMatcherCondition> conditions;
-  conditions.insert(match_subdomains ?
-      condition_factory->CreateHostSuffixPathPrefixCondition(host, path) :
-      condition_factory->CreateHostEqualsPathPrefixCondition(host, path));
-
-  std::set<URLQueryElementMatcherCondition> query_conditions;
-  if (!query.empty()) {
-    ProcessQueryToConditions(
-        condition_factory, query, allow, &query_conditions);
-  }
-
-  std::unique_ptr<URLMatcherSchemeFilter> scheme_filter;
-  if (!scheme.empty())
-    scheme_filter.reset(new URLMatcherSchemeFilter(scheme));
-
-  std::unique_ptr<URLMatcherPortFilter> port_filter;
-  if (port != 0) {
-    std::vector<URLMatcherPortFilter::Range> ranges;
-    ranges.push_back(URLMatcherPortFilter::CreateRange(port));
-    port_filter.reset(new URLMatcherPortFilter(ranges));
-  }
-
-  return base::MakeRefCounted<URLMatcherConditionSet>(
-      id, conditions, query_conditions, std::move(scheme_filter),
-      std::move(port_filter));
-}
-
-// static
 bool URLBlacklist::FilterTakesPrecedence(const FilterComponents& lhs,
                                          const FilterComponents& rhs) {
   // The "*" wildcard is the lowest priority filter.
diff --git a/components/policy/core/browser/url_blacklist_manager.h b/components/policy/core/browser/url_blacklist_manager.h
index cfe75c6..66d3c4e 100644
--- a/components/policy/core/browser/url_blacklist_manager.h
+++ b/components/policy/core/browser/url_blacklist_manager.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "components/policy/core/browser/url_util.h"
 #include "components/policy/policy_export.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/url_matcher/url_matcher.h"
@@ -50,9 +51,6 @@
   URLBlacklist();
   virtual ~URLBlacklist();
 
-  // Allows or blocks URLs matching one of the filters, depending on |allow|.
-  void AddFilters(bool allow, const base::ListValue* filters);
-
   // URLs matching one of the |filters| will be blocked. The filter format is
   // documented at
   // http://www.chromium.org/administrators/url-blacklist-filter-format.
@@ -70,51 +68,15 @@
   // Returns the number of items in the list.
   size_t Size() const;
 
-  // Splits a URL filter into its components. A GURL isn't used because these
-  // can be invalid URLs e.g. "google.com".
-  // Returns false if the URL couldn't be parsed. In case false is returned,
-  // the values of output parameters are undefined.
-  // The |host| is preprocessed so it can be passed to URLMatcher for the
-  // appropriate condition.
-  // The optional username and password are ignored.
-  // |match_subdomains| specifies whether the filter should include subdomains
-  // of the hostname (if it is one.)
-  // |port| is 0 if none is explicitly defined.
-  // |path| does not include query parameters.
-  // |query| contains the query parameters ('?' not included).
-  // All arguments are mandatory.
-  static bool FilterToComponents(const std::string& filter,
-                                 std::string* scheme,
-                                 std::string* host,
-                                 bool* match_subdomains,
-                                 uint16_t* port,
-                                 std::string* path,
-                                 std::string* query);
-
-  // Creates a condition set that can be used with the |url_matcher|. |id| needs
-  // to be a unique number that will be returned by the |url_matcher| if the URL
-  // matches that condition set. |allow| indicates if it is a white-list (true)
-  // or black-list (false) filter.
-  static scoped_refptr<url_matcher::URLMatcherConditionSet> CreateConditionSet(
-      url_matcher::URLMatcher* url_matcher,
-      url_matcher::URLMatcherConditionSet::ID id,
-      const std::string& scheme,
-      const std::string& host,
-      bool match_subdomains,
-      uint16_t port,
-      const std::string& path,
-      const std::string& query,
-      bool allow);
-
  private:
-  struct FilterComponents;
 
   // Returns true if |lhs| takes precedence over |rhs|.
-  static bool FilterTakesPrecedence(const FilterComponents& lhs,
-                                    const FilterComponents& rhs);
+  static bool FilterTakesPrecedence(const url_util::FilterComponents& lhs,
+                                    const url_util::FilterComponents& rhs);
 
   url_matcher::URLMatcherConditionSet::ID id_;
-  std::map<url_matcher::URLMatcherConditionSet::ID, FilterComponents> filters_;
+  std::map<url_matcher::URLMatcherConditionSet::ID, url_util::FilterComponents>
+      filters_;
   std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
 
   DISALLOW_COPY_AND_ASSIGN(URLBlacklist);
diff --git a/components/policy/core/browser/url_blacklist_manager_unittest.cc b/components/policy/core/browser/url_blacklist_manager_unittest.cc
index 183e39c..68f820db 100644
--- a/components/policy/core/browser/url_blacklist_manager_unittest.cc
+++ b/components/policy/core/browser/url_blacklist_manager_unittest.cc
@@ -85,69 +85,6 @@
   base::test::TaskEnvironment task_environment_;
 };
 
-// Parameters for the FilterToComponents test.
-struct FilterTestParams {
- public:
-  FilterTestParams(const std::string& filter,
-                   const std::string& scheme,
-                   const std::string& host,
-                   bool match_subdomains,
-                   uint16_t port,
-                   const std::string& path)
-      : filter_(filter),
-        scheme_(scheme),
-        host_(host),
-        match_subdomains_(match_subdomains),
-        port_(port),
-        path_(path) {}
-
-  FilterTestParams(const FilterTestParams& params)
-      : filter_(params.filter_), scheme_(params.scheme_), host_(params.host_),
-        match_subdomains_(params.match_subdomains_), port_(params.port_),
-        path_(params.path_) {}
-
-  const FilterTestParams& operator=(const FilterTestParams& params) {
-    filter_ = params.filter_;
-    scheme_ = params.scheme_;
-    host_ = params.host_;
-    match_subdomains_ = params.match_subdomains_;
-    port_ = params.port_;
-    path_ = params.path_;
-    return *this;
-  }
-
-  const std::string& filter() const { return filter_; }
-  const std::string& scheme() const { return scheme_; }
-  const std::string& host() const { return host_; }
-  bool match_subdomains() const { return match_subdomains_; }
-  uint16_t port() const { return port_; }
-  const std::string& path() const { return path_; }
-
- private:
-  std::string filter_;
-  std::string scheme_;
-  std::string host_;
-  bool match_subdomains_;
-  uint16_t port_;
-  std::string path_;
-};
-
-// Make Valgrind happy. Without this function, a generic one will print the
-// raw bytes in FilterTestParams, which due to some likely padding will access
-// uninitialized memory.
-void PrintTo(const FilterTestParams& params, std::ostream* os) {
-  *os << params.filter();
-}
-
-class URLBlacklistFilterToComponentsTest
-    : public testing::TestWithParam<FilterTestParams> {
- public:
-  URLBlacklistFilterToComponentsTest() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(URLBlacklistFilterToComponentsTest);
-};
-
 }  // namespace
 
 // Returns whether |url| matches the |pattern|.
@@ -182,28 +119,6 @@
   return blacklist.GetURLBlacklistState(GURL(url));
 }
 
-TEST_P(URLBlacklistFilterToComponentsTest, FilterToComponents) {
-  std::string scheme;
-  std::string host;
-  bool match_subdomains = true;
-  uint16_t port = 42;
-  std::string path;
-  std::string query;
-
-  URLBlacklist::FilterToComponents(GetParam().filter(),
-                                   &scheme,
-                                   &host,
-                                   &match_subdomains,
-                                   &port,
-                                   &path,
-                                   &query);
-  EXPECT_EQ(GetParam().scheme(), scheme);
-  EXPECT_EQ(GetParam().host(), host);
-  EXPECT_EQ(GetParam().match_subdomains(), match_subdomains);
-  EXPECT_EQ(GetParam().port(), port);
-  EXPECT_EQ(GetParam().path(), path);
-}
-
 TEST_F(URLBlacklistManagerTest, LoadBlacklistOnCreate) {
   auto list = std::make_unique<base::ListValue>();
   list->AppendString("example.com");
@@ -238,89 +153,6 @@
   EXPECT_EQ(1, blacklist_manager_->update_called());
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    URLBlacklistFilterToComponentsTestInstance,
-    URLBlacklistFilterToComponentsTest,
-    testing::Values(
-        FilterTestParams("google.com",
-                         std::string(),
-                         ".google.com",
-                         true,
-                         0u,
-                         std::string()),
-        FilterTestParams(".google.com",
-                         std::string(),
-                         "google.com",
-                         false,
-                         0u,
-                         std::string()),
-        FilterTestParams("http://google.com",
-                         "http",
-                         ".google.com",
-                         true,
-                         0u,
-                         std::string()),
-        FilterTestParams("google.com/",
-                         std::string(),
-                         ".google.com",
-                         true,
-                         0u,
-                         "/"),
-        FilterTestParams("http://google.com:8080/whatever",
-                         "http",
-                         ".google.com",
-                         true,
-                         8080u,
-                         "/whatever"),
-        FilterTestParams("http://user:pass@google.com:8080/whatever",
-                         "http",
-                         ".google.com",
-                         true,
-                         8080u,
-                         "/whatever"),
-        FilterTestParams("123.123.123.123",
-                         std::string(),
-                         "123.123.123.123",
-                         false,
-                         0u,
-                         std::string()),
-        FilterTestParams("https://123.123.123.123",
-                         "https",
-                         "123.123.123.123",
-                         false,
-                         0u,
-                         std::string()),
-        FilterTestParams("123.123.123.123/",
-                         std::string(),
-                         "123.123.123.123",
-                         false,
-                         0u,
-                         "/"),
-        FilterTestParams("http://123.123.123.123:123/whatever",
-                         "http",
-                         "123.123.123.123",
-                         false,
-                         123u,
-                         "/whatever"),
-        FilterTestParams("*",
-                         std::string(),
-                         std::string(),
-                         true,
-                         0u,
-                         std::string()),
-        FilterTestParams("ftp://*",
-                         "ftp",
-                         std::string(),
-                         true,
-                         0u,
-                         std::string()),
-        FilterTestParams("http://*/whatever",
-                         "http",
-                         std::string(),
-                         true,
-                         0u,
-                         "/whatever")));
-
 TEST_F(URLBlacklistManagerTest, Filtering) {
   URLBlacklist blacklist;
 
diff --git a/components/policy/core/browser/url_util.cc b/components/policy/core/browser/url_util.cc
index 5d846f2..85b5691c 100644
--- a/components/policy/core/browser/url_util.cc
+++ b/components/policy/core/browser/url_util.cc
@@ -6,16 +6,28 @@
 
 #include <string>
 
+#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_number_conversions.h"
 #include "components/google/core/common/google_util.h"
 #include "components/url_formatter/url_fixer.h"
+#include "components/url_matcher/url_matcher.h"
 #include "net/base/escape.h"
+#include "net/base/filename_util.h"
 #include "net/base/url_util.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
 
+using url_matcher::URLMatcher;
+using url_matcher::URLMatcherCondition;
+using url_matcher::URLMatcherConditionFactory;
+using url_matcher::URLMatcherConditionSet;
+using url_matcher::URLMatcherPortFilter;
+using url_matcher::URLMatcherSchemeFilter;
+using url_matcher::URLQueryElementMatcherCondition;
+
 namespace policy {
 namespace url_util {
 
@@ -39,12 +51,61 @@
 const char kGoogleTranslateSubdomain[] = "translate.";
 const char kAlternateGoogleTranslateHost[] = "translate.googleusercontent.com";
 
+const char kDevToolsLegacyScheme[] = "chrome-devtools";
+const char kDevToolsScheme[] = "devtools";
+
+// Maximum filters per policy. Filters over this index are ignored.
+const size_t kMaxFiltersPerPolicy = 1000;
+
 // Returns a full URL using either "http" or "https" as the scheme.
 GURL BuildURL(bool is_https, const std::string& host_and_path) {
   std::string scheme = is_https ? url::kHttpsScheme : url::kHttpScheme;
   return GURL(scheme + "://" + host_and_path);
 }
 
+void ProcessQueryToConditions(
+    url_matcher::URLMatcherConditionFactory* condition_factory,
+    const std::string& query,
+    bool allow,
+    std::set<URLQueryElementMatcherCondition>* query_conditions) {
+  url::Component query_left = url::MakeRange(0, query.length());
+  url::Component key;
+  url::Component value;
+  // Depending on the filter type being black-list or white-list, the matcher
+  // choose any or every match. The idea is a URL should be black-listed if
+  // there is any occurrence of the key value pair. It should be white-listed
+  // only if every occurrence of the key is followed by the value. This avoids
+  // situations such as a user appending a white-listed video parameter in the
+  // end of the query and watching a video of their choice (the last parameter
+  // is ignored by some web servers like youtube's).
+  URLQueryElementMatcherCondition::Type match_type =
+      allow ? URLQueryElementMatcherCondition::MATCH_ALL
+            : URLQueryElementMatcherCondition::MATCH_ANY;
+
+  while (ExtractQueryKeyValue(query.data(), &query_left, &key, &value)) {
+    URLQueryElementMatcherCondition::QueryElementType query_element_type =
+        value.len ? URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE
+                  : URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY;
+    URLQueryElementMatcherCondition::QueryValueMatchType query_value_match_type;
+    if (!value.len && key.len && query[key.end() - 1] == '*') {
+      --key.len;
+      query_value_match_type =
+          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX;
+    } else if (value.len && query[value.end() - 1] == '*') {
+      --value.len;
+      query_value_match_type =
+          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX;
+    } else {
+      query_value_match_type =
+          URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT;
+    }
+    query_conditions->insert(URLQueryElementMatcherCondition(
+        query.substr(key.begin, key.len), query.substr(value.begin, value.len),
+        query_value_match_type, query_element_type, match_type,
+        condition_factory));
+  }
+}
+
 // Helper class for testing the URL against precompiled regexes. This is a
 // singleton so the cached regexes are only created once.
 class EmbeddedURLExtractor {
@@ -170,5 +231,217 @@
   return EmbeddedURLExtractor::GetInstance()->GetEmbeddedURL(url);
 }
 
+FilterComponents::FilterComponents()
+    : port(0), match_subdomains(true), allow(true) {}
+FilterComponents::~FilterComponents() = default;
+FilterComponents::FilterComponents(FilterComponents&&) = default;
+
+bool FilterComponents::IsBlacklistWildcard() const {
+  return !allow && host.empty() && scheme.empty() && path.empty() &&
+         query.empty() && port == 0 && number_of_key_value_pairs == 0 &&
+         match_subdomains;
+}
+
+scoped_refptr<URLMatcherConditionSet> CreateConditionSet(
+    URLMatcher* url_matcher,
+    int id,
+    const std::string& scheme,
+    const std::string& host,
+    bool match_subdomains,
+    uint16_t port,
+    const std::string& path,
+    const std::string& query,
+    bool allow) {
+  URLMatcherConditionFactory* condition_factory =
+      url_matcher->condition_factory();
+  std::set<URLMatcherCondition> conditions;
+  conditions.insert(
+      match_subdomains
+          ? condition_factory->CreateHostSuffixPathPrefixCondition(host, path)
+          : condition_factory->CreateHostEqualsPathPrefixCondition(host, path));
+
+  std::set<URLQueryElementMatcherCondition> query_conditions;
+  if (!query.empty()) {
+    ProcessQueryToConditions(condition_factory, query, allow,
+                             &query_conditions);
+  }
+
+  std::unique_ptr<URLMatcherSchemeFilter> scheme_filter;
+  if (!scheme.empty())
+    scheme_filter.reset(new URLMatcherSchemeFilter(scheme));
+
+  std::unique_ptr<URLMatcherPortFilter> port_filter;
+  if (port != 0) {
+    std::vector<URLMatcherPortFilter::Range> ranges;
+    ranges.push_back(URLMatcherPortFilter::CreateRange(port));
+    port_filter.reset(new URLMatcherPortFilter(ranges));
+  }
+
+  return base::MakeRefCounted<URLMatcherConditionSet>(
+      id, conditions, query_conditions, std::move(scheme_filter),
+      std::move(port_filter));
+}
+
+bool FilterToComponents(const std::string& filter,
+                        std::string* scheme,
+                        std::string* host,
+                        bool* match_subdomains,
+                        uint16_t* port,
+                        std::string* path,
+                        std::string* query) {
+  DCHECK(scheme);
+  DCHECK(host);
+  DCHECK(match_subdomains);
+  DCHECK(port);
+  DCHECK(path);
+  DCHECK(query);
+  url::Parsed parsed;
+  std::string lc_filter = base::ToLowerASCII(filter);
+  const std::string url_scheme = url_formatter::SegmentURL(filter, &parsed);
+
+  // This is for backward compatibility between 'chrome-devtools' and 'devtools'
+  // schemes. url_formatter::SegmentURL will return 'devtools' if the filter's
+  // scheme is the deprecated 'chrome-devtools'. To comply with that
+  // transformation, since both schemes are equivalent, if the filter's scheme
+  // was 'chrome-devtools', it should be replaced by 'devtools'.
+  if (url_scheme == kDevToolsScheme &&
+      lc_filter.find(kDevToolsLegacyScheme) == 0) {
+    lc_filter.replace(0, 15, kDevToolsScheme);
+  }
+
+  // Check if it's a scheme wildcard pattern. We support both versions
+  // (scheme:* and scheme://*) the later being consistent with old filter
+  // definitions.
+  if (lc_filter == url_scheme + ":*" || lc_filter == url_scheme + "://*") {
+    scheme->assign(url_scheme);
+    host->clear();
+    *match_subdomains = true;
+    *port = 0;
+    path->clear();
+    query->clear();
+    return true;
+  }
+
+  if (url_scheme == url::kFileScheme) {
+    base::FilePath file_path;
+    if (!net::FileURLToFilePath(GURL(filter), &file_path))
+      return false;
+
+    *scheme = url::kFileScheme;
+    host->clear();
+    *match_subdomains = true;
+    *port = 0;
+    *path = file_path.AsUTF8Unsafe();
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+    // Separators have to be canonicalized on Windows.
+    std::replace(path->begin(), path->end(), '\\', '/');
+    *path = "/" + *path;
+#endif
+    return true;
+  }
+
+  // According to documentation host can't be empty.
+  if (!parsed.host.is_nonempty())
+    return false;
+
+  if (parsed.scheme.is_nonempty())
+    scheme->assign(url_scheme);
+  else
+    scheme->clear();
+
+  host->assign(filter, parsed.host.begin, parsed.host.len);
+  *host = base::ToLowerASCII(*host);
+  // Special '*' host, matches all hosts.
+  if (*host == "*") {
+    host->clear();
+    *match_subdomains = true;
+  } else if (host->at(0) == '.') {
+    // A leading dot in the pattern syntax means that we don't want to match
+    // subdomains.
+    host->erase(0, 1);
+    *match_subdomains = false;
+  } else {
+    url::RawCanonOutputT<char> output;
+    url::CanonHostInfo host_info;
+    url::CanonicalizeHostVerbose(filter.c_str(), parsed.host, &output,
+                                 &host_info);
+    if (host_info.family == url::CanonHostInfo::NEUTRAL) {
+      // We want to match subdomains. Add a dot in front to make sure we only
+      // match at domain component boundaries.
+      *host = "." + *host;
+      *match_subdomains = true;
+    } else {
+      *match_subdomains = false;
+    }
+  }
+
+  if (parsed.port.is_nonempty()) {
+    int int_port;
+    if (!base::StringToInt(filter.substr(parsed.port.begin, parsed.port.len),
+                           &int_port)) {
+      return false;
+    }
+    if (int_port <= 0 || int_port > std::numeric_limits<uint16_t>::max())
+      return false;
+    *port = int_port;
+  } else {
+    // Match any port.
+    *port = 0;
+  }
+
+  if (parsed.path.is_nonempty())
+    path->assign(filter, parsed.path.begin, parsed.path.len);
+  else
+    path->clear();
+
+  if (parsed.query.is_nonempty())
+    query->assign(filter, parsed.query.begin, parsed.query.len);
+  else
+    query->clear();
+
+  return true;
+}
+
+POLICY_EXPORT void AddFilters(URLMatcher* matcher,
+                              bool allow,
+                              URLMatcherConditionSet::ID* id,
+                              const base::ListValue* patterns,
+                              std::map<url_matcher::URLMatcherConditionSet::ID,
+                                       url_util::FilterComponents>* filters) {
+  URLMatcherConditionSet::Vector all_conditions;
+  size_t size = std::min(kMaxFiltersPerPolicy, patterns->GetSize());
+  std::string pattern;
+  scoped_refptr<URLMatcherConditionSet> condition_set;
+  for (size_t i = 0; i < size; ++i) {
+    bool success = patterns->GetString(i, &pattern);
+    DCHECK(success);
+    FilterComponents components;
+    components.allow = allow;
+    if (!FilterToComponents(pattern, &components.scheme, &components.host,
+                            &components.match_subdomains, &components.port,
+                            &components.path, &components.query)) {
+      LOG(ERROR) << "Invalid pattern " << pattern;
+      continue;
+    }
+    condition_set =
+        CreateConditionSet(matcher, ++(*id), components.scheme, components.host,
+                           components.match_subdomains, components.port,
+                           components.path, components.query, allow);
+    if (filters) {
+      components.number_of_key_value_pairs =
+          condition_set->query_conditions().size();
+      (*filters)[*id] = std::move(components);
+    }
+    all_conditions.push_back(std::move(condition_set));
+  }
+  matcher->AddConditionSets(all_conditions);
+}
+
+POLICY_EXPORT void AddAllowFilters(url_matcher::URLMatcher* matcher,
+                                   const base::ListValue* patterns) {
+  url_matcher::URLMatcherConditionSet::ID id(0);
+  AddFilters(matcher, true, &id, patterns);
+}
+
 }  // namespace url_util
 }  // namespace policy
diff --git a/components/policy/core/browser/url_util.h b/components/policy/core/browser/url_util.h
index 19d9d16..3a7b7f4 100644
--- a/components/policy/core/browser/url_util.h
+++ b/components/policy/core/browser/url_util.h
@@ -5,7 +5,10 @@
 #ifndef COMPONENTS_POLICY_CORE_BROWSER_URL_UTIL_H_
 #define COMPONENTS_POLICY_CORE_BROWSER_URL_UTIL_H_
 
+#include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 #include "components/policy/policy_export.h"
+#include "components/url_matcher/url_matcher.h"
 
 class GURL;
 
@@ -20,6 +23,76 @@
 // a known format.
 POLICY_EXPORT GURL GetEmbeddedURL(const GURL& url);
 
+struct FilterComponents {
+  FilterComponents();
+  FilterComponents(const FilterComponents&) = delete;
+  FilterComponents(FilterComponents&&);
+  FilterComponents& operator=(const FilterComponents&) = delete;
+  FilterComponents& operator=(FilterComponents&&) = default;
+
+  ~FilterComponents();
+
+  // Returns true if |this| represents the "*" filter in the blacklist.
+  bool IsBlacklistWildcard() const;
+
+  std::string scheme;
+  std::string host;
+  uint16_t port;
+  std::string path;
+  std::string query;
+  int number_of_key_value_pairs;
+  bool match_subdomains;
+  bool allow;
+};
+
+// Creates a condition set that can be used with the |url_matcher|. |id| needs
+// to be a unique number that will be returned by the |url_matcher| if the URL
+// matches that condition set. |allow| indicates if it is a white-list (true)
+// or black-list (false) filter.
+POLICY_EXPORT scoped_refptr<url_matcher::URLMatcherConditionSet>
+CreateConditionSet(url_matcher::URLMatcher* url_matcher,
+                   url_matcher::URLMatcherConditionSet::ID id,
+                   const std::string& scheme,
+                   const std::string& host,
+                   bool match_subdomains,
+                   uint16_t port,
+                   const std::string& path,
+                   const std::string& query,
+                   bool allow);
+
+// Splits a URL filter into its components. A GURL isn't used because these
+// can be invalid URLs e.g. "google.com".
+// Returns false if the URL couldn't be parsed. In case false is returned,
+// the values of output parameters are undefined.
+// The |host| is preprocessed so it can be passed to URLMatcher for the
+// appropriate condition.
+// The optional username and password are ignored.
+// |match_subdomains| specifies whether the filter should include subdomains
+// of the hostname (if it is one.)
+// |port| is 0 if none is explicitly defined.
+// |path| does not include query parameters.
+// |query| contains the query parameters ('?' not included).
+// All arguments are mandatory.
+POLICY_EXPORT bool FilterToComponents(const std::string& filter,
+                                      std::string* scheme,
+                                      std::string* host,
+                                      bool* match_subdomains,
+                                      uint16_t* port,
+                                      std::string* path,
+                                      std::string* query);
+
+// Adds the filters in |list| to |url_matcher| as a ConditionSet::Vector.
+POLICY_EXPORT void AddFilters(
+    url_matcher::URLMatcher* matcher,
+    bool allow,
+    url_matcher::URLMatcherConditionSet::ID* id,
+    const base::ListValue* patterns,
+    std::map<url_matcher::URLMatcherConditionSet::ID,
+             url_util::FilterComponents>* filters = nullptr);
+
+POLICY_EXPORT void AddAllowFilters(url_matcher::URLMatcher* matcher,
+                                   const base::ListValue* patterns);
+
 }  // namespace url_util
 }  // namespace policy
 
diff --git a/components/policy/core/browser/url_util_unittest.cc b/components/policy/core/browser/url_util_unittest.cc
index 1dca89a3..d055ec7d 100644
--- a/components/policy/core/browser/url_util_unittest.cc
+++ b/components/policy/core/browser/url_util_unittest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "components/policy/core/browser/url_util.h"
+#include <memory>
 
+#include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -16,6 +18,95 @@
   return policy::url_util::GetEmbeddedURL(GURL(url));
 }
 
+// Parameters for the FilterToComponents test.
+struct FilterTestParams {
+ public:
+  FilterTestParams(const std::string& filter,
+                   const std::string& scheme,
+                   const std::string& host,
+                   bool match_subdomains,
+                   uint16_t port,
+                   const std::string& path)
+      : filter_(filter),
+        scheme_(scheme),
+        host_(host),
+        match_subdomains_(match_subdomains),
+        port_(port),
+        path_(path) {}
+
+  FilterTestParams(const FilterTestParams& params)
+      : filter_(params.filter_),
+        scheme_(params.scheme_),
+        host_(params.host_),
+        match_subdomains_(params.match_subdomains_),
+        port_(params.port_),
+        path_(params.path_) {}
+
+  const FilterTestParams& operator=(const FilterTestParams& params) {
+    filter_ = params.filter_;
+    scheme_ = params.scheme_;
+    host_ = params.host_;
+    match_subdomains_ = params.match_subdomains_;
+    port_ = params.port_;
+    path_ = params.path_;
+    return *this;
+  }
+
+  const std::string& filter() const { return filter_; }
+  const std::string& scheme() const { return scheme_; }
+  const std::string& host() const { return host_; }
+  bool match_subdomains() const { return match_subdomains_; }
+  uint16_t port() const { return port_; }
+  const std::string& path() const { return path_; }
+
+ private:
+  std::string filter_;
+  std::string scheme_;
+  std::string host_;
+  bool match_subdomains_;
+  uint16_t port_;
+  std::string path_;
+};
+
+// Prints better debug information for Valgrind. Without this function, a
+// generic one will print the raw bytes in FilterTestParams, which due to some
+// likely padding will access uninitialized memory.
+void PrintTo(const FilterTestParams& params, std::ostream* os) {
+  *os << params.filter();
+}
+
+bool MatchFilters(const std::vector<const std::string>& patterns,
+                  const std::string& url) {
+  // Add the pattern to the matcher.
+  url_matcher::URLMatcher matcher;
+  auto list = std::make_unique<base::ListValue>();
+  for (const auto& pattern : patterns)
+    list->AppendString(pattern);
+  AddAllowFilters(&matcher, list.get());
+  return !matcher.MatchURL(GURL(url)).empty();
+}
+
+class FilterToComponentsTest : public testing::TestWithParam<FilterTestParams> {
+ public:
+  FilterToComponentsTest() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FilterToComponentsTest);
+};
+
+class OnlyWildcardTest
+    : public testing::TestWithParam<std::tuple<std::string /* scheme */,
+                                               std::string /* opt_host */,
+                                               std::string /* port */,
+                                               std::string /* path */,
+                                               std::string /* query*/>> {
+ public:
+  OnlyWildcardTest() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OnlyWildcardTest);
+};
+
 }  // namespace
 
 TEST(URLUtilTest, Normalize) {
@@ -231,5 +322,358 @@
             GetEmbeddedURL("https://translate.google.com/path?t=example.com"));
 }
 
+INSTANTIATE_TEST_SUITE_P(URLUtilTest,
+                         OnlyWildcardTest,
+                         testing::Combine(testing::Values("", "https://"),
+                                          testing::Values("", "dev."),
+                                          testing::Values("", ":1234"),
+                                          testing::Values("", "/path"),
+                                          testing::Values("", "?query")));
+
+TEST_P(OnlyWildcardTest, OnlyWildcard) {
+  // Check wildcard filter works on any permutations of format
+  // [scheme://][.]host[:port][/path][@query]
+  const std::string scheme = std::get<0>(GetParam());
+  const std::string opt_host = std::get<1>(GetParam());
+  const std::string port = std::get<2>(GetParam());
+  const std::string path = std::get<3>(GetParam());
+  const std::string query = std::get<4>(GetParam());
+  const std::string url =
+      scheme + opt_host + "google.com" + port + path + query;
+  EXPECT_TRUE(MatchFilters({"*"}, url));
+}
+
+TEST(URLUtilTest, SingleFilter) {
+  // Match domain and all subdomains, for any filtered scheme.
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://google.com"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://google.com/"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://google.com/whatever"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "https://google.com/"));
+  EXPECT_FALSE(MatchFilters({"google.com"}, "bogus://google.com/"));
+  EXPECT_FALSE(MatchFilters({"google.com"}, "http://notgoogle.com/"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://mail.google.com"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://x.mail.google.com"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "https://x.mail.google.com/"));
+  EXPECT_TRUE(MatchFilters({"google.com"}, "http://x.y.google.com/a/b"));
+  EXPECT_FALSE(MatchFilters({"google.com"}, "http://youtube.com/"));
+
+  // Filter only http, ftp and ws schemes.
+  EXPECT_TRUE(MatchFilters({"http://secure.com"}, "http://secure.com"));
+  EXPECT_TRUE(
+      MatchFilters({"http://secure.com"}, "http://secure.com/whatever"));
+  EXPECT_TRUE(MatchFilters({"ftp://secure.com"}, "ftp://secure.com/"));
+  EXPECT_TRUE(MatchFilters({"ws://secure.com"}, "ws://secure.com"));
+  EXPECT_FALSE(MatchFilters({"http://secure.com"}, "https://secure.com/"));
+  EXPECT_FALSE(MatchFilters({"ws://secure.com"}, "wss://secure.com"));
+  EXPECT_TRUE(MatchFilters({"http://secure.com"}, "http://www.secure.com"));
+  EXPECT_FALSE(MatchFilters({"http://secure.com"}, "https://www.secure.com"));
+  EXPECT_FALSE(MatchFilters({"ws://secure.com"}, "wss://www.secure.com"));
+
+  // Filter only a certain path prefix.
+  EXPECT_TRUE(MatchFilters({"path.to/ruin"}, "http://path.to/ruin"));
+  EXPECT_TRUE(MatchFilters({"path.to/ruin"}, "https://path.to/ruin"));
+  EXPECT_TRUE(MatchFilters({"path.to/ruin"}, "http://path.to/ruins"));
+  EXPECT_TRUE(MatchFilters({"path.to/ruin"}, "http://path.to/ruin/signup"));
+  EXPECT_TRUE(MatchFilters({"path.to/ruin"}, "http://www.path.to/ruin"));
+  EXPECT_FALSE(MatchFilters({"path.to/ruin"}, "http://path.to/fortune"));
+
+  // Filter only a certain path prefix and scheme.
+  EXPECT_TRUE(
+      MatchFilters({"https://s.aaa.com/path"}, "https://s.aaa.com/path"));
+  EXPECT_TRUE(
+      MatchFilters({"https://s.aaa.com/path"}, "https://s.aaa.com/path/bbb"));
+  EXPECT_FALSE(
+      MatchFilters({"https://s.aaa.com/path"}, "http://s.aaa.com/path"));
+  EXPECT_FALSE(
+      MatchFilters({"https://s.aaa.com/path"}, "https://aaa.com/path"));
+  EXPECT_FALSE(
+      MatchFilters({"https://s.aaa.com/path"}, "https://x.aaa.com/path"));
+  EXPECT_FALSE(
+      MatchFilters({"https://s.aaa.com/path"}, "https://s.aaa.com/bbb"));
+  EXPECT_FALSE(MatchFilters({"https://s.aaa.com/path"}, "https://s.aaa.com/"));
+
+  // Filter only ws and wss schemes.
+  EXPECT_TRUE(MatchFilters({"ws://ws.aaa.com"}, "ws://ws.aaa.com"));
+  EXPECT_TRUE(MatchFilters({"wss://ws.aaa.com"}, "wss://ws.aaa.com"));
+  EXPECT_FALSE(MatchFilters({"ws://ws.aaa.com"}, "http://ws.aaa.com"));
+  EXPECT_FALSE(MatchFilters({"ws://ws.aaa.com"}, "https://ws.aaa.com"));
+  EXPECT_FALSE(MatchFilters({"ws://ws.aaa.com"}, "ftp://ws.aaa.com"));
+
+  // Match an ip address.
+  EXPECT_TRUE(MatchFilters({"123.123.123.123"}, "http://123.123.123.123/"));
+  EXPECT_FALSE(MatchFilters({"123.123.123.123"}, "http://123.123.123.124/"));
+
+  // Open an exception.
+  EXPECT_FALSE(MatchFilters({"plus.google.com"}, "http://google.com/"));
+  EXPECT_FALSE(MatchFilters({"plus.google.com"}, "http://www.google.com/"));
+  EXPECT_TRUE(MatchFilters({"plus.google.com"}, "http://plus.google.com/"));
+
+  // Match exactly "google.com", only for http.
+  EXPECT_TRUE(MatchFilters({"http://.google.com"}, "http://google.com/"));
+  EXPECT_FALSE(MatchFilters({"http://.google.com"}, "https://google.com/"));
+  EXPECT_FALSE(MatchFilters({"http://.google.com"}, "http://www.google.com/"));
+}
+
+TEST(URLUtilTest, MultipleFilters) {
+  // Test exceptions to path prefixes, and most specific matches.
+  std::vector<const std::string> patterns = {"s.xxx.com/a/b",
+                                             "https://s.xxx.com/a/b/c/d"};
+  EXPECT_FALSE(MatchFilters(patterns, "http://s.xxx.com/a"));
+  EXPECT_FALSE(MatchFilters(patterns, "http://s.xxx.com/a/x"));
+  EXPECT_FALSE(MatchFilters(patterns, "https://s.xxx.com/a/x"));
+  EXPECT_TRUE(MatchFilters(patterns, "http://s.xxx.com/a/b"));
+  EXPECT_TRUE(MatchFilters(patterns, "https://s.xxx.com/a/b"));
+  EXPECT_TRUE(MatchFilters(patterns, "http://s.xxx.com/a/b/x"));
+  EXPECT_TRUE(MatchFilters(patterns, "http://s.xxx.com/a/b/c"));
+  EXPECT_TRUE(MatchFilters(patterns, "https://s.xxx.com/a/b/c"));
+  EXPECT_TRUE(MatchFilters(patterns, "https://s.xxx.com/a/b/c/x"));
+  EXPECT_TRUE(MatchFilters(patterns, "https://s.xxx.com/a/b/c/d"));
+  EXPECT_TRUE(MatchFilters(patterns, "http://s.xxx.com/a/b/c/d"));
+  EXPECT_TRUE(MatchFilters(patterns, "https://s.xxx.com/a/b/c/d/x"));
+  EXPECT_TRUE(MatchFilters(patterns, "http://s.xxx.com/a/b/c/d/x"));
+  EXPECT_FALSE(MatchFilters(patterns, "http://xxx.com/a"));
+  EXPECT_FALSE(MatchFilters(patterns, "http://xxx.com/a/b"));
+
+  // Match queries.
+  std::vector<const std::string> queries = {"*?q=1234", "*?q=5678", "*?a=1&b=2",
+                                            "youtube.com?foo=baz",
+                                            "youtube.com?foo=bar*"};
+  EXPECT_TRUE(MatchFilters(queries, "http://google.com?q=1234"));
+  EXPECT_TRUE(MatchFilters(queries, "http://google.com?q=5678"));
+  EXPECT_TRUE(MatchFilters(queries, "http://google.com?a=1&b=2"));
+  EXPECT_TRUE(MatchFilters(queries, "http://google.com?b=2&a=1"));
+  EXPECT_TRUE(MatchFilters(queries, "http://google.com?a=1&b=4&q=1234"));
+  EXPECT_TRUE(MatchFilters(queries, "http://youtube.com?foo=baz"));
+  EXPECT_TRUE(MatchFilters(queries, "http://youtube.com?foo=barbaz"));
+  EXPECT_TRUE(MatchFilters(queries, "http://youtube.com?a=1&foo=barbaz"));
+  EXPECT_FALSE(MatchFilters(queries, "http://google.com?r=1234"));
+  EXPECT_FALSE(MatchFilters(queries, "http://google.com?r=5678"));
+  EXPECT_FALSE(MatchFilters(queries, "http://google.com?a=2&b=1"));
+  EXPECT_FALSE(MatchFilters(queries, "http://google.com?b=1&a=2"));
+  EXPECT_FALSE(MatchFilters(queries, "http://google.com?a=1&b=3"));
+  EXPECT_FALSE(MatchFilters(queries, "http://youtube.com?foo=meh"));
+  EXPECT_FALSE(MatchFilters(queries, "http://youtube.com?foo=bazbar"));
+  EXPECT_FALSE(MatchFilters(queries, "http://youtube.com?foo=ba"));
+}
+
+TEST(URLUtilTest, BasicCoverage) {
+  // Tests to cover the documentation from
+  // http://www.chromium.org/administrators/url-blacklist-filter-format
+
+  // [scheme://][.]host[:port][/path][@query]
+  // Scheme can be http, https, ftp, chrome, etc. This field is optional, and
+  // must be followed by '://'.
+  EXPECT_TRUE(MatchFilters({"file://*"}, "file:///abc.txt"));
+  EXPECT_TRUE(MatchFilters({"file:*"}, "file:///usr/local/boot.txt"));
+  EXPECT_TRUE(MatchFilters({"https://*"}, "https:///abc.txt"));
+  EXPECT_TRUE(MatchFilters({"ftp://*"}, "ftp://ftp.txt"));
+  EXPECT_TRUE(MatchFilters({"chrome://*"}, "chrome:policy"));
+  EXPECT_TRUE(MatchFilters({"noscheme"}, "http://noscheme"));
+  // Filter custom schemes.
+  EXPECT_TRUE(MatchFilters({"custom://*"}, "custom://example_app"));
+  EXPECT_TRUE(MatchFilters({"custom:*"}, "custom:example2_app"));
+  EXPECT_FALSE(MatchFilters({"custom://*"}, "customs://example_apps"));
+  EXPECT_FALSE(MatchFilters({"custom://*"}, "cust*://example_ap"));
+  EXPECT_FALSE(MatchFilters({"custom://*"}, "ecustom:example_app"));
+  EXPECT_TRUE(MatchFilters({"custom://*"}, "custom:///abc.txt"));
+  // Tests for custom scheme patterns that are not supported.
+  EXPECT_FALSE(MatchFilters({"wrong://app"}, "wrong://app"));
+  EXPECT_FALSE(MatchFilters({"wrong ://*"}, "wrong ://app"));
+  EXPECT_FALSE(MatchFilters({" wrong:*"}, " wrong://app"));
+
+  // Omitting the scheme matches most standard schemes.
+  EXPECT_TRUE(MatchFilters({"example.com"}, "chrome:example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "chrome://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "file://example.com/"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "ftp://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "http://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "https://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "gopher://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "ws://example.com"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "wss://example.com"));
+
+  // Some schemes are not matched when the scheme is omitted.
+  EXPECT_FALSE(MatchFilters({"example.com"}, "about://example.com"));
+  EXPECT_FALSE(MatchFilters({"example.com"}, "about:example.com"));
+  EXPECT_FALSE(MatchFilters({"example.com/*"}, "filesystem:///something"));
+  EXPECT_FALSE(MatchFilters({"example.com"}, "custom://example.com"));
+  EXPECT_FALSE(MatchFilters({"example"}, "custom://example"));
+
+  // An optional '.' (dot) can prefix the host field to disable subdomain
+  // matching, see below for details.
+  EXPECT_TRUE(MatchFilters({".example.com"}, "http://example.com/path"));
+  EXPECT_FALSE(MatchFilters({".example.com"}, "http://mail.example.com/path"));
+  EXPECT_TRUE(MatchFilters({"example.com"}, "http://mail.example.com/path"));
+  EXPECT_TRUE(MatchFilters({"ftp://.ftp.file"}, "ftp://ftp.file"));
+  EXPECT_FALSE(MatchFilters({"ftp://.ftp.file"}, "ftp://sub.ftp.file"));
+
+  // The host field is required, and is a valid hostname or an IP address. It
+  // can also take the special '*' value, see below for details.
+  EXPECT_TRUE(MatchFilters({"*"}, "http://anything"));
+  EXPECT_TRUE(MatchFilters({"*"}, "ftp://anything"));
+  EXPECT_TRUE(MatchFilters({"*"}, "custom://anything"));
+  EXPECT_TRUE(MatchFilters({"host"}, "http://host:8080"));
+  EXPECT_FALSE(MatchFilters({"host"}, "file:///host"));
+  EXPECT_TRUE(MatchFilters({"10.1.2.3"}, "http://10.1.2.3:8080/path"));
+  // No host, will match nothing.
+  EXPECT_FALSE(MatchFilters({":8080"}, "http://host:8080"));
+  EXPECT_FALSE(MatchFilters({":8080"}, "http://:8080"));
+
+  // An optional port can come after the host. It must be a valid port value
+  // from 1 to 65535.
+  EXPECT_TRUE(MatchFilters({"host:8080"}, "http://host:8080/path"));
+  EXPECT_TRUE(MatchFilters({"host:1"}, "http://host:1/path"));
+  // Out of range port.
+  EXPECT_FALSE(MatchFilters({"host:65536"}, "http://host:65536/path"));
+  // Star is not allowed in port numbers.
+  EXPECT_FALSE(MatchFilters({"example.com:*"}, "http://example.com"));
+  EXPECT_FALSE(MatchFilters({"example.com:*"}, "http://example.com:8888"));
+
+  // An optional path can come after port.
+  EXPECT_TRUE(MatchFilters({"host/path"}, "http://host:8080/path"));
+  EXPECT_TRUE(MatchFilters({"host/path/path2"}, "http://host/path/path2"));
+  EXPECT_TRUE(MatchFilters({"host/path"}, "http://host/path/path2"));
+
+  // An optional query can come in the end, which is a set of key-value and
+  // key-only tokens delimited by '&'. The key-value tokens are separated
+  // by '='. A query token can optionally end with a '*' to indicate prefix
+  // match. Token order is ignored during matching.
+  EXPECT_TRUE(MatchFilters({"host?q1=1&q2=2"}, "http://host?q2=2&q1=1"));
+  EXPECT_FALSE(MatchFilters({"host?q1=1&q2=2"}, "http://host?q2=1&q1=2"));
+  EXPECT_FALSE(MatchFilters({"host?q1=1&q2=2"}, "http://host?Q2=2&Q1=1"));
+  EXPECT_TRUE(MatchFilters({"host?q1=1&q2=2"}, "http://host?q2=2&q1=1&q3=3"));
+  EXPECT_TRUE(MatchFilters({"host?q1=1&q2=2*"}, "http://host?q2=21&q1=1&q3=3"));
+
+  // user:pass fields can be included but will be ignored
+  // (e.g. http://user:pass@ftp.example.com/pub/bigfile.iso).
+  EXPECT_TRUE(
+      MatchFilters({"host.com/path"}, "http://user:pass@host.com:8080/path"));
+  EXPECT_TRUE(MatchFilters({"ftp://host.com/path"},
+                           "ftp://user:pass@host.com:8080/path"));
+
+  // Case sensitivity.
+  // Scheme is case insensitive.
+  EXPECT_TRUE(MatchFilters({"suPPort://*"}, "support:example"));
+  EXPECT_TRUE(MatchFilters({"FILE://*"}, "file:example"));
+  EXPECT_TRUE(MatchFilters({"FILE://*"}, "FILE://example"));
+  EXPECT_TRUE(MatchFilters({"FtP:*"}, "ftp://example"));
+  EXPECT_TRUE(MatchFilters({"http://example.com"}, "HTTP://example.com"));
+  EXPECT_TRUE(MatchFilters({"HTTP://example.com"}, "http://example.com"));
+  // Host is case insensitive.
+  EXPECT_TRUE(MatchFilters({"http://EXAMPLE.COM"}, "http://example.com"));
+  EXPECT_TRUE(MatchFilters({"Example.com"}, "http://examplE.com/Path?Query=1"));
+  // Path is case sensitive.
+  EXPECT_FALSE(MatchFilters({"example.com/Path"}, "http://example.com/path"));
+  EXPECT_TRUE(MatchFilters({"http://example.com/aB"}, "http://example.com/aB"));
+  EXPECT_FALSE(
+      MatchFilters({"http://example.com/aB"}, "http://example.com/Ab"));
+  EXPECT_FALSE(
+      MatchFilters({"http://example.com/aB"}, "http://example.com/ab"));
+  EXPECT_FALSE(
+      MatchFilters({"http://example.com/aB"}, "http://example.com/AB"));
+  // Query is case sensitive.
+  EXPECT_FALSE(MatchFilters({"host/path?Query=1"}, "http://host/path?query=1"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    URLUtilTest,
+    FilterToComponentsTest,
+    testing::Values(
+        FilterTestParams("google.com",
+                         std::string(),
+                         ".google.com",
+                         true,
+                         0u,
+                         std::string()),
+        FilterTestParams(".google.com",
+                         std::string(),
+                         "google.com",
+                         false,
+                         0u,
+                         std::string()),
+        FilterTestParams("http://google.com",
+                         "http",
+                         ".google.com",
+                         true,
+                         0u,
+                         std::string()),
+        FilterTestParams("google.com/",
+                         std::string(),
+                         ".google.com",
+                         true,
+                         0u,
+                         "/"),
+        FilterTestParams("http://google.com:8080/whatever",
+                         "http",
+                         ".google.com",
+                         true,
+                         8080u,
+                         "/whatever"),
+        FilterTestParams("http://user:pass@google.com:8080/whatever",
+                         "http",
+                         ".google.com",
+                         true,
+                         8080u,
+                         "/whatever"),
+        FilterTestParams("123.123.123.123",
+                         std::string(),
+                         "123.123.123.123",
+                         false,
+                         0u,
+                         std::string()),
+        FilterTestParams("https://123.123.123.123",
+                         "https",
+                         "123.123.123.123",
+                         false,
+                         0u,
+                         std::string()),
+        FilterTestParams("123.123.123.123/",
+                         std::string(),
+                         "123.123.123.123",
+                         false,
+                         0u,
+                         "/"),
+        FilterTestParams("http://123.123.123.123:123/whatever",
+                         "http",
+                         "123.123.123.123",
+                         false,
+                         123u,
+                         "/whatever"),
+        FilterTestParams("*",
+                         std::string(),
+                         std::string(),
+                         true,
+                         0u,
+                         std::string()),
+        FilterTestParams("ftp://*",
+                         "ftp",
+                         std::string(),
+                         true,
+                         0u,
+                         std::string()),
+        FilterTestParams("http://*/whatever",
+                         "http",
+                         std::string(),
+                         true,
+                         0u,
+                         "/whatever")));
+
+TEST_P(FilterToComponentsTest, FilterToComponents) {
+  std::string scheme;
+  std::string host;
+  bool match_subdomains = true;
+  uint16_t port = 42;
+  std::string path;
+  std::string query;
+
+  url_util::FilterToComponents(GetParam().filter(), &scheme, &host,
+                               &match_subdomains, &port, &path, &query);
+  EXPECT_EQ(GetParam().scheme(), scheme);
+  EXPECT_EQ(GetParam().host(), host);
+  EXPECT_EQ(GetParam().match_subdomains(), match_subdomains);
+  EXPECT_EQ(GetParam().port(), port);
+  EXPECT_EQ(GetParam().path(), path);
+}
+
 }  // namespace url_util
 }  // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index d400c7a0..00581b4 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -18166,7 +18166,7 @@
         'type': 'array',
         'items': { 'type': 'string' },
       },
-      'example_value': ['my-awesome-domain.com', 'my-auxiliary-domain.com'],
+      'example_value': ['my-awesome-domain.com', 'https://ssl.server.com', 'hosting.com/bad_path', 'https://server:8080/path', '.exact.hostname.com', 'file://*', 'custom_scheme:*', '*'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -18174,11 +18174,13 @@
       'id': 590,
       'supported_on': ['chrome.*:78-'],
       'future': True,
-      'caption': '''List of domains for which downloaded content needs to be checked for sensitive data protection rule violations''',
+      'caption': '''URL patterns to check downloaded content against sensitive data protection rules''',
       'tags': [],
-      'desc': '''List of domains for which downloaded content needs to be checked for sensitive data protection rule violations before being saved on local storage.  This policy is used only if <ph name="CHECK_CONTENT_COMPLIANCE_POLICY_NAME">CheckContentCompliance</ph> is set to enabled.
+      'desc': '''List of url patterns for which downloaded content needs to be checked for sensitive data protection rule violations before being saved on local storage.  This policy is used only if <ph name="CHECK_CONTENT_COMPLIANCE_POLICY_NAME">CheckContentCompliance</ph> is set to enabled.
 
-      If this policy is not set or set to an empty list of domains, no downloaded content is checked for sensitive data protection rule violations.
+      If this policy is not set or set to an empty list of url patterns, no downloaded content is checked for sensitive data protection rule violations.
+
+      The URL patterns have the same format as the 'URLBlacklist' policy, which is documented at https://www.chromium.org/administrators/url-blacklist-filter-format.
       '''
     },
     {
@@ -18189,7 +18191,7 @@
         'type': 'array',
         'items': { 'type': 'string' },
       },
-      'example_value': ['my-awesome-domain.com', 'my-auxiliary-domain.com'],
+      'example_value': ['my-awesome-domain.com', 'https://ssl.server.com', 'hosting.com/bad_path', 'https://server:8080/path', '.exact.hostname.com', 'file://*', 'custom_scheme:*', '*'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -18197,11 +18199,13 @@
       'id': 591,
       'supported_on': ['chrome.*:78-'],
       'future': True,
-      'caption': '''List of domains for which uploaded content need not be checked for sensitive data protection rule violations''',
+      'caption': '''URL patterns to not check uploaded content against sensitive data protection rules''',
       'tags': [],
-      'desc': '''List of domains for which uploaded files, data pasted from the clipboard, or data dragged and dropped does not need to be checked for sensitive data protection rule violations.  This policy is used only if <ph name="CHECK_CONTENT_COMPLIANCE_POLICY_NAME">CheckContentCompliance</ph> is set to enabled.
+      'desc': '''List of url patterns for which uploaded files, data pasted from the clipboard, or data dragged and dropped does not need to be checked for sensitive data protection rule violations.  This policy is used only if <ph name="CHECK_CONTENT_COMPLIANCE_POLICY_NAME">CheckContentCompliance</ph> is set to enabled.
 
-      If this policy is not set or set to an empty list of domains, all content is checked for sensitive data protection rule violations.
+      If this policy is not set or set to an empty list of url patterns, all content is checked for sensitive data protection rule violations.
+
+      The URL patterns have the same format as the 'URLBlacklist' policy, which is documented at https://www.chromium.org/administrators/url-blacklist-filter-format.
       '''
     },
     {
@@ -18231,7 +18235,7 @@
         'type': 'array',
         'items': { 'type': 'string' },
       },
-      'example_value': ['my-awesome-domain.com', 'my-auxiliary-domain.com'],
+      'example_value': ['my-awesome-domain.com', 'https://ssl.server.com', 'hosting.com/bad_path', 'https://server:8080/path', '.exact.hostname.com', 'file://*', 'custom_scheme:*', '*'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -18239,11 +18243,13 @@
       'id': 600,
       'supported_on': ['chrome.*:78-'],
       'future': True,
-      'caption': '''List of domains for which uploaded content needs to be checked for malware''',
+      'caption': '''URL patterns to check uploaded content for malware''',
       'tags': [],
-      'desc': '''List of domains for which uploaded content needs to be checked for malware.  This policy is used only if <ph name="SEND_FILES_FOR_MALWARE_CHECK_POLICY_NAME">SendFilesForMalwareCheck</ph> is set to a value allowing or enforcing malware checks for uploads.
+      'desc': '''List of url patterns for which uploaded content needs to be checked for malware.  This policy is used only if <ph name="SEND_FILES_FOR_MALWARE_CHECK_POLICY_NAME">SendFilesForMalwareCheck</ph> is set to a value allowing or enforcing malware checks for uploads.
 
-      If this policy is not set or set to an empty list of domains, no uploaded content is checked for malware.
+      If this policy is not set or set to an empty list of url patterns, no uploaded content is checked for malware.
+
+      The URL patterns have the same format as the 'URLBlacklist' policy, which is documented at https://www.chromium.org/administrators/url-blacklist-filter-format.
       '''
     },
     {
diff --git a/components/prefs/json_pref_store.cc b/components/prefs/json_pref_store.cc
index 2b565797..dc80ccd 100644
--- a/components/prefs/json_pref_store.cc
+++ b/components/prefs/json_pref_store.cc
@@ -446,11 +446,10 @@
 
   if (pref_filter_) {
     filtering_in_progress_ = true;
-    const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
-        base::Bind(
-            &JsonPrefStore::FinalizeFileRead, AsWeakPtr(),
-            initialization_successful));
-    pref_filter_->FilterOnLoad(post_filter_on_load_callback,
+    PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
+        base::BindOnce(&JsonPrefStore::FinalizeFileRead, AsWeakPtr(),
+                       initialization_successful));
+    pref_filter_->FilterOnLoad(std::move(post_filter_on_load_callback),
                                std::move(unfiltered_prefs));
   } else {
     FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs),
diff --git a/components/prefs/json_pref_store_unittest.cc b/components/prefs/json_pref_store_unittest.cc
index 1842f45..5ab55c0 100644
--- a/components/prefs/json_pref_store_unittest.cc
+++ b/components/prefs/json_pref_store_unittest.cc
@@ -68,7 +68,7 @@
 
   // PrefFilter implementation:
   void FilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents) override;
   void FilterUpdate(const std::string& path) override {}
   OnWriteCallbackPair FilterSerializeData(
@@ -101,16 +101,16 @@
 InterceptingPrefFilter::~InterceptingPrefFilter() {}
 
 void InterceptingPrefFilter::FilterOnLoad(
-    const PostFilterOnLoadCallback& post_filter_on_load_callback,
+    PostFilterOnLoadCallback post_filter_on_load_callback,
     std::unique_ptr<base::DictionaryValue> pref_store_contents) {
-  post_filter_on_load_callback_ = post_filter_on_load_callback;
+  post_filter_on_load_callback_ = std::move(post_filter_on_load_callback);
   intercepted_prefs_ = std::move(pref_store_contents);
 }
 
 void InterceptingPrefFilter::ReleasePrefs() {
   EXPECT_FALSE(post_filter_on_load_callback_.is_null());
-  post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false);
-  post_filter_on_load_callback_.Reset();
+  std::move(post_filter_on_load_callback_)
+      .Run(std::move(intercepted_prefs_), false);
 }
 
 class MockPrefStoreObserver : public PrefStore::Observer {
diff --git a/components/prefs/pref_change_registrar.cc b/components/prefs/pref_change_registrar.cc
index da7de46..eddd593 100644
--- a/components/prefs/pref_change_registrar.cc
+++ b/components/prefs/pref_change_registrar.cc
@@ -24,8 +24,9 @@
 }
 
 void PrefChangeRegistrar::Add(const std::string& path,
-                              const base::Closure& obs) {
-  Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs));
+                              const base::RepeatingClosure& obs) {
+  Add(path,
+      base::BindRepeating(&PrefChangeRegistrar::InvokeUnnamedCallback, obs));
 }
 
 void PrefChangeRegistrar::Add(const std::string& path,
@@ -81,9 +82,9 @@
     observers_[pref].Run(pref);
 }
 
-void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback,
+void PrefChangeRegistrar::InvokeUnnamedCallback(base::OnceClosure callback,
                                                 const std::string& pref_name) {
-  callback.Run();
+  std::move(callback).Run();
 }
 
 PrefService* PrefChangeRegistrar::prefs() {
diff --git a/components/prefs/pref_change_registrar.h b/components/prefs/pref_change_registrar.h
index 8aba1b3..e2cf9bcf8 100644
--- a/components/prefs/pref_change_registrar.h
+++ b/components/prefs/pref_change_registrar.h
@@ -40,7 +40,7 @@
   // the preference that is changing as its parameter.
   //
   // Only one observer may be registered per path.
-  void Add(const std::string& path, const base::Closure& obs);
+  void Add(const std::string& path, const base::RepeatingClosure& obs);
   void Add(const std::string& path, const NamedChangeCallback& obs);
 
   // Removes the pref observer registered for |path|.
@@ -67,7 +67,7 @@
   void OnPreferenceChanged(PrefService* service,
                            const std::string& pref_name) override;
 
-  static void InvokeUnnamedCallback(const base::Closure& callback,
+  static void InvokeUnnamedCallback(base::OnceClosure callback,
                                     const std::string& pref_name);
 
   using ObserverMap = std::map<std::string, NamedChangeCallback>;
diff --git a/components/prefs/pref_filter.h b/components/prefs/pref_filter.h
index aabcdc2..33e61cc 100644
--- a/components/prefs/pref_filter.h
+++ b/components/prefs/pref_filter.h
@@ -29,8 +29,8 @@
   // builder. |schedule_write| indicates whether a write should be immediately
   // scheduled (typically because the |prefs| were pre-modified).
   using PostFilterOnLoadCallback =
-      base::Callback<void(std::unique_ptr<base::DictionaryValue> prefs,
-                          bool schedule_write)>;
+      base::OnceCallback<void(std::unique_ptr<base::DictionaryValue> prefs,
+                              bool schedule_write)>;
 
   virtual ~PrefFilter() {}
 
@@ -42,7 +42,7 @@
   // PersistentPrefStores should handle this to make the reads look synchronous
   // to external users (see SegregatedPrefStore::ReadPrefs() for an example).
   virtual void FilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents) = 0;
 
   // Receives notification when a pref store value is changed, before Observers
diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd
index 78700d9..adff924 100644
--- a/components/resources/components_resources.grd
+++ b/components/resources/components_resources.grd
@@ -22,6 +22,7 @@
       <part file="safe_browsing_resources.grdp" />
       <part file="security_interstitials_resources.grdp" />
       <part file="signin_resources.grdp" />
+      <part file="sync_driver_resources.grdp" />
       <part file="translate_resources.grdp" />
       <part file="user_actions_ui_resources.grdp" />
       <part file="version_ui_resources.grdp" />
diff --git a/components/resources/sync_driver_resources.grdp b/components/resources/sync_driver_resources.grdp
new file mode 100644
index 0000000..9c322f5a
--- /dev/null
+++ b/components/resources/sync_driver_resources.grdp
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_HTML" file="../sync/driver/resources/index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_JS" file="../sync/driver/resources/sync_index.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_CHROME_SYNC_JS" file="../sync/driver/resources/chrome_sync.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TYPES_JS" file="../sync/driver/resources/types.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_LOG_JS" file="../sync/driver/resources/sync_log.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS" file="../sync/driver/resources/sync_node_browser.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_SEARCH_JS" file="../sync/driver/resources/sync_search.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_ABOUT_JS" file="../sync/driver/resources/about.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_DATA_JS" file="../sync/driver/resources/data.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_EVENTS_JS" file="../sync/driver/resources/events.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SEARCH_JS" file="../sync/driver/resources/search.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_USER_EVENTS_JS" file="../sync/driver/resources/user_events.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TRAFFIC_LOG_JS"  file="../sync/driver/resources/traffic_log.js" type="BINDATA" compress="gzip" />
+</grit-part>
diff --git a/components/sync/driver/BUILD.gn b/components/sync/driver/BUILD.gn
index b968b087..d30baec 100644
--- a/components/sync/driver/BUILD.gn
+++ b/components/sync/driver/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/features.gni")
 import("//build/config/jumbo.gni")
-import("//tools/grit/grit_rule.gni")
 
 declare_args() {
   # Controls the product part of the user agent calculated in sync_util.cc.
@@ -136,23 +135,6 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
-grit("resources") {
-  source = "resources.grd"
-
-  # The .grd contains references to generated files.
-  source_is_generated = true
-  outputs = [
-    "grit/sync_driver_resources.h",
-    "sync_driver_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/components"
-  depfile_dir = target_gen_dir
-  grit_flags = [
-    "-E",
-    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
-  ]
-}
-
 static_library("test_support") {
   testonly = true
   sources = [
diff --git a/components/sync/driver/resources.grd b/components/sync/driver/resources.grd
deleted file mode 100644
index 5b59e1f09..0000000
--- a/components/sync/driver/resources.grd
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0"
-      current_release="1"
-      output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/sync_driver_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="sync_driver_resources.pak" type="data_package" />
-  </outputs>
-  <release seq="1">
-    <includes>
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_HTML" file="resources/index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_JS" file="resources/sync_index.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_CHROME_SYNC_JS" file="resources/chrome_sync.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TYPES_JS" file="resources/types.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_LOG_JS" file="resources/sync_log.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS" file="resources/sync_node_browser.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_SEARCH_JS" file="resources/sync_search.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_ABOUT_JS" file="resources/about.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_DATA_JS" file="resources/data.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_EVENTS_JS" file="resources/events.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_SEARCH_JS" file="resources/search.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_USER_EVENTS_JS" file="resources/user_events.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_SYNC_DRIVER_SYNC_INTERNALS_TRAFFIC_LOG_JS"  file="resources/traffic_log.js" type="BINDATA" compress="gzip" />
-    </includes>
-  </release>
-</grit>
diff --git a/components/sync/nigori/nigori_state.cc b/components/sync/nigori/nigori_state.cc
index 9afe92e..4bb6f2b 100644
--- a/components/sync/nigori/nigori_state.cc
+++ b/components/sync/nigori/nigori_state.cc
@@ -75,6 +75,10 @@
   for (int i = 0; i < proto.keystore_key_size(); ++i) {
     state.keystore_keys.push_back(proto.keystore_key(i));
   }
+  if (proto.has_pending_keystore_decryptor_token()) {
+    state.pending_keystore_decryptor_token =
+        proto.pending_keystore_decryptor_token();
+  }
   return state;
 }
 
@@ -127,6 +131,10 @@
   for (const std::string& keystore_key : keystore_keys) {
     proto.add_keystore_key(keystore_key);
   }
+  if (pending_keystore_decryptor_token.has_value()) {
+    *proto.mutable_pending_keystore_decryptor_token() =
+        *pending_keystore_decryptor_token;
+  }
   return proto;
 }
 
diff --git a/components/sync/nigori/nigori_state.h b/components/sync/nigori/nigori_state.h
index b23922ca..7d4d79a5 100644
--- a/components/sync/nigori/nigori_state.h
+++ b/components/sync/nigori/nigori_state.h
@@ -68,6 +68,11 @@
   // key. These keys are not a part of Nigori node and are persisted
   // separately. Must be encrypted with OSCrypt before persisting.
   std::vector<std::string> keystore_keys;
+
+  // Represents |keystore_decryptor_token| from NigoriSpecifics in case it
+  // can't be decrypted right after remote update arrival due to lack of
+  // keystore keys. May be set only for keystore Nigori.
+  base::Optional<sync_pb::EncryptedData> pending_keystore_decryptor_token;
 };
 
 }  // namespace syncer
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index e5d564f..9ef3de9 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -88,30 +88,6 @@
                                                keystore_decryptor_token);
 }
 
-// Attempts to decrypt |keystore_decryptor_token| with |keystore_keys|. Returns
-// a keybag with one key in case of success or an empty one in case of error.
-NigoriKeyBag DecryptKeystoreDecryptorToken(
-    const std::vector<std::string>& keystore_keys,
-    const sync_pb::EncryptedData& keystore_decryptor_token) {
-  if (keystore_decryptor_token.blob().empty()) {
-    return NigoriKeyBag::CreateEmpty();
-  }
-
-  std::unique_ptr<Cryptographer> cryptographer =
-      CreateCryptographerFromKeystoreKeys(keystore_keys);
-
-  sync_pb::NigoriKey key;
-  // This check should never fail as long as we don't receive invalid data.
-  if (!cryptographer || !cryptographer->CanDecrypt(keystore_decryptor_token) ||
-      !cryptographer->Decrypt(keystore_decryptor_token, &key)) {
-    return NigoriKeyBag::CreateEmpty();
-  }
-
-  NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
-  key_bag.AddKeyFromProto(key);
-  return key_bag;
-}
-
 // Creates keystore Nigori specifics given |keystore_keys|.
 // Returns new NigoriSpecifics if successful and base::nullopt otherwise. If
 // successful the result will contain:
@@ -633,6 +609,7 @@
   }
 
   state_.cryptographer->SelectDefaultEncryptionKey(default_key_name);
+  state_.pending_keystore_decryptor_token.reset();
   state_.passphrase_type = NigoriSpecifics::CUSTOM_PASSPHRASE;
   state_.custom_passphrase_key_derivation_params =
       custom_passphrase_key_derivation_params;
@@ -774,7 +751,8 @@
   // server responsibility to send updated keystore keys. |keystore_keys_| is
   // expected to be non-empty before MergeSyncData() call, regardless of
   // passphrase type.
-  return state_.keystore_keys.empty();
+  return state_.keystore_keys.empty() ||
+         state_.pending_keystore_decryptor_token.has_value();
 }
 
 bool NigoriSyncBridgeImpl::SetKeystoreKeys(
@@ -793,6 +771,19 @@
     base::Base64Encode(keys[i], &state_.keystore_keys[i]);
   }
 
+  if (state_.pending_keystore_decryptor_token.has_value()) {
+    // Newly arrived keystore keys could resolve pending encryption state in
+    // keystore mode.
+    DCHECK_EQ(state_.passphrase_type, NigoriSpecifics::KEYSTORE_PASSPHRASE);
+    DCHECK(state_.pending_keys.has_value());
+    UpdateCryptographerFromKeystoreNigori(
+        sync_pb::EncryptedData(*state_.pending_keys),
+        sync_pb::EncryptedData(*state_.pending_keystore_decryptor_token));
+    for (auto& observer : observers_) {
+      observer.OnCryptographerStateChanged(state_.cryptographer.get(),
+                                           state_.pending_keys.has_value());
+    }
+  }
   // Note: we don't need to persist keystore keys here, because we will receive
   // Nigori node right after this method and persist all the data during
   // UpdateLocalState().
@@ -821,20 +812,21 @@
                       "sync of Nigori.");
   }
   DCHECK(data->specifics.has_nigori());
-  // Ensure we have |keystore_keys| during the initial download, requested to
-  // the server as per NeedKeystoreKey(), and required for decrypting the
-  // keystore_decryptor_token in specifics or initializing the default keystore
-  // Nigori.
-  if (state_.keystore_keys.empty()) {
-    // TODO(crbug.com/922900): verify, whether it's a valid behavior for custom
-    // passphrase.
-    return ModelError(FROM_HERE,
-                      "Keystore keys are not set during first time sync.");
-  }
+
   if (!data->specifics.nigori().encryption_keybag().blob().empty()) {
     // We received regular Nigori.
     return UpdateLocalState(data->specifics.nigori());
   }
+  // Ensure we have |keystore_keys| during the initial download, requested to
+  // the server as per NeedKeystoreKey(), and required for initializing the
+  // default keystore Nigori.
+  if (state_.keystore_keys.empty()) {
+    // TODO(crbug.com/922900): try to relax this requirement for Nigori
+    // initialization as well. Keystore keys might not arrive, for example, due
+    // to throttling.
+    return ModelError(FROM_HERE,
+                      "Keystore keys are not set during first time sync.");
+  }
   // We received uninitialized Nigori and need to initialize it as default
   // keystore Nigori.
   base::Optional<NigoriSpecifics> initialized_specifics =
@@ -882,6 +874,11 @@
 
   const bool passphrase_type_changed =
       UpdatePassphraseType(new_passphrase_type, &state_.passphrase_type);
+  if (passphrase_type_changed) {
+    // Keystore decryptor token is relevant only for keystore nigori, so it's
+    // safe to clear its pending state now.
+    state_.pending_keystore_decryptor_token.reset();
+  }
   DCHECK_NE(state_.passphrase_type, NigoriSpecifics::UNKNOWN);
 
   const bool encrypted_types_changed =
@@ -896,7 +893,6 @@
         ProtoTimeToTime(specifics.keystore_migration_time());
   }
 
-  DCHECK(!state_.keystore_keys.empty());
   const sync_pb::EncryptedData& encryption_keybag =
       specifics.encryption_keybag();
   switch (state_.passphrase_type) {
@@ -957,18 +953,44 @@
   DCHECK(!encryption_keybag.blob().empty());
   DCHECK(!keystore_decryptor_token.blob().empty());
 
-  state_.cryptographer->EmplaceKeysFrom(DecryptKeystoreDecryptorToken(
-      state_.keystore_keys, keystore_decryptor_token));
+  // Decryption of |keystore_decryptor_token|.
+  std::unique_ptr<Cryptographer> keystore_cryptographer =
+      CreateCryptographerFromKeystoreKeys(state_.keystore_keys);
+  if (!keystore_cryptographer) {
+    return ModelError(FROM_HERE,
+                      "Failed to create cryptographer from keystore keys.");
+  }
 
+  NigoriKeyBag keystore_decryptor_key_bag = NigoriKeyBag::CreateEmpty();
+  sync_pb::NigoriKey keystore_decryptor_key;
+  if (keystore_cryptographer->Decrypt(keystore_decryptor_token,
+                                      &keystore_decryptor_key)) {
+    keystore_decryptor_key_bag.AddKeyFromProto(keystore_decryptor_key);
+    state_.pending_keystore_decryptor_token.reset();
+  } else {
+    state_.pending_keystore_decryptor_token = keystore_decryptor_token;
+  }
+
+  // TODO(crbug.com/922900): issue ModelError if |keystore_decryptor_keybag| is
+  // not empty and can't decrypt the |encryption_keybag|?
+  if (keystore_decryptor_key_bag.CanDecrypt(encryption_keybag)) {
+    // |encryption_keybag| must contain the key it was encrypted with, so it's
+    // okay to add it earlier.
+    state_.cryptographer->EmplaceKeysFrom(keystore_decryptor_key_bag);
+  }
+
+  // Decryption of |encryption_keybag|.
   sync_pb::NigoriKeyBag key_bag;
   if (!state_.cryptographer->Decrypt(encryption_keybag, &key_bag)) {
-    return ModelError(FROM_HERE, "Failed to decrypt incoming keystore nigori.");
+    state_.cryptographer->ClearDefaultEncryptionKey();
+    state_.pending_keys = encryption_keybag;
+    return base::nullopt;
   }
 
   state_.cryptographer->EmplaceKeysFrom(NigoriKeyBag::CreateFromProto(key_bag));
-  state_.pending_keys.reset();
   state_.cryptographer->SelectDefaultEncryptionKey(
       encryption_keybag.key_name());
+  state_.pending_keys.reset();
   return base::nullopt;
 }
 
@@ -977,6 +999,13 @@
   // TODO(crbug.com/922900): support the case when client knows passphrase.
   NOTIMPLEMENTED();
   DCHECK(!encryption_keybag.blob().empty());
+
+  // TODO(crbug.com/922900): consider detection of protocol violation instead
+  // of cleaning previously set |pending_keys|. If they was set, they should
+  // remain set after exiting this function (but might have different value).
+  // Clean up previous pending keys state.
+  state_.pending_keys.reset();
+
   if (!state_.cryptographer->CanDecrypt(encryption_keybag)) {
     // Historically, prior to USS, key derived from explicit passphrase was
     // stored in prefs and effectively we do migration here.
@@ -1052,9 +1081,18 @@
         *state_.custom_passphrase_key_derivation_params, &specifics);
   }
   if (state_.passphrase_type == NigoriSpecifics::KEYSTORE_PASSPHRASE) {
-    EncryptKeystoreDecryptorToken(*state_.cryptographer,
-                                  specifics.mutable_keystore_decryptor_token(),
-                                  state_.keystore_keys);
+    // TODO(crbug.com/922900): it seems possible to have corrupted
+    // |pending_keystore_decryptor_token| and an ability to recover it in case
+    // |pending_keys| isn't set and |keystore_keys| contains some keys.
+    if (state_.pending_keystore_decryptor_token.has_value()) {
+      *specifics.mutable_keystore_decryptor_token() =
+          *state_.pending_keystore_decryptor_token;
+    } else {
+      DCHECK(!state_.keystore_keys.empty());
+      EncryptKeystoreDecryptorToken(
+          *state_.cryptographer, specifics.mutable_keystore_decryptor_token(),
+          state_.keystore_keys);
+    }
   }
   if (!state_.keystore_migration_time.is_null()) {
     specifics.set_keystore_migration_time(
@@ -1098,6 +1136,7 @@
   state_.keystore_keys.clear();
   state_.cryptographer = CryptographerImpl::CreateEmpty();
   state_.pending_keys.reset();
+  state_.pending_keystore_decryptor_token.reset();
   state_.passphrase_type = NigoriSpecifics::UNKNOWN;
   state_.encrypt_everything = false;
   state_.custom_passphrase_time = base::Time();
@@ -1180,7 +1219,12 @@
   switch (state_.passphrase_type) {
     case NigoriSpecifics::UNKNOWN:
     case NigoriSpecifics::IMPLICIT_PASSPHRASE:
+      return;
     case NigoriSpecifics::KEYSTORE_PASSPHRASE:
+      // TODO(crbug.com/922900): in backward-compatible keystore mode,
+      // |pending_keys| could be decrypted with user-provided passphrase.
+      // Consider calling OnPassphraseRequired() together with decryption
+      // logic.
       return;
     case NigoriSpecifics::CUSTOM_PASSPHRASE:
     case NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE:
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index cd697f3..c2e72b2 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -567,6 +567,33 @@
   EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
 }
 
+// This test emulates late arrival of keystore keys, so neither
+// |keystore_decryptor_token| or |encryption_keybag| could be decrypted at the
+// moment NigoriSpecifics arrived. They should be decrypted right after
+// keystore keys arrival.
+TEST_F(NigoriSyncBridgeImplTest, ShouldDecryptPendingKeysInKeystoreMode) {
+  const std::string kRawKeystoreKey = "raw_keystore_key";
+  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(kRawKeystoreKey);
+  EntityData entity_data;
+  *entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics(
+      /*keybag_keys_params=*/{kKeystoreKeyParams},
+      /*keystore_decryptor_params=*/kKeystoreKeyParams,
+      /*keystore_key_params=*/kKeystoreKeyParams);
+
+  EXPECT_CALL(*observer(), OnCryptographerStateChanged(
+                               NotNull(), /*has_pending_keys=*/true));
+  EXPECT_THAT(bridge()->MergeSyncData(std::move(entity_data)),
+              Eq(base::nullopt));
+  const Cryptographer& cryptographer = bridge()->GetCryptographerForTesting();
+  EXPECT_FALSE(cryptographer.CanEncrypt());
+
+  EXPECT_CALL(*observer(), OnCryptographerStateChanged(
+                               NotNull(), /*has_pending_keys=*/false));
+  EXPECT_TRUE(bridge()->SetKeystoreKeys({kRawKeystoreKey}));
+  EXPECT_THAT(cryptographer, CanDecryptWith(kKeystoreKeyParams));
+  EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
+}
+
 // Tests that we can perform initial sync with custom passphrase Nigori.
 // We should notify observers about encryption state changes and cryptographer
 // shouldn't be ready (by having pending keys) until user provides the
diff --git a/components/sync/protocol/nigori_local_data.proto b/components/sync/protocol/nigori_local_data.proto
index d9d343b..574ee68 100644
--- a/components/sync/protocol/nigori_local_data.proto
+++ b/components/sync/protocol/nigori_local_data.proto
@@ -86,6 +86,10 @@
   // browser startup. Due to backward compatibility requirements keys are
   // always Base64 encoded.
   repeated string keystore_key = 10;
+
+  // Encryptor keystore decryptor token. Used for decryption of keystore Nigori
+  // in case keystore keys arrived after NigoriSpecifics.
+  optional EncryptedData pending_keystore_decryptor_token = 11;
 }
 
 // Sync proto to store Nigori data in storage. Proto should be encrypted with
diff --git a/components/sync/protocol/nigori_specifics.proto b/components/sync/protocol/nigori_specifics.proto
index a72dbfc..8ebadf4 100644
--- a/components/sync/protocol/nigori_specifics.proto
+++ b/components/sync/protocol/nigori_specifics.proto
@@ -192,4 +192,7 @@
 
   // Boolean corresponding to whether Web Apps data should be encrypted.
   optional bool encrypt_web_apps = 48;
+
+  // Boolean corresponding to whether OS preferences should be encrypted.
+  optional bool encrypt_os_preferences = 49;
 }
diff --git a/components/sync/protocol/os_preference_specifics.proto b/components/sync/protocol/os_preference_specifics.proto
new file mode 100644
index 0000000..1f731739f
--- /dev/null
+++ b/components/sync/protocol/os_preference_specifics.proto
@@ -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.
+//
+// Sync protocol datatype extension for Chrome OS preferences.
+
+// If you change or add any fields in this file, update proto_visitors.h and
+// potentially proto_enum_conversions.{h, cc}.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.sync.protocol";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+import "preference_specifics.proto";
+
+// Properties of a synced Chrome OS system preference. Uses PreferenceSpecifics
+// instead of declaring name and value fields to allow more code sharing in the
+// client (e.g. all PrefModelAssociators can operate on PreferenceSpecifics).
+message OsPreferenceSpecifics {
+  optional PreferenceSpecifics preference = 1;
+}
diff --git a/components/sync/protocol/os_priority_preference_specifics.proto b/components/sync/protocol/os_priority_preference_specifics.proto
new file mode 100644
index 0000000..d7be4267
--- /dev/null
+++ b/components/sync/protocol/os_priority_preference_specifics.proto
@@ -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.
+//
+// Sync protocol datatype extension for Chrome OS priority preferences.
+
+// If you change or add any fields in this file, update proto_visitors.h and
+// potentially proto_enum_conversions.{h, cc}.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.sync.protocol";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+import "preference_specifics.proto";
+
+// Properties of a Chrome OS priority preference. Uses PreferenceSpecifics
+// instead of declaring name and value fields to allow more code sharing
+// in the client (e.g. all PrefModelAssociators can use PreferenceSpecifics).
+message OsPriorityPreferenceSpecifics {
+  optional PreferenceSpecifics preference = 1;
+}
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 189a671..dc87cd28 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -607,6 +607,7 @@
   VISIT(encrypt_everything);
   VISIT_REP(encrypted_types_specifics_field_number);
   VISIT_REP(keystore_key);
+  VISIT(pending_keystore_decryptor_token);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::NigoriLocalData& proto) {
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index 5848eae..c39a3483 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -36,6 +36,8 @@
   "mountain_share_specifics",
   "nigori_local_data",
   "nigori_specifics",
+  "os_preference_specifics",
+  "os_priority_preference_specifics",
   "password_specifics",
   "persisted_entity_data",
   "preference_specifics",
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto
index 993becdc..446b430f 100644
--- a/components/sync/protocol/sync.proto
+++ b/components/sync/protocol/sync.proto
@@ -40,6 +40,8 @@
 import "managed_user_whitelist_specifics.proto";
 import "mountain_share_specifics.proto";
 import "nigori_specifics.proto";
+import "os_preference_specifics.proto";
+import "os_priority_preference_specifics.proto";
 import "password_specifics.proto";
 import "preference_specifics.proto";
 import "printer_specifics.proto";
@@ -155,6 +157,8 @@
     SecurityEventSpecifics security_event = 600372;
     WebAppSpecifics web_app = 673225;
     WifiConfigurationSpecifics wifi_configuration = 662827;
+    OsPreferenceSpecifics os_preference = 702141;
+    OsPriorityPreferenceSpecifics os_priority_preference = 703915;
   }
   reserved 194582;
   reserved "managed_user";
diff --git a/components/viz/host/hit_test/hit_test_query_unittest.cc b/components/viz/host/hit_test/hit_test_query_unittest.cc
index 1adf93cb..1b116b8 100644
--- a/components/viz/host/hit_test/hit_test_query_unittest.cc
+++ b/components/viz/host/hit_test/hit_test_query_unittest.cc
@@ -6,13 +6,16 @@
 
 #include <cstdint>
 
+#include "base/feature_list.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
 namespace test {
 
-class HitTestQueryTest : public testing::Test {
+class HitTestQueryTest : public testing::TestWithParam<bool> {
  public:
   HitTestQueryTest() = default;
   ~HitTestQueryTest() override = default;
@@ -23,7 +26,19 @@
 
  protected:
   HitTestQuery& hit_test_query() { return hit_test_query_; }
+  void SetUp() override {
+    if (!GetParam()) {
+      // kHitTestIgnore has different meanings in v1 and v2. Some tests set
+      // kHitTestIgnore for certain regions which works for v1 but fails v2.
+      test_flags_ |= HitTestRegionFlags::kHitTestIgnore;
+      return;
+    }
 
+    feature_list_.InitAndEnableFeature(features::kEnableVizHitTestSurfaceLayer);
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  uint32_t test_flags_ = HitTestRegionFlags::kHitTestChildSurface;
   std::vector<AggregatedHitTestRegion> active_data_;
 
  private:
@@ -32,6 +47,8 @@
   DISALLOW_COPY_AND_ASSIGN(HitTestQueryTest);
 };
 
+INSTANTIATE_TEST_SUITE_P(/* no prefix */, HitTestQueryTest, testing::Bool());
+
 // One surface.
 //
 //  +e---------+
@@ -40,7 +57,7 @@
 //  |          |
 //  +----------+
 //
-TEST_F(HitTestQueryTest, OneSurface) {
+TEST_P(HitTestQueryTest, OneSurface) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   gfx::Rect e_bounds = gfx::Rect(0, 0, 600, 600);
   gfx::Transform transform_e_to_e;
@@ -87,7 +104,7 @@
 //  | +---+ |     |      3        c2
 //  +-------------+      4        none
 //
-TEST_F(HitTestQueryTest, OneEmbedderTwoChildren) {
+TEST_P(HitTestQueryTest, OneEmbedderTwoChildren) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId c2_id = FrameSinkId(3, 3);
@@ -146,7 +163,7 @@
 }
 
 // One embedder with a rotated child.
-TEST_F(HitTestQueryTest, OneEmbedderRotatedChild) {
+TEST_P(HitTestQueryTest, OneEmbedderRotatedChild) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
@@ -196,7 +213,7 @@
 //  |   ||   4     |       4        b
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, ClippedChildWithTabAndTransparentBackground) {
+TEST_P(HitTestQueryTest, ClippedChildWithTabAndTransparentBackground) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -213,11 +230,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 3));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 2));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 2));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -276,7 +290,7 @@
 //  |   || | 4     |       4        b
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, ClippedChildWithChildUnderneath) {
+TEST_P(HitTestQueryTest, ClippedChildWithChildUnderneath) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -296,11 +310,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 4));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 2));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 2));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -352,7 +363,7 @@
 
 // Tests transforming location to be in target's coordinate system given the
 // target's ancestor list, in the case of ClippedChildWithChildUnderneath test.
-TEST_F(HitTestQueryTest, ClippedChildWithChildUnderneathTransform) {
+TEST_P(HitTestQueryTest, ClippedChildWithChildUnderneathTransform) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -372,11 +383,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 4));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 2));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 2));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -434,7 +442,7 @@
 //  |   ||   7     |
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, ClippedChildrenWithTabAndTransparentBackground) {
+TEST_P(HitTestQueryTest, ClippedChildrenWithTabAndTransparentBackground) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -460,11 +468,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 6));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c1_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c1_bounds_in_e, transform_e_to_c1, 2));  // c1
+  active_data_.push_back(AggregatedHitTestRegion(
+      c1_id, test_flags_, c1_bounds_in_e, transform_e_to_c1, 2));  // c1
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -473,11 +478,8 @@
       b_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       b_bounds_in_c1, transform_c1_to_b, 0));  // b
-  active_data_.push_back(
-      AggregatedHitTestRegion(c2_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c2_bounds_in_e, transform_e_to_c2, 2));  // c2
+  active_data_.push_back(AggregatedHitTestRegion(
+      c2_id, test_flags_, c2_bounds_in_e, transform_e_to_c2, 2));  // c2
   active_data_.push_back(AggregatedHitTestRegion(
       g_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -550,7 +552,7 @@
 // Tests transforming location to be in target's coordinate system given the
 // target's ancestor list, in the case of
 // ClippedChildrenWithTabAndTransparentBackground test.
-TEST_F(HitTestQueryTest,
+TEST_P(HitTestQueryTest,
        ClippedChildrenWithTabAndTransparentBackgroundTransform) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
@@ -577,11 +579,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 6));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c1_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c1_bounds_in_e, transform_e_to_c1, 2));  // c1
+  active_data_.push_back(AggregatedHitTestRegion(
+      c1_id, test_flags_, c1_bounds_in_e, transform_e_to_c1, 2));  // c1
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -590,11 +589,8 @@
       b_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       b_bounds_in_c1, transform_c1_to_b, 0));  // b
-  active_data_.push_back(
-      AggregatedHitTestRegion(c2_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c2_bounds_in_e, transform_e_to_c2, 2));  // c2
+  active_data_.push_back(AggregatedHitTestRegion(
+      c2_id, test_flags_, c2_bounds_in_e, transform_e_to_c2, 2));  // c2
   active_data_.push_back(AggregatedHitTestRegion(
       g_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -658,7 +654,7 @@
 //  | +--------|----+     |
 //  +---------------------+
 //
-TEST_F(HitTestQueryTest, MultipleLayerChild) {
+TEST_P(HitTestQueryTest, MultipleLayerChild) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -681,11 +677,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 5));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c1_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c1_bounds_in_e, transform_e_to_c1, 3));  // c1
+  active_data_.push_back(AggregatedHitTestRegion(
+      c1_id, test_flags_, c1_bounds_in_e, transform_e_to_c1, 3));  // c1
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -754,7 +747,7 @@
 //  | +--------|----+     |
 //  +---------------------+
 //
-TEST_F(HitTestQueryTest, MultipleLayerTransparentChild) {
+TEST_P(HitTestQueryTest, MultipleLayerTransparentChild) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -777,26 +770,14 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 5));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c1_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c1_bounds_in_e, transform_e_to_c1, 3));  // c1
-  active_data_.push_back(
-      AggregatedHitTestRegion(a_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              a_bounds_in_c1, transform_c1_to_a, 2));  // a
-  active_data_.push_back(
-      AggregatedHitTestRegion(b_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              b_bounds_in_a, transform_a_to_b, 1));  // b
-  active_data_.push_back(
-      AggregatedHitTestRegion(g_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              g_bounds_in_b, transform_b_to_g, 0));  // g
+  active_data_.push_back(AggregatedHitTestRegion(
+      c1_id, test_flags_, c1_bounds_in_e, transform_e_to_c1, 3));  // c1
+  active_data_.push_back(AggregatedHitTestRegion(
+      a_id, test_flags_, a_bounds_in_c1, transform_c1_to_a, 2));  // a
+  active_data_.push_back(AggregatedHitTestRegion(
+      b_id, test_flags_, b_bounds_in_a, transform_a_to_b, 1));  // b
+  active_data_.push_back(AggregatedHitTestRegion(
+      g_id, test_flags_, g_bounds_in_b, transform_b_to_g, 0));  // g
   active_data_.push_back(AggregatedHitTestRegion(
       c2_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -834,7 +815,7 @@
   EXPECT_TRUE(target4.flags);
 }
 
-TEST_F(HitTestQueryTest, InvalidAggregatedHitTestRegionData) {
+TEST_P(HitTestQueryTest, InvalidAggregatedHitTestRegionData) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -852,10 +833,7 @@
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 3));  // e
   active_data_.push_back(AggregatedHitTestRegion(
-      c_id,
-      HitTestRegionFlags::kHitTestChildSurface |
-          HitTestRegionFlags::kHitTestIgnore,
-      c_bounds_in_e, transform_e_to_c, INT32_MIN));  // c
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, INT32_MIN));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -889,11 +867,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, INT32_MAX));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 2));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 2));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -915,11 +890,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 3));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 3));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 3));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -938,7 +910,7 @@
 }
 
 // Tests flags kHitTestMouse and kHitTestTouch.
-TEST_F(HitTestQueryTest, MouseTouchFlags) {
+TEST_P(HitTestQueryTest, MouseTouchFlags) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId c2_id = FrameSinkId(3, 3);
@@ -997,7 +969,7 @@
                                HitTestRegionFlags::kHitTestTouch);
 }
 
-TEST_F(HitTestQueryTest, RootHitTestAskFlag) {
+TEST_P(HitTestQueryTest, RootHitTestAskFlag) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   gfx::Rect e_bounds = gfx::Rect(0, 0, 600, 600);
   gfx::Transform transform_e_to_e;
@@ -1036,7 +1008,7 @@
 //  | +---+ |     |      3        c2
 //  +-------------+
 //
-TEST_F(HitTestQueryTest, ChildHitTestAskFlag) {
+TEST_P(HitTestQueryTest, ChildHitTestAskFlag) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId c2_id = FrameSinkId(3, 3);
@@ -1102,7 +1074,7 @@
 //  |   ||   3     |
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, NestedOOPIFs) {
+TEST_P(HitTestQueryTest, NestedOOPIFs) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId b_id = FrameSinkId(3, 3);
@@ -1163,7 +1135,7 @@
 }
 
 // Tests getting the transform from root to a given target.
-TEST_F(HitTestQueryTest, GetTransformToTarget) {
+TEST_P(HitTestQueryTest, GetTransformToTarget) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   FrameSinkId a_id = FrameSinkId(3, 3);
@@ -1185,11 +1157,8 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 4));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore,
-                              c_bounds_in_e, transform_e_to_c, 2));  // c
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id, test_flags_, c_bounds_in_e, transform_e_to_c, 2));  // c
   active_data_.push_back(AggregatedHitTestRegion(
       a_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -1249,7 +1218,7 @@
 //  |   |          |
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, TransparentOverlayRegions) {
+TEST_P(HitTestQueryTest, TransparentOverlayRegions) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c1_id = FrameSinkId(2, 2);
   FrameSinkId c2_id = FrameSinkId(3, 3);
@@ -1266,12 +1235,9 @@
       e_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
       e_bounds_in_e, transform_e_to_e, 3));  // e
-  active_data_.push_back(
-      AggregatedHitTestRegion(c1_id,
-                              HitTestRegionFlags::kHitTestChildSurface |
-                                  HitTestRegionFlags::kHitTestIgnore |
-                                  HitTestRegionFlags::kHitTestMouse,
-                              c1_bounds_in_e, transform_e_to_c1, 1));  // c1
+  active_data_.push_back(AggregatedHitTestRegion(
+      c1_id, test_flags_ | HitTestRegionFlags::kHitTestMouse, c1_bounds_in_e,
+      transform_e_to_c1, 1));  // c1
   active_data_.push_back(AggregatedHitTestRegion(
       d1_id,
       HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
@@ -1301,7 +1267,7 @@
                                HitTestRegionFlags::kHitTestMouse);
 }
 
-TEST_F(HitTestQueryTest, FindTargetForLocationStartingFrom) {
+TEST_P(HitTestQueryTest, FindTargetForLocationStartingFrom) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
@@ -1360,7 +1326,7 @@
 //  |   |          |
 //  +--------------+
 //
-TEST_F(HitTestQueryTest, OverlappedRootView) {
+TEST_P(HitTestQueryTest, OverlappedRootView) {
   FrameSinkId e_id = FrameSinkId(1, 1);
   FrameSinkId c_id = FrameSinkId(2, 2);
   gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
diff --git a/components/web_resource/eula_accepted_notifier.cc b/components/web_resource/eula_accepted_notifier.cc
index 75a6b75c..8145233 100644
--- a/components/web_resource/eula_accepted_notifier.cc
+++ b/components/web_resource/eula_accepted_notifier.cc
@@ -33,8 +33,8 @@
   if (registrar_.IsEmpty()) {
     registrar_.Init(local_state_);
     registrar_.Add(prefs::kEulaAccepted,
-                   base::Bind(&EulaAcceptedNotifier::OnPrefChanged,
-                              base::Unretained(this)));
+                   base::BindRepeating(&EulaAcceptedNotifier::OnPrefChanged,
+                                       base::Unretained(this)));
   }
   return false;
 }
diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc
index 2188ae6..f98dfb21 100644
--- a/components/zoom/zoom_controller.cc
+++ b/components/zoom/zoom_controller.cc
@@ -46,8 +46,9 @@
   host_zoom_map_ = content::HostZoomMap::GetForWebContents(web_contents);
   zoom_level_ = host_zoom_map_->GetDefaultZoomLevel();
 
-  zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback(
-      base::Bind(&ZoomController::OnZoomLevelChanged, base::Unretained(this)));
+  zoom_subscription_ =
+      host_zoom_map_->AddZoomLevelChangedCallback(base::BindRepeating(
+          &ZoomController::OnZoomLevelChanged, base::Unretained(this)));
 
   UpdateState(std::string());
 }
@@ -335,8 +336,9 @@
     return;
 
   host_zoom_map_ = new_host_zoom_map;
-  zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback(
-      base::Bind(&ZoomController::OnZoomLevelChanged, base::Unretained(this)));
+  zoom_subscription_ =
+      host_zoom_map_->AddZoomLevelChangedCallback(base::BindRepeating(
+          &ZoomController::OnZoomLevelChanged, base::Unretained(this)));
 }
 
 void ZoomController::OnZoomLevelChanged(
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 9ac17c8e..a88a9b43 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -88,6 +88,10 @@
     return web_contents()->GetFrameTree()->root()->render_manager();
   }
 
+  std::string DepictFrameTree(FrameTreeNode* node) {
+    return visualizer_.DepictFrameTree(node);
+  }
+
   void ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome outcome,
                      base::Location location) {
     base::HistogramBase::Sample sample = base::HistogramBase::Sample(outcome);
@@ -168,6 +172,7 @@
 
   base::test::ScopedFeatureList feature_list_;
 
+  FrameTreeVisualizer visualizer_;
   base::HistogramTester histogram_tester_;
   std::vector<base::Bucket> expected_outcomes_;
   std::vector<base::Bucket> expected_disabled_reasons_;
@@ -932,6 +937,7 @@
   EXPECT_EQ(2u, render_frame_host_manager()->GetProxyCount());
   RenderFrameHostImpl* rfh_a = current_frame_host();
   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+  std::string frame_tree_a = DepictFrameTree(rfh_a->frame_tree_node());
 
   // 2. Navigate from a cacheable page to an uncacheable page (A->B).
   EXPECT_TRUE(NavigateToURL(shell(), url_b));
@@ -958,6 +964,10 @@
   delete_observer_rfh_b.WaitUntilDeleted();
   EXPECT_EQ(2u, render_frame_host_manager()->GetProxyCount());
 
+  // Page A should still have the correct frame tree.
+  EXPECT_EQ(frame_tree_a,
+            DepictFrameTree(current_frame_host()->frame_tree_node()));
+
   // 4. Navigate from a cacheable page to a cacheable page (A->C).
   EXPECT_TRUE(NavigateToURL(shell(), url_c));
   EXPECT_EQ(3u, render_frame_host_manager()->GetProxyCount());
@@ -977,6 +987,10 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   EXPECT_EQ(2u, render_frame_host_manager()->GetProxyCount());
 
+  // Page A should still have the correct frame tree.
+  EXPECT_EQ(frame_tree_a,
+            DepictFrameTree(current_frame_host()->frame_tree_node()));
+
   // Page C should be in the cache.
   EXPECT_FALSE(delete_observer_rfh_c.deleted());
   EXPECT_TRUE(rfh_c->is_in_back_forward_cache());
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 23620d9..dfe6997 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1285,6 +1285,16 @@
   if (!url_tuple_or_precursor_tuple.IsInvalid() &&
       !origin_tuple_or_precursor_tuple.IsInvalid() &&
       origin_tuple_or_precursor_tuple != url_tuple_or_precursor_tuple) {
+    // Allow a WebView specific exception for origins that have a data scheme.
+    // WebView converts data: URLs into non-opaque data:// origins which is
+    // different than what all other builds do. This causes the consistency
+    // check to fail because we try to compare a data:// origin with an opaque
+    // origin that contains precursor info.
+    if (url_tuple_or_precursor_tuple.scheme() == url::kDataScheme &&
+        url::AllowNonStandardSchemesForAndroidWebView()) {
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+    }
+
     return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
   }
 
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc
index 569503d..36785544 100644
--- a/content/browser/devtools/devtools_http_handler.cc
+++ b/content/browser/devtools/devtools_http_handler.cc
@@ -710,8 +710,8 @@
     scoped_refptr<DevToolsAgentHost> browser_agent =
         DevToolsAgentHost::CreateForBrowser(
             thread_->task_runner(),
-            base::Bind(&DevToolsSocketFactory::CreateForTethering,
-                       base::Unretained(socket_factory_.get())));
+            base::BindRepeating(&DevToolsSocketFactory::CreateForTethering,
+                                base::Unretained(socket_factory_.get())));
     connection_to_client_[connection_id].reset(new DevToolsAgentHostClientImpl(
         thread_->task_runner(), server_wrapper_.get(), connection_id,
         browser_agent));
diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
index 9fbcfe6f..e9263f2 100644
--- a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
+++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
@@ -111,8 +111,8 @@
       base::FilePath::FromUTF8Unsafe(download_helper->GetDownloadPath());
 
   FilenameDeterminedCallback filename_determined_callback =
-      base::Bind(&DevToolsDownloadManagerDelegate::OnDownloadPathGenerated,
-                 base::Unretained(this), item->GetId(), callback);
+      base::BindOnce(&DevToolsDownloadManagerDelegate::OnDownloadPathGenerated,
+                     base::Unretained(this), item->GetId(), callback);
 
   PostTask(
       FROM_HERE,
@@ -159,7 +159,7 @@
     const std::string& suggested_filename,
     const std::string& mime_type,
     const base::FilePath& suggested_directory,
-    const FilenameDeterminedCallback& callback) {
+    FilenameDeterminedCallback callback) {
   base::FilePath generated_name =
       net::GenerateFileName(url, content_disposition, std::string(),
                             suggested_filename, mime_type, "download");
@@ -169,7 +169,7 @@
 
   base::FilePath suggested_path(suggested_directory.Append(generated_name));
   base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(callback, suggested_path));
+                 base::BindOnce(std::move(callback), suggested_path));
 }
 
 void DevToolsDownloadManagerDelegate::OnDownloadPathGenerated(
diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.h b/content/browser/devtools/protocol/devtools_download_manager_delegate.h
index 89a4029..d93c870 100644
--- a/content/browser/devtools/protocol/devtools_download_manager_delegate.h
+++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.h
@@ -54,15 +54,15 @@
   static DevToolsDownloadManagerDelegate* GetInstance();
   ~DevToolsDownloadManagerDelegate() override;
 
-  typedef base::Callback<void(const base::FilePath&)>
-      FilenameDeterminedCallback;
+  using FilenameDeterminedCallback =
+      base::OnceCallback<void(const base::FilePath&)>;
 
   static void GenerateFilename(const GURL& url,
                                const std::string& content_disposition,
                                const std::string& suggested_filename,
                                const std::string& mime_type,
                                const base::FilePath& suggested_directory,
-                               const FilenameDeterminedCallback& callback);
+                               FilenameDeterminedCallback callback);
 
   void OnDownloadPathGenerated(uint32_t download_id,
                                const content::DownloadTargetCallback& callback,
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 15a5fd0d..693144d 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -1918,7 +1918,7 @@
   std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue());
   params = WaitForMatchingNotification(
       "Security.securityStateChanged",
-      base::Bind(&SecurityStateChangedHasCertificateExplanation));
+      base::BindRepeating(&SecurityStateChangedHasCertificateExplanation));
 
   // There should be one explanation containing the server's certificate chain.
   net::SHA256HashValue cert_chain_fingerprint =
@@ -2182,8 +2182,8 @@
 
   void WaitForCompletion(download::DownloadItem* download) {
     DownloadUpdatedObserver(
-        download,
-        base::Bind(&IsDownloadInState, download::DownloadItem::COMPLETE))
+        download, base::BindRepeating(&IsDownloadInState,
+                                      download::DownloadItem::COMPLETE))
         .WaitForEvent();
   }
 
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 5758c52..4675c01 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -766,10 +766,11 @@
   // We don't support clip/emulation when capturing from window, bail out.
   if (!from_surface.fromMaybe(true)) {
     widget_host->GetSnapshotFromBrowser(
-        base::Bind(&PageHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(),
-                   base::Passed(std::move(callback)), screenshot_format,
-                   screenshot_quality, gfx::Size(), gfx::Size(),
-                   blink::WebDeviceEmulationParams()),
+        base::BindOnce(&PageHandler::ScreenshotCaptured,
+                       weak_factory_.GetWeakPtr(),
+                       base::Passed(std::move(callback)), screenshot_format,
+                       screenshot_quality, gfx::Size(), gfx::Size(),
+                       blink::WebDeviceEmulationParams()),
         false);
     return;
   }
@@ -866,10 +867,11 @@
   }
 
   widget_host->GetSnapshotFromBrowser(
-      base::Bind(&PageHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(),
-                 base::Passed(std::move(callback)), screenshot_format,
-                 screenshot_quality, original_view_size, requested_image_size,
-                 original_params),
+      base::BindOnce(&PageHandler::ScreenshotCaptured,
+                     weak_factory_.GetWeakPtr(),
+                     base::Passed(std::move(callback)), screenshot_format,
+                     screenshot_quality, original_view_size,
+                     requested_image_size, original_params),
       true);
 }
 
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index c89f6a7a..f4a76b3b 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -91,13 +91,13 @@
 void GetDevToolsRouteInfoOnCoreThread(
     scoped_refptr<ServiceWorkerContextWrapper> context,
     int64_t version_id,
-    const base::Callback<void(int, int)>& callback) {
+    base::OnceCallback<void(int, int)> callback) {
   if (content::ServiceWorkerVersion* version =
           context->GetLiveVersion(version_id)) {
     RunOrPostTaskOnThread(
         FROM_HERE, BrowserThread::UI,
         base::BindOnce(
-            callback, version->embedded_worker()->process_id(),
+            std::move(callback), version->embedded_worker()->process_id(),
             version->embedded_worker()->worker_devtools_agent_route_id()));
   }
 }
@@ -219,12 +219,13 @@
   enabled_ = true;
 
   context_watcher_ = new ServiceWorkerContextWatcher(
-      context_, base::Bind(&ServiceWorkerHandler::OnWorkerRegistrationUpdated,
-                           weak_factory_.GetWeakPtr()),
-      base::Bind(&ServiceWorkerHandler::OnWorkerVersionUpdated,
-                 weak_factory_.GetWeakPtr()),
-      base::Bind(&ServiceWorkerHandler::OnErrorReported,
-                 weak_factory_.GetWeakPtr()));
+      context_,
+      base::BindRepeating(&ServiceWorkerHandler::OnWorkerRegistrationUpdated,
+                          weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&ServiceWorkerHandler::OnWorkerVersionUpdated,
+                          weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&ServiceWorkerHandler::OnErrorReported,
+                          weak_factory_.GetWeakPtr()));
   context_watcher_->Start();
 
   return Response::OK();
@@ -318,9 +319,10 @@
     return CreateInvalidVersionIdErrorResponse();
   RunOrPostTaskOnThread(
       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
-      base::BindOnce(&GetDevToolsRouteInfoOnCoreThread, context_, id,
-                     base::Bind(&ServiceWorkerHandler::OpenNewDevToolsWindow,
-                                weak_factory_.GetWeakPtr())));
+      base::BindOnce(
+          &GetDevToolsRouteInfoOnCoreThread, context_, id,
+          base::BindOnce(&ServiceWorkerHandler::OpenNewDevToolsWindow,
+                         weak_factory_.GetWeakPtr())));
   return Response::OK();
 }
 
diff --git a/content/browser/devtools/protocol/tethering_handler.cc b/content/browser/devtools/protocol/tethering_handler.cc
index eab85ac..ec47997 100644
--- a/content/browser/devtools/protocol/tethering_handler.cc
+++ b/content/browser/devtools/protocol/tethering_handler.cc
@@ -63,7 +63,7 @@
         })");
 
 using CreateServerSocketCallback =
-    base::Callback<std::unique_ptr<net::ServerSocket>(std::string*)>;
+    base::OnceCallback<std::unique_ptr<net::ServerSocket>(std::string*)>;
 
 class SocketPump {
  public:
@@ -73,9 +73,9 @@
         pending_destruction_(false) {
   }
 
-  std::string Init(const CreateServerSocketCallback& socket_callback) {
+  std::string Init(CreateServerSocketCallback socket_callback) {
     std::string channel_name;
-    server_socket_ = socket_callback.Run(&channel_name);
+    server_socket_ = std::move(socket_callback).Run(&channel_name);
     if (!server_socket_.get() || channel_name.empty()) {
       SelfDestruct();
       return std::string();
@@ -189,12 +189,13 @@
 
 class BoundSocket {
  public:
-  typedef base::Callback<void(uint16_t, const std::string&)> AcceptedCallback;
+  using AcceptedCallback =
+      base::OnceCallback<void(uint16_t, const std::string&)>;
 
   BoundSocket(AcceptedCallback accepted_callback,
-              const CreateServerSocketCallback& socket_callback)
-      : accepted_callback_(accepted_callback),
-        socket_callback_(socket_callback),
+              CreateServerSocketCallback socket_callback)
+      : accepted_callback_(std::move(accepted_callback)),
+        socket_callback_(std::move(socket_callback)),
         socket_(new net::TCPServerSocket(nullptr, net::NetLogSource())),
         port_(0) {}
 
@@ -243,9 +244,9 @@
       return;
 
     SocketPump* pump = new SocketPump(accept_socket_.release());
-    std::string name = pump->Init(socket_callback_);
+    std::string name = pump->Init(std::move(socket_callback_));
     if (!name.empty())
-      accepted_callback_.Run(port_, name);
+      std::move(accepted_callback_).Run(port_, name);
   }
 
   AcceptedCallback accepted_callback_;
@@ -279,9 +280,7 @@
 TetheringHandler::TetheringImpl::TetheringImpl(
     base::WeakPtr<TetheringHandler> handler,
     const CreateServerSocketCallback& socket_callback)
-    : handler_(handler),
-      socket_callback_(socket_callback) {
-}
+    : handler_(handler), socket_callback_(std::move(socket_callback)) {}
 
 TetheringHandler::TetheringImpl::~TetheringImpl() = default;
 
@@ -295,7 +294,7 @@
     return;
   }
 
-  BoundSocket::AcceptedCallback accepted = base::Bind(
+  BoundSocket::AcceptedCallback accepted = base::BindOnce(
       &TetheringHandler::TetheringImpl::Accepted, base::Unretained(this));
   std::unique_ptr<BoundSocket> bound_socket =
       std::make_unique<BoundSocket>(std::move(accepted), socket_callback_);
diff --git a/content/browser/devtools/protocol/tethering_handler.h b/content/browser/devtools/protocol/tethering_handler.h
index b0a83d7..7441af6 100644
--- a/content/browser/devtools/protocol/tethering_handler.h
+++ b/content/browser/devtools/protocol/tethering_handler.h
@@ -28,7 +28,7 @@
                          public Tethering::Backend {
  public:
   using CreateServerSocketCallback =
-      base::Callback<std::unique_ptr<net::ServerSocket>(std::string*)>;
+      base::RepeatingCallback<std::unique_ptr<net::ServerSocket>(std::string*)>;
 
   TetheringHandler(const CreateServerSocketCallback& socket_callback,
                    scoped_refptr<base::SingleThreadTaskRunner> task_runner);
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index 5832ebfa..1c5a8a0 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -26,6 +26,11 @@
 
 using blink::scheduler::WebSchedulerTrackedFeature;
 
+// Removes the time limit for cached content. This is used on bots to identify
+// accidentally passing tests.
+const base::Feature kBackForwardCacheNoTimeEviction{
+    "BackForwardCacheNoTimeEviction", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // The number of entries the BackForwardCache can hold per tab.
 static constexpr size_t kBackForwardCacheLimit = 1;
 
@@ -103,7 +108,8 @@
       ToFeatureBit(WebSchedulerTrackedFeature::kWebVR) |
       ToFeatureBit(WebSchedulerTrackedFeature::kWebXR) |
       ToFeatureBit(WebSchedulerTrackedFeature::kSharedWorker) |
-      ToFeatureBit(WebSchedulerTrackedFeature::kWebXR);
+      ToFeatureBit(WebSchedulerTrackedFeature::kWebXR) |
+      ToFeatureBit(WebSchedulerTrackedFeature::kWebLocks);
 
   uint64_t result = kAlwaysDisallowedFeatures;
 
@@ -147,10 +153,13 @@
 
 }  // namespace
 
-BackForwardCacheImpl::Entry::Entry(std::unique_ptr<RenderFrameHostImpl> rfh,
-                                   RenderFrameProxyHostMap proxies)
-    : render_frame_host(std::move(rfh)), proxy_hosts(std::move(proxies)) {}
-
+BackForwardCacheImpl::Entry::Entry(
+    std::unique_ptr<RenderFrameHostImpl> rfh,
+    RenderFrameProxyHostMap proxies,
+    std::set<RenderViewHostImpl*> render_view_hosts)
+    : render_frame_host(std::move(rfh)),
+      proxy_hosts(std::move(proxies)),
+      render_view_hosts(std::move(render_view_hosts)) {}
 BackForwardCacheImpl::Entry::~Entry() {}
 
 std::string BackForwardCacheImpl::CanStoreDocumentResult::ToString() {
@@ -232,6 +241,9 @@
 BackForwardCacheImpl::~BackForwardCacheImpl() = default;
 
 base::TimeDelta BackForwardCacheImpl::GetTimeToLiveInBackForwardCache() {
+  if (base::FeatureList::IsEnabled(kBackForwardCacheNoTimeEviction))
+    return base::TimeDelta::Max();
+
   return base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
       features::kBackForwardCache, "TimeToLiveInBackForwardCacheInSeconds",
       kDefaultTimeToLiveInBackForwardCacheInSeconds));
diff --git a/content/browser/frame_host/back_forward_cache_impl.h b/content/browser/frame_host/back_forward_cache_impl.h
index 6ee4dee..8b272088 100644
--- a/content/browser/frame_host/back_forward_cache_impl.h
+++ b/content/browser/frame_host/back_forward_cache_impl.h
@@ -26,6 +26,7 @@
 
 class RenderFrameHostImpl;
 class RenderFrameProxyHost;
+class RenderViewHostImpl;
 
 // BackForwardCache:
 //
@@ -41,7 +42,8 @@
                            std::unique_ptr<RenderFrameProxyHost>>;
 
     Entry(std::unique_ptr<RenderFrameHostImpl> rfh,
-          RenderFrameProxyHostMap proxy_hosts);
+          RenderFrameProxyHostMap proxy_hosts,
+          std::set<RenderViewHostImpl*> render_view_hosts);
     ~Entry();
 
     // The main document being stored.
@@ -53,6 +55,17 @@
     // cached.
     RenderFrameProxyHostMap proxy_hosts;
 
+    // RenderViewHosts belonging to the main frame, and its proxies (if any).
+    //
+    // While RenderViewHostImpl(s) are in the BackForwardCache, they aren't
+    // reused for pages outside the cache. This prevents us from having two main
+    // frames, (one in the cache, one live), associated with a single
+    // RenderViewHost.
+    //
+    // Keeping these here also prevents RenderFrameHostManager code from
+    // unwittingly iterating over RenderViewHostImpls that are in the cache.
+    std::set<RenderViewHostImpl*> render_view_hosts;
+
     DISALLOW_COPY_AND_ASSIGN(Entry);
   };
 
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index 68e89c8e..e842fe0 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -14,6 +14,7 @@
 #include "base/callback.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "base/unguessable_token.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
@@ -380,7 +381,7 @@
       static_cast<RenderViewHostImpl*>(RenderViewHostFactory::Create(
           site_instance, render_view_delegate_, render_widget_delegate_,
           routing_id, main_frame_routing_id, widget_routing_id, swapped_out));
-  render_view_host_map_[site_instance->GetId()] = rvh;
+  RegisterRenderViewHost(rvh);
   return base::WrapRefCounted(rvh);
 }
 
@@ -393,7 +394,13 @@
   return base::WrapRefCounted(it->second);
 }
 
-void FrameTree::RenderViewHostDeleted(RenderViewHost* rvh) {
+void FrameTree::RegisterRenderViewHost(RenderViewHostImpl* rvh) {
+  CHECK(
+      !base::Contains(render_view_host_map_, rvh->GetSiteInstance()->GetId()));
+  render_view_host_map_[rvh->GetSiteInstance()->GetId()] = rvh;
+}
+
+void FrameTree::UnregisterRenderViewHost(RenderViewHostImpl* rvh) {
   auto it = render_view_host_map_.find(rvh->GetSiteInstance()->GetId());
   CHECK(it != render_view_host_map_.end());
   CHECK_EQ(it->second, rvh);
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index f32aadd..092e9cf2 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -222,11 +222,24 @@
   scoped_refptr<RenderViewHostImpl> GetRenderViewHost(
       SiteInstance* site_instance);
 
-  // The FrameTree maintains a list of existing RenderViewHostImpl so that
-  // FrameTree::CreateRenderViewHost() can return them directly instead of
-  // creating a new one. Calling this function removes it from the list when the
-  // |render_view_host| is deleted.
-  void RenderViewHostDeleted(RenderViewHost* render_view_host);
+  // Registers a RenderViewHost so that it can be reused by other frames
+  // belonging to the same SiteInstance.
+  //
+  // This method does not take ownership of|rvh|.
+  //
+  // NOTE: This method CHECK fails if a RenderViewHost is already registered for
+  // |rvh|'s SiteInstance.
+  //
+  // ALSO NOTE: After calling RegisterRenderViewHost, UnregisterRenderViewHost
+  // *must* be called for |rvh| when it is destroyed or put into the
+  // BackForwardCache, to prevent FrameTree::CreateRenderViewHost from trying to
+  // reuse it.
+  void RegisterRenderViewHost(RenderViewHostImpl* rvh);
+
+  // Unregisters the RenderViewHostImpl that's available for reuse for a
+  // particular SiteInstance. NOTE: This method CHECK fails if it is called for
+  // a |render_view_host| that is not currently set for reuse.
+  void UnregisterRenderViewHost(RenderViewHostImpl* render_view_host);
 
   // This is called when the frame is about to be removed and started to run
   // unload handlers.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 9e32310..08a8d9f 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -6272,6 +6272,7 @@
 
 void RenderFrameHostImpl::CreateWebBluetoothService(
     mojo::PendingReceiver<blink::mojom::WebBluetoothService> receiver) {
+  BackForwardCache::DisableForRenderFrameHost(this, "WebBluetooth");
   // RFHI owns |web_bluetooth_services_| and |web_bluetooth_service| owns the
   // |receiver_| which may run the error handler. |receiver_| can't run the
   // error handler after it's destroyed so it can't run after the RFHI is
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 7a136b4e..7ffd769 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -507,8 +507,12 @@
     TRACE_EVENT1("navigation", "BackForwardCache_MaybeStorePage", "can_store",
                  can_store.ToString());
     if (can_store) {
+      std::set<RenderViewHostImpl*> old_render_view_hosts;
+
       // Prepare the main frame.
       back_forward_cache.Freeze(old_render_frame_host.get());
+      old_render_view_hosts.insert(static_cast<RenderViewHostImpl*>(
+          old_render_frame_host->GetRenderViewHost()));
 
       // Prepare the proxies.
       RenderFrameProxyHostMap old_proxy_hosts;
@@ -517,8 +521,10 @@
         // This avoids including the proxy created when starting a
         // new cross-process, cross-BrowsingInstance navigation, as well as any
         // restored proxies which are also in a different BrowsingInstance.
-        if (instance->IsRelatedSiteInstance(it.second->GetSiteInstance()))
+        if (instance->IsRelatedSiteInstance(it.second->GetSiteInstance())) {
+          old_render_view_hosts.insert(it.second->GetRenderViewHost());
           old_proxy_hosts[it.first] = std::move(it.second);
+        }
       }
       // Remove the previously extracted proxies from the
       // RenderFrameHostManager, which also remove their respective
@@ -526,8 +532,13 @@
       for (auto& it : old_proxy_hosts)
         DeleteRenderFrameProxyHost(it.second->GetSiteInstance());
 
+      // Ensures RenderViewHosts are not reused while they are in the cache.
+      for (RenderViewHostImpl* rvh : old_render_view_hosts)
+        rvh->EnterBackForwardCache();
+
       auto entry = std::make_unique<BackForwardCacheImpl::Entry>(
-          std::move(old_render_frame_host), std::move(old_proxy_hosts));
+          std::move(old_render_frame_host), std::move(old_proxy_hosts),
+          std::move(old_render_view_hosts));
       back_forward_cache.StoreEntry(std::move(entry));
       return;
     }
@@ -2494,9 +2505,9 @@
   // If a document is being restored from the BackForwardCache, restore all
   // cached state now.
   if (pending_bfcache_entry) {
-    RenderFrameProxyHostMap pending_proxy_hosts =
+    RenderFrameProxyHostMap proxy_hosts_to_restore =
         std::move(pending_bfcache_entry->proxy_hosts);
-    for (auto& proxy : pending_proxy_hosts) {
+    for (auto& proxy : proxy_hosts_to_restore) {
       // We only cache pages when swapping BrowsingInstance, so we should never
       // be reusing SiteInstances.
       CHECK(!base::Contains(proxy_hosts_,
@@ -2505,6 +2516,11 @@
           ->AddObserver(this);
       proxy_hosts_.insert(std::move(proxy));
     }
+
+    std::set<RenderViewHostImpl*> render_view_hosts_to_restore =
+        std::move(pending_bfcache_entry->render_view_hosts);
+    for (RenderViewHostImpl* rvh : render_view_hosts_to_restore)
+      rvh->LeaveBackForwardCache();
   }
 
   // For top-level frames, the RenderWidget{Host} will not be destroyed when the
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 711f83aa..c45fbed 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/hash/hash.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -128,34 +129,16 @@
 class RenderFrameHostManagerTestWebUIControllerFactory
     : public WebUIControllerFactory {
  public:
-  RenderFrameHostManagerTestWebUIControllerFactory()
-      : should_create_webui_(false), type_(1) {
-    CHECK_NE(reinterpret_cast<WebUI::TypeID>(type_), WebUI::kNoWebUI);
-  }
+  RenderFrameHostManagerTestWebUIControllerFactory() {}
   ~RenderFrameHostManagerTestWebUIControllerFactory() override {}
 
-  void set_should_create_webui(bool should_create_webui) {
-    should_create_webui_ = should_create_webui;
-  }
-
-  // This method simulates the expectation that different WebUI instance types
-  // would be created. The |type| value will be returned by GetWebUIType casted
-  // to WebUI::TypeID.
-  // As WebUI::TypeID is a typedef to void pointer, factory implementations
-  // return values that they know to be unique to their respective cases. So
-  // values set here should be safe if kept very low (just above zero).
-  void set_webui_type(uintptr_t type) {
-    CHECK_NE(reinterpret_cast<WebUI::TypeID>(type), WebUI::kNoWebUI);
-    type_ = type;
-  }
-
   // WebUIFactory implementation.
   std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
       WebUI* web_ui,
       const GURL& url) override {
     // If WebUI creation is enabled for the test and this is a WebUI URL,
     // returns a new instance.
-    if (should_create_webui_ && HasWebUIScheme(url))
+    if (HasWebUIScheme(url))
       return std::make_unique<WebUIController>(web_ui);
     return nullptr;
   }
@@ -164,8 +147,8 @@
                              const GURL& url) override {
     // If WebUI creation is enabled for the test and this is a WebUI URL,
     // returns a mock WebUI type.
-    if (should_create_webui_ && HasWebUIScheme(url)) {
-      return reinterpret_cast<WebUI::TypeID>(type_);
+    if (HasWebUIScheme(url)) {
+      return reinterpret_cast<WebUI::TypeID>(base::Hash(url.host()));
     }
     return WebUI::kNoWebUI;
   }
@@ -181,9 +164,6 @@
   }
 
  private:
-  bool should_create_webui_;
-  uintptr_t type_;
-
   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
 };
 
@@ -352,12 +332,6 @@
     WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
   }
 
-  void set_should_create_webui(bool should_create_webui) {
-    factory_.set_should_create_webui(should_create_webui);
-  }
-
-  void set_webui_type(int type) { factory_.set_webui_type(type); }
-
   GURL isolated_cross_site_url() const {
     return GURL("http://isolated-cross-site.com");
   }
@@ -477,7 +451,6 @@
 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
 // a regression test for bug 9364.
 TEST_F(RenderFrameHostManagerTest, ChromeSchemeProcesses) {
-  set_should_create_webui(true);
   const GURL kChromeUrl(GetWebUIURL("foo"));
   const GURL kDestUrl("http://www.google.com/");
 
@@ -970,7 +943,6 @@
 
 // Tests WebUI creation.
 TEST_F(RenderFrameHostManagerTest, WebUI) {
-  set_should_create_webui(true);
   scoped_refptr<SiteInstance> instance =
       SiteInstance::Create(browser_context());
 
@@ -1021,7 +993,6 @@
 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
 // grant the correct bindings.  http://crbug.com/189101.
 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
-  set_should_create_webui(true);
   scoped_refptr<SiteInstance> blank_instance =
       SiteInstance::Create(browser_context());
   blank_instance->GetProcess()->Init();
@@ -1093,8 +1064,6 @@
 
 // Tests that a WebUI is correctly reused between chrome:// pages.
 TEST_F(RenderFrameHostManagerTest, WebUIWasReused) {
-  set_should_create_webui(true);
-
   // Navigate to a WebUI page.
   const GURL kUrl1(GetWebUIURL("foo"));
   contents()->NavigateAndCommit(kUrl1);
@@ -1111,8 +1080,6 @@
 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
 // page to a non-chrome:// page.
 TEST_F(RenderFrameHostManagerTest, WebUIWasCleared) {
-  set_should_create_webui(true);
-
   // Navigate to a WebUI page.
   const GURL kUrl1(GetWebUIURL("foo"));
   contents()->NavigateAndCommit(kUrl1);
@@ -1438,50 +1405,6 @@
       rfh2->GetRenderViewHost()->opener_frame_route_id());
 }
 
-// Test that RenderViewHosts created for WebUI navigations are properly
-// granted WebUI bindings even if an unprivileged swapped out RenderViewHost
-// is in the same process (http://crbug.com/79918).
-TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
-  set_should_create_webui(true);
-  const GURL kSettingsUrl(GetWebUIURL("chrome/settings"));
-  const GURL kPluginUrl(GetWebUIURL("plugins"));
-
-  // Navigate to an initial WebUI URL.
-  contents()->NavigateAndCommit(kSettingsUrl);
-
-  // Ensure the RVH has WebUI bindings.
-  TestRenderViewHost* rvh1 = test_rvh();
-  EXPECT_TRUE(rvh1->GetMainFrame()->GetEnabledBindings() &
-              BINDINGS_POLICY_WEB_UI);
-
-  // Create a new tab and simulate it being the opener for the main
-  // tab.  It should be in the same SiteInstance.
-  std::unique_ptr<TestWebContents> opener1(
-      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
-  RenderFrameHostManager* opener1_manager =
-      opener1->GetRenderManagerForTesting();
-  contents()->SetOpener(opener1.get());
-
-  // Navigate to a different WebUI URL (different SiteInstance, same
-  // BrowsingInstance).
-  contents()->NavigateAndCommit(kPluginUrl);
-  TestRenderViewHost* rvh2 = test_rvh();
-  EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
-  EXPECT_TRUE(
-      rvh1->GetSiteInstance()->IsRelatedSiteInstance(rvh2->GetSiteInstance()));
-
-  // Ensure a proxy and swapped out RVH are created in the first opener tab.
-  EXPECT_TRUE(
-      opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance()));
-  TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
-      opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
-  EXPECT_FALSE(opener1_rvh->is_active());
-
-  // Ensure the new RVH has WebUI bindings.
-  EXPECT_TRUE(rvh2->GetMainFrame()->GetEnabledBindings() &
-              BINDINGS_POLICY_WEB_UI);
-}
-
 // Test that we reuse the same guest SiteInstance if we navigate across sites.
 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
   GURL guest_url(std::string(kGuestScheme).append("://abc123"));
@@ -2057,8 +1980,6 @@
 // See https://crbug.com/536145.
 TEST_F(RenderFrameHostManagerTestWithSiteIsolation,
        DontGrantPendingWebUIToSubframe) {
-  set_should_create_webui(true);
-
   // Make sure the initial process is live so that the pending WebUI navigation
   // does not commit immediately.  Give the page a subframe as well.
   const GURL kUrl1("http://foo.com");
@@ -2516,8 +2437,6 @@
 
 // Checks that a restore navigation to a WebUI works.
 TEST_F(RenderFrameHostManagerTest, RestoreNavigationToWebUI) {
-  set_should_create_webui(true);
-
   const GURL kInitUrl(GetWebUIURL("foo"));
   scoped_refptr<SiteInstanceImpl> initial_instance =
       SiteInstanceImpl::Create(browser_context());
@@ -2576,7 +2495,6 @@
 // Simulates two simultaneous navigations involving one WebUI where the current
 // RenderFrameHost commits.
 TEST_F(RenderFrameHostManagerTest, SimultaneousNavigationWithOneWebUI1) {
-  set_should_create_webui(true);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo/"));
 
@@ -2638,7 +2556,6 @@
 // Simulates two simultaneous navigations involving one WebUI where the new,
 // cross-site RenderFrameHost commits.
 TEST_F(RenderFrameHostManagerTest, SimultaneousNavigationWithOneWebUI2) {
-  set_should_create_webui(true);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo/"));
 
@@ -2696,8 +2613,6 @@
 // Simulates two simultaneous navigations involving two WebUIs where the current
 // RenderFrameHost commits.
 TEST_F(RenderFrameHostManagerTest, SimultaneousNavigationWithTwoWebUIs1) {
-  set_should_create_webui(true);
-  set_webui_type(1);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo"));
 
@@ -2718,8 +2633,7 @@
   EXPECT_EQ(web_ui1, host1->pending_web_ui());
   EXPECT_FALSE(GetPendingFrameHost(manager));
 
-  // Navigation another WebUI page, with a different type.
-  set_webui_type(2);
+  // Navigate to another WebUI page.
   const GURL kUrl(GetWebUIURL("bar"));
   auto navigation =
       NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
@@ -2762,8 +2676,6 @@
 // Simulates two simultaneous navigations involving two WebUIs where the new,
 // cross-site RenderFrameHost commits.
 TEST_F(RenderFrameHostManagerTest, SimultaneousNavigationWithTwoWebUIs2) {
-  set_should_create_webui(true);
-  set_webui_type(1);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo/"));
 
@@ -2784,8 +2696,7 @@
   EXPECT_EQ(web_ui1, host1->pending_web_ui());
   EXPECT_FALSE(GetPendingFrameHost(manager));
 
-  // Navigation another WebUI page, with a different type.
-  set_webui_type(2);
+  // Navigate to another WebUI page.
   const GURL kUrl(GetWebUIURL("bar/"));
   auto navigation =
       NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
@@ -2872,7 +2783,6 @@
 // Tests that the correct intermediary and final navigation states are reached
 // when navigating from a renderer that is not live to a WebUI URL.
 TEST_F(RenderFrameHostManagerTest, NavigateFromDeadRendererToWebUI) {
-  set_should_create_webui(true);
   RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
 
   RenderFrameHostImpl* initial_host = manager->current_frame_host();
@@ -2948,7 +2858,6 @@
 // Tests that the correct intermediary and final navigation states are reached
 // when navigating same-site between two WebUIs of the same type.
 TEST_F(RenderFrameHostManagerTest, NavigateSameSiteBetweenWebUIs) {
-  set_should_create_webui(true);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo"));
 
@@ -2991,8 +2900,6 @@
 TEST_F(RenderFrameHostManagerTest, NavigateCrossSiteBetweenWebUIs) {
   // Cross-site navigations will always cause the change of the WebUI instance
   // but for consistency sake different types will be set for each navigation.
-  set_should_create_webui(true);
-  set_webui_type(1);
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(),
                                                     GetWebUIURL("foo"));
 
@@ -3001,12 +2908,9 @@
   EXPECT_TRUE(host->IsRenderFrameLive());
   EXPECT_TRUE(host->web_ui());
 
-  // Set the WebUI controller to return a different WebUIType value. This will
-  // cause the next navigation to "chrome://bar" to require a different WebUI
-  // than the current one, forcing it to be treated as cross-site.
-  set_webui_type(2);
-
-  // Navigation request.
+  // Navigate to different WebUI. This will cause the next navigation to
+  // "chrome://bar" to require a different WebUI than the current one,
+  // forcing it to be treated as cross-site.
   const GURL kUrl(GetWebUIURL("bar"));
   auto web_ui_navigation =
       NavigationSimulator::CreateBrowserInitiated(kUrl, contents());
@@ -3021,15 +2925,8 @@
   WebUIImpl* next_web_ui = manager->GetNavigatingWebUI();
   EXPECT_TRUE(next_web_ui);
   EXPECT_EQ(next_web_ui, speculative_host->web_ui());
-  EXPECT_NE(next_web_ui, manager->current_frame_host()->web_ui());
-  EXPECT_FALSE(speculative_host->pending_web_ui());
-
-  EXPECT_TRUE(manager->current_frame_host()->web_ui());
-  EXPECT_FALSE(manager->current_frame_host()->pending_web_ui());
-  EXPECT_EQ(speculative_host, GetPendingFrameHost(manager));
-  EXPECT_NE(next_web_ui, manager->current_frame_host()->web_ui());
-  EXPECT_EQ(next_web_ui, speculative_host->web_ui());
   EXPECT_EQ(next_web_ui, manager->GetNavigatingWebUI());
+  EXPECT_NE(next_web_ui, manager->current_frame_host()->web_ui());
   EXPECT_FALSE(speculative_host->pending_web_ui());
 
   // The RenderFrameHost committed.
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index d51d42f0..ac1ebbb 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -224,10 +224,6 @@
       is_swapped_out_(swapped_out),
       routing_id_(routing_id),
       main_frame_routing_id_(main_frame_routing_id),
-      is_waiting_for_close_ack_(false),
-      sudden_termination_allowed_(false),
-      updating_web_preferences_(false),
-      has_notified_about_creation_(false),
       visual_properties_manager_(this) {
   DCHECK(instance_.get());
   CHECK(delegate_);  // http://crbug.com/82827
@@ -287,8 +283,12 @@
 
   // This can be called inside the FrameTree destructor. When the delegate is
   // the InterstialPageImpl, the |frame_tree| is set to null before deleting it.
-  if (FrameTree* frame_tree = GetDelegate()->GetFrameTree())
-    frame_tree->RenderViewHostDeleted(this);
+  if (FrameTree* frame_tree = GetDelegate()->GetFrameTree()) {
+    // If |this| is in the BackForwardCache, then it was already removed from
+    // the FrameTree at the time it entered the BackForwardCache.
+    if (!is_in_back_forward_cache_)
+      frame_tree->UnregisterRenderViewHost(this);
+  }
 }
 
 RenderViewHostDelegate* RenderViewHostImpl::GetDelegate() {
@@ -405,6 +405,20 @@
   GetWidget()->UpdatePriority();
 }
 
+void RenderViewHostImpl::EnterBackForwardCache() {
+  FrameTree* frame_tree = GetDelegate()->GetFrameTree();
+  frame_tree->UnregisterRenderViewHost(this);
+  is_in_back_forward_cache_ = true;
+}
+
+void RenderViewHostImpl::LeaveBackForwardCache() {
+  FrameTree* frame_tree = GetDelegate()->GetFrameTree();
+  // At this point, the frames |this| RenderViewHostImpl belongs to are
+  // guaranteed to be committed, so it should be reused going forward.
+  frame_tree->RegisterRenderViewHost(this);
+  is_in_back_forward_cache_ = false;
+}
+
 bool RenderViewHostImpl::IsRenderViewLive() {
   return GetProcess()->IsInitializedAndNotDead() &&
          GetWidget()->renderer_initialized();
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 280fc36..8053f72 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -207,6 +207,15 @@
   // view is not considered active.
   void SetMainFrameRoutingId(int routing_id);
 
+  // Called when the RenderFrameHostImpls/RenderFrameProxyHosts that own this
+  // RenderViewHost enter the BackForwardCache.
+  void EnterBackForwardCache();
+
+  // Called when the RenderFrameHostImpls/RenderFrameProxyHosts that own this
+  // RenderViewHost leave the BackForwardCache. This occurs immediately before a
+  // restored document is committed.
+  void LeaveBackForwardCache();
+
   // Called during frame eviction to return all SurfaceIds in the frame tree.
   // Marks all views in the frame tree as evicted.
   std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction();
@@ -329,10 +338,10 @@
   // Set to true when waiting for a ViewHostMsg_ClosePageACK.
   // TODO(creis): Move to RenderFrameHost and RenderWidgetHost.
   // See http://crbug.com/418265.
-  bool is_waiting_for_close_ack_;
+  bool is_waiting_for_close_ack_ = false;
 
   // True if the render view can be shut down suddenly.
-  bool sudden_termination_allowed_;
+  bool sudden_termination_allowed_ = false;
 
   // This is updated every time UpdateWebkitPreferences is called. That method
   // is in turn called when any of the settings change that the WebPreferences
@@ -348,14 +357,17 @@
   // This monitors input changes so they can be reflected to the interaction MQ.
   std::unique_ptr<InputDeviceChangeObserver> input_device_change_observer_;
 
-  bool updating_web_preferences_;
+  bool updating_web_preferences_ = false;
 
   // This tracks whether this RenderViewHost has notified observers about its
   // creation with RenderViewCreated.  RenderViewHosts may transition from
   // active (with a RenderFrameHost for the main frame) to inactive state and
   // then back to active, and for the latter transition, this avoids firing
   // duplicate RenderViewCreated events.
-  bool has_notified_about_creation_;
+  bool has_notified_about_creation_ = false;
+
+  // BackForwardCache:
+  bool is_in_back_forward_cache_ = false;
 
   // Used to send Page and Widget visual properties to Renderers.
   VisualPropertiesManager visual_properties_manager_;
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index f8c60b4..5b93be81 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1329,9 +1329,18 @@
   // platforms without site-per-process.
   IsolateOrigin("a.com");
 
+  // IsolateOrigin internally performs navigations which get stored into the
+  // back-forward cache. It needs to be flushed. The
+  // BeginNavigationInitiatorReplacer below will simulate receiving
+  // RenderFrameCreated() on every active RenderFrameHost, but it will miss the
+  // ones in the BackForwardCache. This would causes a mismatch later when it
+  // will observe RenderFrameDeleted.
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  web_contents->GetController().GetBackForwardCache().Flush();
+
   // Prepare to intercept BeginNavigation mojo IPC.  This has to be done before
   // the test creates the RenderFrameHostImpl that is the target of the IPC.
-  WebContents* web_contents = shell()->web_contents();
   BeginNavigationInitiatorReplacer injector(
       web_contents, url::Origin::Create(GURL("http://b.com")));
 
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 64c32d8..8dc989a 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -2811,6 +2811,73 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
+                       PointerEventsNoneWithNestedSameOriginIFrame) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "/frame_tree/page_with_same_origin_nested_frames.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  ASSERT_EQ(1U, root->child_count());
+  RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
+      root->current_frame_host()->GetRenderWidgetHost()->GetView());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site A ------- proxies for B\n"
+      "        +--Site B -- proxies for A\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://baz.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child_node = root->child_at(0);
+  FrameTreeNode* grandchild_node = child_node->child_at(0);
+
+  // This is to make sure that the hit_test_data is clean before running the
+  // hit_test_data_change_observer.
+  WaitForHitTestData(child_node->current_frame_host());
+  WaitForHitTestData(grandchild_node->current_frame_host());
+
+  HitTestRegionObserver hit_test_data_change_observer(
+      root_view->GetRootFrameSinkId());
+  hit_test_data_change_observer.WaitForHitTestData();
+
+  EXPECT_TRUE(ExecuteScript(web_contents(),
+                            "document.getElementById('wrapper').style."
+                            "pointerEvents = 'none';"));
+
+  hit_test_data_change_observer.WaitForHitTestDataChange();
+
+  MainThreadFrameObserver observer(
+      root->current_frame_host()->GetRenderWidgetHost());
+  observer.Wait();
+
+  // ------------------------
+  // root    50px
+  //     ---------------------
+  //     |child  50px        |
+  // 50px|    -------------- |
+  //     |50px| grand_child ||
+  //     |    |             ||
+  //     |    |-------------||
+  //     ---------------------
+
+  // DispatchMouseEventAndWaitUntilDispatch will make sure the mouse event goes
+  // to the right frame. Create a listener for the grandchild to verify that it
+  // does not receive the event. No need to create one for the child because
+  // root and child are on the same process.
+  RenderWidgetHostMouseEventMonitor grandchild_frame_monitor(
+      grandchild_node->current_frame_host()->GetRenderWidgetHost());
+
+  // Since child has pointer-events: none, (125, 125) should be claimed by root.
+  DispatchMouseEventAndWaitUntilDispatch(web_contents(), root_view,
+                                         gfx::PointF(125, 125), root_view,
+                                         gfx::PointF(125, 125));
+  EXPECT_FALSE(grandchild_frame_monitor.EventWasReceived());
+}
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
                        PointerEventsNoneWithNestedOOPIF) {
   GURL main_url(embedded_test_server()->GetURL(
       "/frame_tree/page_with_positioned_nested_frames.html"));
@@ -2854,12 +2921,6 @@
       root->current_frame_host()->GetRenderWidgetHost());
   observer.Wait();
 
-  // Create listeners for mouse events.
-  RenderWidgetHostMouseEventMonitor main_frame_monitor(
-      root->current_frame_host()->GetRenderWidgetHost());
-  RenderWidgetHostMouseEventMonitor child_frame_monitor(
-      child_node->current_frame_host()->GetRenderWidgetHost());
-
   // ------------------------
   // root    50px
   //     ---------------------
@@ -2869,30 +2930,17 @@
   //     |    |             ||
   //     |    |-------------||
   //     ---------------------
-  //
+
+  // DispatchMouseEventAndWaitUntilDispatch will make sure the mouse event goes
+  // to the right frame. Create a listener for the child to verify that it does
+  // not receive the event.
+  RenderWidgetHostMouseEventMonitor child_frame_monitor(
+      child_node->current_frame_host()->GetRenderWidgetHost());
+
   // Since child has pointer-events: none, (125, 125) should be claimed by root.
-  blink::WebMouseEvent mouse_event(
-      blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
-      blink::WebInputEvent::GetStaticTimeStampForTests());
-  mouse_event.button = blink::WebPointerProperties::Button::kLeft;
-  SetWebEventPositions(&mouse_event, gfx::Point(125, 125), root_view);
-  mouse_event.click_count = 1;
-
-  main_frame_monitor.ResetEventReceived();
-  child_frame_monitor.ResetEventReceived();
-
-  InputEventAckWaiter waiter(root->current_frame_host()->GetRenderWidgetHost(),
-                             blink::WebInputEvent::kMouseDown);
-  RenderWidgetHostInputEventRouter* router =
-      web_contents()->GetInputEventRouter();
-  router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
-  waiter.Wait();
-
-  EXPECT_TRUE(main_frame_monitor.EventWasReceived());
-  EXPECT_NEAR(125, main_frame_monitor.event().PositionInWidget().x,
-              kHitTestTolerance);
-  EXPECT_NEAR(125, main_frame_monitor.event().PositionInWidget().y,
-              kHitTestTolerance);
+  DispatchMouseEventAndWaitUntilDispatch(web_contents(), root_view,
+                                         gfx::PointF(125, 125), root_view,
+                                         gfx::PointF(125, 125));
   EXPECT_FALSE(child_frame_monitor.EventWasReceived());
 }
 
diff --git a/content/public/test/back_forward_cache_util.cc b/content/public/test/back_forward_cache_util.cc
index 4916450..1802778 100644
--- a/content/public/test/back_forward_cache_util.cc
+++ b/content/public/test/back_forward_cache_util.cc
@@ -13,6 +13,12 @@
 
 namespace content {
 
+bool IsInBackForwardCache(RenderFrameHost* render_frame_host) {
+  RenderFrameHostImpl* rfhi =
+      static_cast<RenderFrameHostImpl*>(render_frame_host);
+  return rfhi->is_in_back_forward_cache();
+}
+
 class BackForwardCacheDisabledTester::Impl
     : public BackForwardCacheTestDelegate {
  public:
diff --git a/content/public/test/back_forward_cache_util.h b/content/public/test/back_forward_cache_util.h
index 2569ccb..9364cb2 100644
--- a/content/public/test/back_forward_cache_util.h
+++ b/content/public/test/back_forward_cache_util.h
@@ -9,6 +9,12 @@
 
 namespace content {
 class BackForwardCacheImpl;
+class RenderFrameHost;
+
+// Returns true if |render_frame_host| is currently stored in the
+// BackForwardCache.
+bool IsInBackForwardCache(RenderFrameHost* render_frame_host)
+    WARN_UNUSED_RESULT;
 
 // This is a helper class to check in the tests that back-forward cache
 // was disabled for a particular reason.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 2b94ace..ef4c3e6 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -88,7 +88,6 @@
 #include "content/public/test/test_fileapi_operation_waiter.h"
 #include "content/public/test/test_launcher.h"
 #include "content/public/test/test_navigation_observer.h"
-#include "content/public/test/test_utils.h"
 #include "content/test/did_commit_navigation_interceptor.h"
 #include "ipc/ipc_security_test_util.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 81784ef..d11956c 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -204,12 +204,6 @@
     "media/webrtc/peer_connection_tracker.h",
     "media/webrtc/rtc_peer_connection_handler.cc",
     "media/webrtc/rtc_peer_connection_handler.h",
-    "media/webrtc/rtc_rtp_sender.cc",
-    "media/webrtc/rtc_rtp_sender.h",
-    "media/webrtc/rtc_rtp_transceiver.cc",
-    "media/webrtc/rtc_rtp_transceiver.h",
-    "media/webrtc/transceiver_state_surfacer.cc",
-    "media/webrtc/transceiver_state_surfacer.h",
     "media/webrtc/webrtc_set_description_observer.cc",
     "media/webrtc/webrtc_set_description_observer.h",
     "menu_item_builder.cc",
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
index fd0c0c9..b3492c4 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 #include "base/test/task_environment.h"
-#include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h"
 
 namespace content {
 
@@ -67,7 +67,7 @@
 };
 
 TEST_F(PeerConnectionDependencyFactoryTest, CreateRTCPeerConnectionHandler) {
-  MockWebRTCPeerConnectionHandlerClient client_jsep;
+  blink::MockWebRTCPeerConnectionHandlerClient client_jsep;
   std::unique_ptr<blink::WebRTCPeerConnectionHandler> pc_handler(
       dependency_factory_->CreateRTCPeerConnectionHandler(
           &client_jsep,
diff --git a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
index 040e25a29..9c3da6d 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
@@ -8,8 +8,6 @@
 #include "content/common/media/peer_connection_tracker.mojom.h"
 #include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/public/test/mock_render_thread.h"
-#include "content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h"
-#include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "ipc/ipc_message_macros.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
@@ -21,7 +19,9 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
+#include "third_party/blink/public/web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h"
 
 using ::testing::_;
 
@@ -91,29 +91,29 @@
 std::unique_ptr<blink::WebRTCRtpTransceiver> CreateDefaultTransceiver(
     blink::WebRTCRtpTransceiverImplementationType implementation_type) {
   std::unique_ptr<blink::WebRTCRtpTransceiver> transceiver;
-  FakeRTCRtpSender sender(
+  blink::FakeRTCRtpSenderImpl sender(
       "senderTrackId", {"senderStreamId"},
       blink::scheduler::GetSingleThreadTaskRunnerForTesting());
-  FakeRTCRtpReceiver receiver(
+  blink::FakeRTCRtpReceiverImpl receiver(
       "receiverTrackId", {"receiverStreamId"},
       blink::scheduler::GetSingleThreadTaskRunnerForTesting());
   if (implementation_type ==
       blink::WebRTCRtpTransceiverImplementationType::kFullTransceiver) {
-    transceiver = std::make_unique<FakeRTCRtpTransceiver>(
+    transceiver = std::make_unique<blink::FakeRTCRtpTransceiverImpl>(
         base::nullopt, std::move(sender), std::move(receiver),
         false /* stopped */,
         webrtc::RtpTransceiverDirection::kSendOnly /* direction */,
         base::nullopt /* current_direction */);
   } else if (implementation_type ==
              blink::WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly) {
-    transceiver = std::make_unique<RTCRtpSenderOnlyTransceiver>(
-        std::make_unique<FakeRTCRtpSender>(sender));
+    transceiver = std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
+        std::make_unique<blink::FakeRTCRtpSenderImpl>(sender));
   } else {
     DCHECK_EQ(
         implementation_type,
         blink::WebRTCRtpTransceiverImplementationType::kPlanBReceiverOnly);
     transceiver = std::make_unique<blink::RTCRtpReceiverOnlyTransceiver>(
-        std::make_unique<FakeRTCRtpReceiver>(receiver));
+        std::make_unique<blink::FakeRTCRtpReceiverImpl>(receiver));
   }
   return transceiver;
 }
@@ -132,7 +132,7 @@
 
  private:
   blink::MockPeerConnectionDependencyFactory dependency_factory_;
-  MockWebRTCPeerConnectionHandlerClient client_;
+  blink::MockWebRTCPeerConnectionHandlerClient client_;
 };
 
 class PeerConnectionTrackerTest : public ::testing::Test {
@@ -194,11 +194,12 @@
 TEST_F(PeerConnectionTrackerTest, AddTransceiverWithOptionalValuesPresent) {
   CreateTrackerWithMocks();
   CreateAndRegisterPeerConnectionHandler();
-  FakeRTCRtpTransceiver transceiver(
+  blink::FakeRTCRtpTransceiverImpl transceiver(
       "midValue",
-      FakeRTCRtpSender("senderTrackId", {"streamIdA", "streamIdB"},
-                       blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
-      FakeRTCRtpReceiver(
+      blink::FakeRTCRtpSenderImpl(
+          "senderTrackId", {"streamIdA", "streamIdB"},
+          blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
+      blink::FakeRTCRtpReceiverImpl(
           "receiverTrackId", {"streamIdC"},
           blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
       true /* stopped */,
@@ -235,11 +236,12 @@
 TEST_F(PeerConnectionTrackerTest, AddTransceiverWithOptionalValuesNull) {
   CreateTrackerWithMocks();
   CreateAndRegisterPeerConnectionHandler();
-  FakeRTCRtpTransceiver transceiver(
+  blink::FakeRTCRtpTransceiverImpl transceiver(
       base::nullopt,
-      FakeRTCRtpSender(base::nullopt, {},
-                       blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
-      FakeRTCRtpReceiver(
+      blink::FakeRTCRtpSenderImpl(
+          base::nullopt, {},
+          blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
+      blink::FakeRTCRtpReceiverImpl(
           "receiverTrackId", {},
           blink::scheduler::GetSingleThreadTaskRunnerForTesting()),
       false /* stopped */,
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 853f3d7a..70f5927 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -568,7 +568,7 @@
 
 // Counts the number of senders that have |stream_id| as an associated stream.
 size_t GetLocalStreamUsageCount(
-    const std::vector<std::unique_ptr<RTCRtpSender>>& rtp_senders,
+    const std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>>& rtp_senders,
     const std::string stream_id) {
   size_t usage_count = 0;
   for (const auto& sender : rtp_senders) {
@@ -759,7 +759,7 @@
     }
   }
 
-  bool ReceiverWasAdded(const RtpTransceiverState& transceiver_state) {
+  bool ReceiverWasAdded(const blink::RtpTransceiverState& transceiver_state) {
     DCHECK(handler_);
     uintptr_t receiver_id = blink::RTCRtpReceiverImpl::getId(
         transceiver_state.receiver_state()->webrtc_receiver().get());
@@ -772,7 +772,7 @@
 
   bool ReceiverWasRemoved(
       const blink::RTCRtpReceiverImpl& receiver,
-      const std::vector<RtpTransceiverState>& transceiver_states) {
+      const std::vector<blink::RtpTransceiverState>& transceiver_states) {
     for (const auto& transceiver_state : transceiver_states) {
       if (transceiver_state.receiver_state()->webrtc_receiver() ==
           receiver.state().webrtc_receiver()) {
@@ -1129,8 +1129,8 @@
           peer_connection_tracker_,
           PeerConnectionTracker::ACTION_CREATE_OFFER));
 
-  TransceiverStateSurfacer transceiver_state_surfacer(task_runner_,
-                                                      signaling_thread());
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      task_runner_, signaling_thread());
   RunSynchronousRepeatingClosureOnSignalingThread(
       base::BindRepeating(
           &RTCPeerConnectionHandler::CreateOfferOnSignalingThread,
@@ -1143,7 +1143,7 @@
   std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> transceivers;
   for (auto& transceiver_state : transceiver_states) {
     auto transceiver = CreateOrUpdateTransceiver(
-        std::move(transceiver_state), TransceiverStateUpdateMode::kAll);
+        std::move(transceiver_state), blink::TransceiverStateUpdateMode::kAll);
     transceivers.push_back(std::move(transceiver));
   }
   return transceivers;
@@ -1152,7 +1152,7 @@
 void RTCPeerConnectionHandler::CreateOfferOnSignalingThread(
     webrtc::CreateSessionDescriptionObserver* observer,
     webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offer_options,
-    TransceiverStateSurfacer* transceiver_state_surfacer) {
+    blink::TransceiverStateSurfacer* transceiver_state_surfacer) {
   native_peer_connection_->CreateOffer(observer, offer_options);
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
       transceivers =
@@ -1605,8 +1605,8 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
       track_ref = track_adapter_map_->GetOrCreateLocalTrackAdapter(web_track);
-  TransceiverStateSurfacer transceiver_state_surfacer(task_runner_,
-                                                      signaling_thread());
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      task_runner_, signaling_thread());
   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
       error_or_transceiver;
   RunSynchronousRepeatingClosureOnSignalingThread(
@@ -1623,8 +1623,9 @@
   }
 
   auto transceiver_states = transceiver_state_surfacer.ObtainStates();
-  auto transceiver = CreateOrUpdateTransceiver(
-      std::move(transceiver_states[0]), TransceiverStateUpdateMode::kAll);
+  auto transceiver =
+      CreateOrUpdateTransceiver(std::move(transceiver_states[0]),
+                                blink::TransceiverStateUpdateMode::kAll);
   std::unique_ptr<blink::WebRTCRtpTransceiver> web_transceiver =
       std::move(transceiver);
   return web_transceiver;
@@ -1633,7 +1634,7 @@
 void RTCPeerConnectionHandler::AddTransceiverWithTrackOnSignalingThread(
     rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track,
     webrtc::RtpTransceiverInit init,
-    TransceiverStateSurfacer* transceiver_state_surfacer,
+    blink::TransceiverStateSurfacer* transceiver_state_surfacer,
     webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>*
         error_or_transceiver) {
   *error_or_transceiver =
@@ -1658,8 +1659,8 @@
     DCHECK_EQ(kind, webrtc::MediaStreamTrackInterface::kVideoKind);
     media_type = cricket::MEDIA_TYPE_VIDEO;
   }
-  TransceiverStateSurfacer transceiver_state_surfacer(task_runner_,
-                                                      signaling_thread());
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      task_runner_, signaling_thread());
   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
       error_or_transceiver;
   RunSynchronousRepeatingClosureOnSignalingThread(
@@ -1677,8 +1678,9 @@
   }
 
   auto transceiver_states = transceiver_state_surfacer.ObtainStates();
-  auto transceiver = CreateOrUpdateTransceiver(
-      std::move(transceiver_states[0]), TransceiverStateUpdateMode::kAll);
+  auto transceiver =
+      CreateOrUpdateTransceiver(std::move(transceiver_states[0]),
+                                blink::TransceiverStateUpdateMode::kAll);
   std::unique_ptr<blink::WebRTCRtpTransceiver> web_transceiver =
       std::move(transceiver);
   return std::move(web_transceiver);
@@ -1687,7 +1689,7 @@
 void RTCPeerConnectionHandler::AddTransceiverWithMediaTypeOnSignalingThread(
     cricket::MediaType media_type,
     webrtc::RtpTransceiverInit init,
-    TransceiverStateSurfacer* transceiver_state_surfacer,
+    blink::TransceiverStateSurfacer* transceiver_state_surfacer,
     webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>*
         error_or_transceiver) {
   *error_or_transceiver =
@@ -1716,8 +1718,8 @@
   // transceiver (Plan B: sender only).
   // TODO(hbos): Implement and surface full transceiver support under Unified
   // Plan. https://crbug.com/777617
-  TransceiverStateSurfacer transceiver_state_surfacer(task_runner_,
-                                                      signaling_thread());
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      task_runner_, signaling_thread());
   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>>
       error_or_sender;
   RunSynchronousRepeatingClosureOnSignalingThread(
@@ -1746,19 +1748,19 @@
     // Plan B: Create sender only.
     DCHECK(transceiver_state.sender_state());
     auto webrtc_sender = transceiver_state.sender_state()->webrtc_sender();
-    DCHECK(FindSender(RTCRtpSender::getId(webrtc_sender.get())) ==
+    DCHECK(FindSender(blink::RTCRtpSenderImpl::getId(webrtc_sender.get())) ==
            rtp_senders_.end());
-    RtpSenderState sender_state = transceiver_state.MoveSenderState();
+    blink::RtpSenderState sender_state = transceiver_state.MoveSenderState();
     DCHECK(sender_state.is_initialized());
-    rtp_senders_.push_back(std::make_unique<RTCRtpSender>(
+    rtp_senders_.push_back(std::make_unique<blink::RTCRtpSenderImpl>(
         native_peer_connection_, track_adapter_map_, std::move(sender_state)));
-    web_transceiver = std::make_unique<RTCRtpSenderOnlyTransceiver>(
-        std::make_unique<RTCRtpSender>(*rtp_senders_.back().get()));
+    web_transceiver = std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
+        std::make_unique<blink::RTCRtpSenderImpl>(*rtp_senders_.back().get()));
   } else {
     DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
     // Unified Plan: Create or recycle a transceiver.
     auto transceiver = CreateOrUpdateTransceiver(
-        std::move(transceiver_state), TransceiverStateUpdateMode::kAll);
+        std::move(transceiver_state), blink::TransceiverStateUpdateMode::kAll);
     web_transceiver = std::move(transceiver);
   }
   if (peer_connection_tracker_) {
@@ -1780,7 +1782,7 @@
 void RTCPeerConnectionHandler::AddTrackOnSignalingThread(
     rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
     std::vector<std::string> stream_ids,
-    TransceiverStateSurfacer* transceiver_state_surfacer,
+    blink::TransceiverStateSurfacer* transceiver_state_surfacer,
     webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>>*
         error_or_sender) {
   *error_or_sender = native_peer_connection_->AddTrack(track, stream_ids);
@@ -1788,7 +1790,7 @@
   if (error_or_sender->ok()) {
     auto sender = error_or_sender->value();
     if (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB) {
-      transceivers = {new SurfaceSenderStateOnly(sender)};
+      transceivers = {new blink::SurfaceSenderStateOnly(sender)};
     } else {
       DCHECK_EQ(configuration_.sdp_semantics,
                 webrtc::SdpSemantics::kUnifiedPlan);
@@ -1841,8 +1843,8 @@
                              web_track.Id().Utf8());
   if (peer_connection_tracker_) {
     auto sender_only_transceiver =
-        std::make_unique<RTCRtpSenderOnlyTransceiver>(
-            std::make_unique<RTCRtpSender>(*it->get()));
+        std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
+            std::make_unique<blink::RTCRtpSenderImpl>(*it->get()));
     size_t sender_index = GetTransceiverIndex(*sender_only_transceiver);
     peer_connection_tracker_->TrackRemoveTransceiver(
         this, PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack,
@@ -1871,8 +1873,8 @@
   const auto& sender = *it;
   auto webrtc_sender = sender->state().webrtc_sender();
 
-  TransceiverStateSurfacer transceiver_state_surfacer(task_runner_,
-                                                      signaling_thread());
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      task_runner_, signaling_thread());
   bool result;
   RunSynchronousRepeatingClosureOnSignalingThread(
       base::BindRepeating(
@@ -1896,7 +1898,7 @@
 
   // Update the transceiver.
   auto transceiver = CreateOrUpdateTransceiver(
-      std::move(transceiver_state), TransceiverStateUpdateMode::kAll);
+      std::move(transceiver_state), blink::TransceiverStateUpdateMode::kAll);
   if (peer_connection_tracker_) {
     size_t transceiver_index = GetTransceiverIndex(*transceiver);
     peer_connection_tracker_->TrackModifyTransceiver(
@@ -1910,7 +1912,7 @@
 
 void RTCPeerConnectionHandler::RemoveTrackUnifiedPlanOnSignalingThread(
     rtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
-    TransceiverStateSurfacer* transceiver_state_surfacer,
+    blink::TransceiverStateSurfacer* transceiver_state_surfacer,
     bool* result) {
   *result = native_peer_connection_->RemoveTrack(sender);
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
@@ -2231,7 +2233,7 @@
 }
 
 void RTCPeerConnectionHandler::OnModifyTransceivers(
-    std::vector<RtpTransceiverState> transceiver_states,
+    std::vector<blink::RtpTransceiverState> transceiver_states,
     bool is_remote_description) {
   DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> web_transceivers(
@@ -2245,7 +2247,7 @@
     // Figure out if this transceiver is new or if setting the state modified
     // the transceiver such that it should be logged by the
     // |peer_connection_tracker_|.
-    uintptr_t transceiver_id = RTCRtpTransceiver::GetId(
+    uintptr_t transceiver_id = blink::RTCRtpTransceiverImpl::GetId(
         transceiver_states[i].webrtc_transceiver().get());
     auto it = FindTransceiver(transceiver_id);
     bool transceiver_is_new = (it == rtp_transceivers_.end());
@@ -2261,9 +2263,9 @@
     }
 
     // Update the transceiver.
-    web_transceivers[i] =
-        CreateOrUpdateTransceiver(std::move(transceiver_states[i]),
-                                  TransceiverStateUpdateMode::kSetDescription);
+    web_transceivers[i] = CreateOrUpdateTransceiver(
+        std::move(transceiver_states[i]),
+        blink::TransceiverStateUpdateMode::kSetDescription);
 
     // Log a "transceiverAdded" or "transceiverModified" event in
     // chrome://webrtc-internals if new or modified.
@@ -2391,7 +2393,7 @@
   // video or not.
 }
 
-std::vector<std::unique_ptr<RTCRtpSender>>::iterator
+std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>>::iterator
 RTCPeerConnectionHandler::FindSender(uintptr_t id) {
   for (auto it = rtp_senders_.begin(); it != rtp_senders_.end(); ++it) {
     if ((*it)->Id() == id)
@@ -2409,7 +2411,7 @@
   return rtp_receivers_.end();
 }
 
-std::vector<std::unique_ptr<RTCRtpTransceiver>>::iterator
+std::vector<std::unique_ptr<blink::RTCRtpTransceiverImpl>>::iterator
 RTCPeerConnectionHandler::FindTransceiver(uintptr_t id) {
   for (auto it = rtp_transceivers_.begin(); it != rtp_transceivers_.end();
        ++it) {
@@ -2448,10 +2450,10 @@
   return 0u;
 }
 
-std::unique_ptr<RTCRtpTransceiver>
+std::unique_ptr<blink::RTCRtpTransceiverImpl>
 RTCPeerConnectionHandler::CreateOrUpdateTransceiver(
-    RtpTransceiverState transceiver_state,
-    TransceiverStateUpdateMode update_mode) {
+    blink::RtpTransceiverState transceiver_state,
+    blink::TransceiverStateUpdateMode update_mode) {
   DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   DCHECK(transceiver_state.is_initialized());
   DCHECK(transceiver_state.sender_state());
@@ -2460,18 +2462,19 @@
   auto webrtc_sender = transceiver_state.sender_state()->webrtc_sender();
   auto webrtc_receiver = transceiver_state.receiver_state()->webrtc_receiver();
 
-  std::unique_ptr<RTCRtpTransceiver> transceiver;
-  auto it = FindTransceiver(RTCRtpTransceiver::GetId(webrtc_transceiver.get()));
+  std::unique_ptr<blink::RTCRtpTransceiverImpl> transceiver;
+  auto it = FindTransceiver(
+      blink::RTCRtpTransceiverImpl::GetId(webrtc_transceiver.get()));
   if (it == rtp_transceivers_.end()) {
     // Create a new transceiver, including a sender and a receiver.
-    transceiver = std::make_unique<RTCRtpTransceiver>(
+    transceiver = std::make_unique<blink::RTCRtpTransceiverImpl>(
         native_peer_connection_, track_adapter_map_,
         std::move(transceiver_state));
     rtp_transceivers_.push_back(transceiver->ShallowCopy());
-    DCHECK(FindSender(RTCRtpSender::getId(webrtc_sender.get())) ==
+    DCHECK(FindSender(blink::RTCRtpSenderImpl::getId(webrtc_sender.get())) ==
            rtp_senders_.end());
-    rtp_senders_.push_back(
-        std::make_unique<RTCRtpSender>(*transceiver->content_sender()));
+    rtp_senders_.push_back(std::make_unique<blink::RTCRtpSenderImpl>(
+        *transceiver->content_sender()));
     DCHECK(FindReceiver(blink::RTCRtpReceiverImpl::getId(
                webrtc_receiver.get())) == rtp_receivers_.end());
     rtp_receivers_.push_back(std::make_unique<blink::RTCRtpReceiverImpl>(
@@ -2480,7 +2483,7 @@
     // Update the transceiver. This also updates the sender and receiver.
     transceiver = (*it)->ShallowCopy();
     transceiver->set_state(std::move(transceiver_state), update_mode);
-    DCHECK(FindSender(RTCRtpSender::getId(webrtc_sender.get())) !=
+    DCHECK(FindSender(blink::RTCRtpSenderImpl::getId(webrtc_sender.get())) !=
            rtp_senders_.end());
     DCHECK(FindReceiver(blink::RTCRtpReceiverImpl::getId(
                webrtc_receiver.get())) != rtp_receivers_.end());
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index f32ce7b..3676102 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -20,8 +20,6 @@
 #include "base/threading/thread.h"
 #include "content/common/content_export.h"
 #include "content/renderer/media/webrtc/media_stream_track_metrics.h"
-#include "content/renderer/media/webrtc/rtc_rtp_sender.h"
-#include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
@@ -29,6 +27,8 @@
 #include "third_party/blink/public/platform/web_rtc_stats_request.h"
 #include "third_party/blink/public/platform/web_rtc_stats_response.h"
 #include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_receiver_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/webrtc/api/stats/rtc_stats.h"
 #include "third_party/webrtc/api/stats/rtc_stats_collector_callback.h"
@@ -235,8 +235,9 @@
   void OnAddReceiverPlanB(blink::RtpReceiverState receiver_state);
   void OnRemoveReceiverPlanB(uintptr_t receiver_id);
   void OnModifySctpTransport(blink::WebRTCSctpTransportSnapshot state);
-  void OnModifyTransceivers(std::vector<RtpTransceiverState> transceiver_states,
-                            bool is_remote_description);
+  void OnModifyTransceivers(
+      std::vector<blink::RtpTransceiverState> transceiver_states,
+      bool is_remote_description);
   void OnDataChannel(scoped_refptr<webrtc::DataChannelInterface> channel);
   void OnIceCandidate(const std::string& sdp,
                       const std::string& sdp_mid,
@@ -288,19 +289,19 @@
   void AddTransceiverWithTrackOnSignalingThread(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track,
       webrtc::RtpTransceiverInit init,
-      TransceiverStateSurfacer* transceiver_state_surfacer,
+      blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>*
           error_or_transceiver);
   void AddTransceiverWithMediaTypeOnSignalingThread(
       cricket::MediaType media_type,
       webrtc::RtpTransceiverInit init,
-      TransceiverStateSurfacer* transceiver_state_surfacer,
+      blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>*
           error_or_transceiver);
   void AddTrackOnSignalingThread(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
       std::vector<std::string> stream_ids,
-      TransceiverStateSurfacer* transceiver_state_surfacer,
+      blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>>*
           error_or_sender);
   bool RemoveTrackPlanB(blink::WebRTCRtpSender* web_sender);
@@ -308,7 +309,7 @@
   RemoveTrackUnifiedPlan(blink::WebRTCRtpSender* web_sender);
   void RemoveTrackUnifiedPlanOnSignalingThread(
       rtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
-      TransceiverStateSurfacer* transceiver_state_surfacer,
+      blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       bool* result);
   std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> CreateOfferInternal(
       const blink::WebRTCSessionDescriptionRequest& request,
@@ -316,12 +317,13 @@
   void CreateOfferOnSignalingThread(
       webrtc::CreateSessionDescriptionObserver* observer,
       webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offer_options,
-      TransceiverStateSurfacer* transceiver_state_surfacer);
-  std::vector<std::unique_ptr<RTCRtpSender>>::iterator FindSender(uintptr_t id);
+      blink::TransceiverStateSurfacer* transceiver_state_surfacer);
+  std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>>::iterator FindSender(
+      uintptr_t id);
   std::vector<std::unique_ptr<blink::RTCRtpReceiverImpl>>::iterator
   FindReceiver(uintptr_t id);
-  std::vector<std::unique_ptr<RTCRtpTransceiver>>::iterator FindTransceiver(
-      uintptr_t id);
+  std::vector<std::unique_ptr<blink::RTCRtpTransceiverImpl>>::iterator
+  FindTransceiver(uintptr_t id);
   // For full transceiver implementations, returns the index of
   // |rtp_transceivers_| that correspond to |web_transceiver|.
   // For sender-only transceiver implementations, returns the index of
@@ -331,9 +333,9 @@
   // NOTREACHED()-crashes if no correspondent is found.
   size_t GetTransceiverIndex(
       const blink::WebRTCRtpTransceiver& web_transceiver);
-  std::unique_ptr<RTCRtpTransceiver> CreateOrUpdateTransceiver(
-      RtpTransceiverState transceiver_state,
-      TransceiverStateUpdateMode update_mode);
+  std::unique_ptr<blink::RTCRtpTransceiverImpl> CreateOrUpdateTransceiver(
+      blink::RtpTransceiverState transceiver_state,
+      blink::TransceiverStateUpdateMode update_mode);
 
   scoped_refptr<base::SingleThreadTaskRunner> signaling_thread() const;
 
@@ -369,11 +371,11 @@
   // transceivers. Transceivers may become inactive, but are never removed.
   // TODO(hbos): Implement transceiver behaviors. https://crbug.com/777617
   // Content layer correspondents of |webrtc::RtpSenderInterface|.
-  std::vector<std::unique_ptr<RTCRtpSender>> rtp_senders_;
+  std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>> rtp_senders_;
   // Content layer correspondents of |webrtc::RtpReceiverInterface|.
   std::vector<std::unique_ptr<blink::RTCRtpReceiverImpl>> rtp_receivers_;
   // Content layer correspondents of |webrtc::RtpTransceiverInterface|.
-  std::vector<std::unique_ptr<RTCRtpTransceiver>> rtp_transceivers_;
+  std::vector<std::unique_ptr<blink::RTCRtpTransceiverImpl>> rtp_transceivers_;
 
   base::WeakPtr<PeerConnectionTracker> peer_connection_tracker_;
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index 9ec7db587..7ac4048 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -25,7 +25,6 @@
 #include "base/values.h"
 #include "content/child/child_process.h"
 #include "content/renderer/media/audio/mock_audio_device_factory.h"
-#include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/peer_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -55,6 +54,7 @@
 #include "third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h"
 #include "third_party/blink/public/web/modules/webrtc/webrtc_audio_device_impl.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
@@ -289,7 +289,8 @@
   RTCPeerConnectionHandlerTest() : mock_peer_connection_(nullptr) {}
 
   void SetUp() override {
-    mock_client_.reset(new NiceMock<MockWebRTCPeerConnectionHandlerClient>());
+    mock_client_.reset(
+        new NiceMock<blink::MockWebRTCPeerConnectionHandlerClient>());
     mock_dependency_factory_.reset(
         new blink::MockPeerConnectionDependencyFactory());
 
@@ -420,8 +421,8 @@
             error_or_transceiver.value()->ImplementationType(),
             blink::WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly);
         auto sender = error_or_transceiver.value()->Sender();
-        senders_.push_back(std::unique_ptr<RTCRtpSender>(
-            static_cast<RTCRtpSender*>(sender.release())));
+        senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>(
+            static_cast<blink::RTCRtpSenderImpl*>(sender.release())));
       }
     }
     for (const auto& web_video_track : web_stream.VideoTracks()) {
@@ -432,15 +433,15 @@
             error_or_transceiver.value()->ImplementationType(),
             blink::WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly);
         auto sender = error_or_transceiver.value()->Sender();
-        senders_.push_back(std::unique_ptr<RTCRtpSender>(
-            static_cast<RTCRtpSender*>(sender.release())));
+        senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>(
+            static_cast<blink::RTCRtpSenderImpl*>(sender.release())));
       }
     }
     return senders_size_before_add < senders_.size();
   }
 
-  std::vector<std::unique_ptr<RTCRtpSender>>::iterator FindSenderForTrack(
-      const blink::WebMediaStreamTrack& web_track) {
+  std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>>::iterator
+  FindSenderForTrack(const blink::WebMediaStreamTrack& web_track) {
     for (auto it = senders_.begin(); it != senders_.end(); ++it) {
       if ((*it)->Track().UniqueId() == web_track.UniqueId())
         return it;
@@ -588,7 +589,7 @@
   std::unique_ptr<AudioCapturerSourceTestingPlatformSupport>
       webrtc_audio_device_platform_support_;
   blink::Platform* platform_original_ = nullptr;
-  std::unique_ptr<MockWebRTCPeerConnectionHandlerClient> mock_client_;
+  std::unique_ptr<blink::MockWebRTCPeerConnectionHandlerClient> mock_client_;
   std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
       mock_dependency_factory_;
   std::unique_ptr<NiceMock<MockPeerConnectionTracker>> mock_tracker_;
@@ -598,7 +599,7 @@
   // Weak reference to the mocked native peer connection implementation.
   blink::MockPeerConnectionImpl* mock_peer_connection_;
 
-  std::vector<std::unique_ptr<RTCRtpSender>> senders_;
+  std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>> senders_;
   std::map<webrtc::MediaStreamTrackInterface*,
            rtc::scoped_refptr<webrtc::RtpReceiverInterface>>
       receivers_by_track_;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.cc b/content/renderer/media/webrtc/webrtc_set_description_observer.cc
index ad7962f8..7054b20c 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer.cc
@@ -60,19 +60,19 @@
   // Only surface transceiver/receiver states if the peer connection is not
   // closed. If the peer connection is closed, the peer connection handler may
   // have been destroyed along with any track adapters that
-  // TransceiverStateSurfacer assumes exist. This is treated as a special case
-  // due to https://crbug.com/897251.
+  // blink::TransceiverStateSurfacer assumes exist. This is treated as a special
+  // case due to https://crbug.com/897251.
   if (pc_->signaling_state() != webrtc::PeerConnectionInterface::kClosed) {
     if (surface_receivers_only_) {
       for (const auto& receiver : pc_->GetReceivers()) {
-        transceivers.push_back(new SurfaceReceiverStateOnly(receiver));
+        transceivers.push_back(new blink::SurfaceReceiverStateOnly(receiver));
       }
     } else {
       transceivers = pc_->GetTransceivers();
     }
   }
-  TransceiverStateSurfacer transceiver_state_surfacer(main_task_runner_,
-                                                      signaling_task_runner_);
+  blink::TransceiverStateSurfacer transceiver_state_surfacer(
+      main_task_runner_, signaling_task_runner_);
   transceiver_state_surfacer.Initialize(pc_, track_adapter_map_,
                                         std::move(transceivers));
   main_task_runner_->PostTask(
@@ -86,7 +86,7 @@
     OnSetDescriptionCompleteOnMainThread(
         webrtc::RTCError error,
         webrtc::PeerConnectionInterface::SignalingState signaling_state,
-        TransceiverStateSurfacer transceiver_state_surfacer) {
+        blink::TransceiverStateSurfacer transceiver_state_surfacer) {
   CHECK(main_task_runner_->BelongsToCurrentThread());
   WebRtcSetDescriptionObserver::States states;
   states.signaling_state = signaling_state;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.h b/content/renderer/media/webrtc/webrtc_set_description_observer.h
index 050c14d7..d927ac1d 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer.h
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer.h
@@ -14,10 +14,10 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/common/content_export.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
-#include "content/renderer/media/webrtc/rtc_rtp_sender.h"
-#include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
-#include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
 #include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_receiver_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/webrtc/api/jsep.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
@@ -50,7 +50,7 @@
 
     webrtc::PeerConnectionInterface::SignalingState signaling_state;
     blink::WebRTCSctpTransportSnapshot sctp_transport_state;
-    std::vector<RtpTransceiverState> transceiver_states;
+    std::vector<blink::RtpTransceiverState> transceiver_states;
 
     DISALLOW_COPY_AND_ASSIGN(States);
   };
@@ -104,7 +104,7 @@
   void OnSetDescriptionCompleteOnMainThread(
       webrtc::RTCError error,
       webrtc::PeerConnectionInterface::SignalingState signaling_state,
-      TransceiverStateSurfacer transceiver_state_surfacer);
+      blink::TransceiverStateSurfacer transceiver_state_surfacer);
 
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index 1e8b137..3f5695a 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -297,7 +297,7 @@
     auto sender = transceiver->sender();
     auto receiver = transceiver->receiver();
     EXPECT_EQ(1u, observer_->states().transceiver_states.size());
-    const RtpTransceiverState& transceiver_state =
+    const blink::RtpTransceiverState& transceiver_state =
         observer_->states().transceiver_states[0];
     // Inspect transceiver states.
     EXPECT_TRUE(transceiver_state.is_initialized());
@@ -312,7 +312,8 @@
                                       transceiver->fired_direction()));
     // Inspect sender states.
     EXPECT_TRUE(transceiver_state.sender_state());
-    const RtpSenderState& sender_state = *transceiver_state.sender_state();
+    const blink::RtpSenderState& sender_state =
+        *transceiver_state.sender_state();
     EXPECT_TRUE(sender_state.is_initialized());
     EXPECT_EQ(sender.get(), sender_state.webrtc_sender());
     EXPECT_EQ(sender->track(), sender_state.track_ref()->webrtc_track());
@@ -349,7 +350,7 @@
 
     auto receiver = receivers_[0];
     EXPECT_EQ(1u, observer_->states().transceiver_states.size());
-    const RtpTransceiverState& transceiver_state =
+    const blink::RtpTransceiverState& transceiver_state =
         observer_->states().transceiver_states[0];
     EXPECT_FALSE(transceiver_state.sender_state());
     EXPECT_TRUE(transceiver_state.receiver_state());
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e6e271c..beb7c4a 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -233,8 +233,6 @@
     "../public/test/web_contents_binding_set_test_binder.h",
     "../public/test/web_contents_tester.cc",
     "../public/test/web_contents_tester.h",
-    "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc",
-    "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h",
     "appcache_test_helper.cc",
     "appcache_test_helper.h",
     "barrier_builder.cc",
@@ -1938,16 +1936,11 @@
     "../renderer/media/batching_media_log_unittest.cc",
     "../renderer/media/inspector_media_event_handler_unittest.cc",
     "../renderer/media/renderer_webaudiodevice_impl_unittest.cc",
-    "../renderer/media/webrtc/fake_rtc_rtp_transceiver.cc",
-    "../renderer/media/webrtc/fake_rtc_rtp_transceiver.h",
     "../renderer/media/webrtc/media_stream_track_metrics_unittest.cc",
     "../renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc",
     "../renderer/media/webrtc/peer_connection_tracker_unittest.cc",
     "../renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc",
-    "../renderer/media/webrtc/rtc_rtp_sender_unittest.cc",
-    "../renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc",
     "../renderer/media/webrtc/task_queue_factory_unittest.cc",
-    "../renderer/media/webrtc/transceiver_state_surfacer_unittest.cc",
     "../renderer/media/webrtc/webrtc_audio_renderer_unittest.cc",
     "../renderer/media/webrtc/webrtc_set_description_observer_unittest.cc",
     "../renderer/peripheral_content_heuristic_unittest.cc",
diff --git a/content/test/data/frame_tree/page_with_same_origin_nested_frames.html b/content/test/data/frame_tree/page_with_same_origin_nested_frames.html
new file mode 100644
index 0000000..b6ec041e2
--- /dev/null
+++ b/content/test/data/frame_tree/page_with_same_origin_nested_frames.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+iframe {
+  top: 50px;
+  left: 50px;
+  width: 200px;
+  height: 200px;
+}
+</style>
+<html>
+<body>
+<div id="wrapper">
+  <iframe src="page_with_positioned_frame.html"></iframe>
+</div>
+</body>
+</html>
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index df1e0162..90291215 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -3954,12 +3954,6 @@
 
     # Keep builders sorted by OS, then name.
     builders {
-      mixins: "android-try"
-      mixins: "builderless"
-      mixins: "linux-xenial"
-      name: "android-opus-kitkat-arm-rel"
-    }
-    builders {
       mixins: "android-angle-try"
       mixins: "goma-rbe-prod"
       name: "android_angle_deqp_rel_ng"
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index df1e0162..90291215 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -3954,12 +3954,6 @@
 
     # Keep builders sorted by OS, then name.
     builders {
-      mixins: "android-try"
-      mixins: "builderless"
-      mixins: "linux-xenial"
-      name: "android-opus-kitkat-arm-rel"
-    }
-    builders {
       mixins: "android-angle-try"
       mixins: "goma-rbe-prod"
       name: "android_angle_deqp_rel_ng"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index ee9f053..5f23b67 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -4133,9 +4133,6 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbucket/luci.chromium.try/android-opus-kitkat-arm-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
   }
   builders {
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index ee9f053..5f23b67 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -4133,9 +4133,6 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbucket/luci.chromium.try/android-opus-kitkat-arm-rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
   }
   builders {
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 0f0e991d..39b252b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1346,7 +1346,16 @@
         Save Passwords
       </message>
       <message name="IDS_IOS_LEAK_CHECK_SWITCH" desc="Title for the switch toggling whether Chrome should check that entered credentials have been part of a leak.">
-       Check password safety
+        Check password safety
+      </message>
+      <message name="IDS_IOS_LEAK_CHECK_SIGNED_IN_DESC" desc="Text that describes the 'Password Leak Detection' functionality to signed-in users.">
+        Warns you if a password you use was part of a data breach.
+      </message>
+      <message name="IDS_IOS_LEAK_CHECK_SIGNED_OUT_DISABLED_DESC" desc="Text that describes the 'Password Leak Detection' functionality to signed-out users who have disabled the feature.">
+        Google can check if your passwords were part of a data breach.
+      </message>
+      <message name="IDS_IOS_LEAK_CHECK_SIGNED_OUT_ENABLED_DESC" desc="Text that describes the 'Password Leak Detection' functionality to signed-out users who have not disabled the feature.">
+        Google can check if your passwords were part of a data breach. This will be turned on when you sign in with your Google Account.
       </message>
       <message name="IDS_IOS_PASSWORDS" desc="Title for the view in Settings for managing saved passwords. [Length: 15em] [iOS only]">
         Passwords
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index da234fa2..3c86e641 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -482,7 +482,8 @@
 }
 
 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
-  if (action == @selector(copy:)) {
+  // Allow copying if the steady location bar is visible.
+  if (!self.locationBarSteadyView.hidden && action == @selector(copy:)) {
     return YES;
   }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 9bb4deb..826346e 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -7,6 +7,7 @@
 
 #include "base/bind.h"
 #include "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -24,6 +25,8 @@
 #error "This file requires ARC support."
 #endif
 
+using base::test::ios::kWaitForUIElementTimeout;
+
 namespace {
 
 // Web page 1.
@@ -60,6 +63,49 @@
   return nil;
 }
 
+// Returns visit Copied Link button matcher from UIMenuController.
+id<GREYMatcher> VisitCopiedLinkButton() {
+  NSString* a11yLabelCopiedLink =
+      l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK);
+  return grey_allOf(grey_accessibilityLabel(a11yLabelCopiedLink),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns Paste button matcher from UIMenuController.
+id<GREYMatcher> PasteButton() {
+  NSString* a11yLabelPaste = @"Paste";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelPaste),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns Select button from UIMenuController.
+id<GREYMatcher> SelectButton() {
+  NSString* a11yLabelSelect = @"Select";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelSelect),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns Select All button from UIMenuController.
+id<GREYMatcher> SelectAllButton() {
+  NSString* a11yLabelSelectAll = @"Select All";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelSelectAll),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns Cut button from UIMenuController.
+id<GREYMatcher> CutButton() {
+  NSString* a11yLabelCut = @"Cut";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelCut),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
+// Returns Search Copied Text button from UIMenuController.
+id<GREYMatcher> SearchCopiedTextButton() {
+  NSString* a11yLabelsearchCopiedText = @"Search for Copied Text";
+  return grey_allOf(grey_accessibilityLabel(a11yLabelsearchCopiedText),
+                    chrome_test_util::SystemSelectionCallout(), nil);
+}
+
 }  //  namespace
 
 #pragma mark - Steady state tests
@@ -120,6 +166,15 @@
                                           SystemSelectionCalloutCopyButton()]
       assertWithMatcher:grey_notNil()];
 
+  // Pressing should not allow pasting when pasteboard is empty.
+  // Verify that system text selection callout is not displayed.
+  [[EarlGrey selectElementWithMatcher:VisitCopiedLinkButton()]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:SearchCopiedTextButton()]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:PasteButton()]
+      assertWithMatcher:grey_nil()];
+
   [self checkLocationBarSteadyState];
 
   // Tapping it should copy the URL.
@@ -136,27 +191,22 @@
                         hasSuffix:base::SysUTF8ToNSString(kPage1URL)];
                   }];
   // Wait for copy to happen or timeout after 5 seconds.
-  BOOL success = [copyCondition waitWithTimeout:5];
-  GREYAssertTrue(success, @"Copying page 1 URL failed");
+  GREYAssertTrue([copyCondition waitWithTimeout:5],
+                 @"Copying page 1 URL failed");
 
   // Go to another web page.
   [self openPage2];
 
   // Visit copied link should now be available.
-  NSString* a11yLabelPasteGo =
-      l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK);
-  id<GREYMatcher> pasteAndGoMatcher =
-      grey_allOf(grey_accessibilityLabel(a11yLabelPasteGo),
-                 chrome_test_util::SystemSelectionCallout(), nil);
   [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
       performAction:grey_longPress()];
-  [[EarlGrey selectElementWithMatcher:pasteAndGoMatcher]
+  [[EarlGrey selectElementWithMatcher:VisitCopiedLinkButton()]
       assertWithMatcher:grey_notNil()];
 
   [self checkLocationBarSteadyState];
 
   // Tapping it should navigate to Page 1.
-  [[EarlGrey selectElementWithMatcher:pasteAndGoMatcher]
+  [[EarlGrey selectElementWithMatcher:VisitCopiedLinkButton()]
       performAction:grey_tap()];
 
   [ChromeEarlGrey waitForPageToFinishLoading];
@@ -202,8 +252,8 @@
                         hasSuffix:base::SysUTF8ToNSString(kPage1URL)];
                   }];
   // Wait for copy to happen or timeout after 5 seconds.
-  BOOL success = [copyCondition waitWithTimeout:5];
-  GREYAssertTrue(success, @"Copying page 1 URL failed");
+  GREYAssertTrue([copyCondition waitWithTimeout:5],
+                 @"Copying page 1 URL failed");
 
   // Defocus the omnibox.
   if ([ChromeEarlGrey isIPadIdiom]) {
@@ -268,3 +318,182 @@
 }
 
 @end
+
+#pragma mark - Edit state tests
+
+@interface LocationBarEditStateTestCase : ChromeTestCase
+@end
+
+@implementation LocationBarEditStateTestCase
+
+- (void)setUp {
+  [super setUp];
+
+  [ChromeEarlGrey clearBrowsingHistory];
+
+  // Clear the pasteboard in case there is a URL copied.
+  UIPasteboard* pasteboard = UIPasteboard.generalPasteboard;
+  [pasteboard setValue:@"" forPasteboardType:UIPasteboardNameGeneral];
+}
+
+// Copy button should be hidden when the omnibox is empty otherwise it should be
+// displayed. Paste button should be hidden when pasteboard is empty otherwise
+// it should be displayed. Select & SelectAll buttons should be hidden when the
+// omnibox is empty.
+- (void)testEmptyOmnibox {
+  // Focus omnibox.
+  [self focusFakebox];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_tap()];
+
+  // Pressing should not allow copying when omnibox is empty.
+  // Wait for Copy button to appear or timeout after 2 seconds.
+  GREYCondition* CopyButtonIsDisplayed = [GREYCondition
+      conditionWithName:@"Copy button display condition"
+                  block:^BOOL {
+                    NSError* error = nil;
+                    [[EarlGrey selectElementWithMatcher:
+                                   chrome_test_util::
+                                       SystemSelectionCalloutCopyButton()]
+                        assertWithMatcher:grey_notNil()
+                                    error:&error];
+                    return error == nil;
+                  }];
+  // Verify that system text selection callout is not displayed.
+  GREYAssertFalse(
+      [CopyButtonIsDisplayed waitWithTimeout:kWaitForUIElementTimeout],
+      @"Copy button should not be displayed");
+
+  // Pressing should not allow select or selectAll when omnibox is empty.
+  // Verify that system text selection callout is not displayed.
+  [[EarlGrey selectElementWithMatcher:SelectButton()]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:SelectAllButton()]
+      assertWithMatcher:grey_nil()];
+
+  // Pressing should not allow pasting when pasteboard is empty.
+  // Verify that system text selection callout is not displayed.
+  [[EarlGrey selectElementWithMatcher:VisitCopiedLinkButton()]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:SearchCopiedTextButton()]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:PasteButton()]
+      assertWithMatcher:grey_nil()];
+
+  // Writing in the omnibox field.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_typeText(@"this is a test")];
+
+  // Click on the omnibox.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_tap()];
+
+  // SelectAll the omnibox field.
+  GREYCondition* SelectAllButtonIsDisplayed = [GREYCondition
+      conditionWithName:@"SelectAll button display condition"
+                  block:^BOOL {
+                    NSError* error = nil;
+                    [[EarlGrey selectElementWithMatcher:SelectAllButton()]
+                        performAction:grey_tap()
+                                error:&error];
+                    return error == nil;
+                  }];
+  GREYAssertTrue(
+      [SelectAllButtonIsDisplayed waitWithTimeout:kWaitForUIElementTimeout],
+      @"SelectAll button display failed");
+
+  // Cut the text.
+  [[EarlGrey selectElementWithMatcher:CutButton()] performAction:grey_tap()];
+
+  // Pressing should allow pasting.
+  // Click on the omnibox.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_tap()];
+  // Verify that system text selection callout is displayed (Search Copied
+  // Text).
+  GREYCondition* searchCopiedTextButtonIsDisplayed = [GREYCondition
+      conditionWithName:@"Search Copied Text button display condition"
+                  block:^BOOL {
+                    NSError* error = nil;
+                    [[EarlGrey
+                        selectElementWithMatcher:SearchCopiedTextButton()]
+                        assertWithMatcher:grey_notNil()
+                                    error:&error];
+                    return error == nil;
+                  }];
+  GREYAssertTrue([searchCopiedTextButtonIsDisplayed
+                     waitWithTimeout:kWaitForUIElementTimeout],
+                 @"Search Copied Text button display failed");
+  // Verify that system text selection callout is displayed (Paste).
+  [[EarlGrey selectElementWithMatcher:PasteButton()]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Select & SelectAll buttons should be displayed when the omnibox is not empty
+// and no text is selected. If the selected text is a sub part of the omnibox
+// fied, Select button should be hidden & SelectAll button should be displayed.
+// If the selected text is the entire omnibox field, select & SelectAll button
+// should be hidden.
+- (void)testSelection {
+  // Focus omnibox.
+  [self focusFakebox];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  // Write in the omnibox field.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_typeText(@"this is a test")];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_tap()];
+
+  // Pressing should allow select and selectAll.
+  // Wait for UIMenuController to appear or timeout after 2 seconds.
+  GREYCondition* SelectButtonIsDisplayed = [GREYCondition
+      conditionWithName:@"Select button display condition"
+                  block:^BOOL {
+                    NSError* error = nil;
+                    [[EarlGrey selectElementWithMatcher:SelectButton()]
+                        assertWithMatcher:grey_notNil()
+                                    error:&error];
+                    return error == nil;
+                  }];
+  // Verify that system text selection callout is displayed.
+  GREYAssertTrue(
+      [SelectButtonIsDisplayed waitWithTimeout:kWaitForUIElementTimeout],
+      @"Select button display failed");
+  [[EarlGrey selectElementWithMatcher:SelectAllButton()]
+      assertWithMatcher:grey_notNil()];
+
+  // Pressing select should allow copy.
+  // select should be hidden.
+  [[EarlGrey selectElementWithMatcher:SelectButton()] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::
+                                          SystemSelectionCalloutCopyButton()]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:SelectButton()]
+      assertWithMatcher:grey_nil()];
+
+  // Pressing selectAll should allow copy.
+  // selectAll should be hidden.
+  [[EarlGrey selectElementWithMatcher:SelectAllButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::
+                                          SystemSelectionCalloutCopyButton()]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:SelectAllButton()]
+      assertWithMatcher:grey_nil()];
+}
+
+#pragma mark - Helpers
+
+// Taps the fake omnibox and waits for the real omnibox to be visible.
+- (void)focusFakebox {
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
+      performAction:grey_tap()];
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:chrome_test_util::Omnibox()];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
index 3f1def6..34cd571 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -616,15 +616,22 @@
 }
 
 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
-  // If there is selected text, show copy and cut.
+  // If the text is not empty and there is selected text, show copy and cut.
   if ([self textInRange:self.selectedTextRange].length > 0 &&
       (action == @selector(cut:) || action == @selector(copy:))) {
     return YES;
   }
 
-  // If there is no selected text, show select and selectAll.
-  if ([self textInRange:self.selectedTextRange].length == 0 &&
-      (action == @selector(select:) || action == @selector(selectAll:))) {
+  // If the text is not empty and there is no selected text, show select
+  if (self.text.length > 0 &&
+      [self textInRange:self.selectedTextRange].length == 0 &&
+      action == @selector(select:)) {
+    return YES;
+  }
+
+  // If selected text is les than the text length, show selectAll.
+  if ([self textInRange:self.selectedTextRange].length != self.text.length &&
+      action == @selector(selectAll:)) {
     return YES;
   }
 
diff --git a/ios/chrome/browser/ui/passwords/BUILD.gn b/ios/chrome/browser/ui/passwords/BUILD.gn
index e4b60331..ba90c73 100644
--- a/ios/chrome/browser/ui/passwords/BUILD.gn
+++ b/ios/chrome/browser/ui/passwords/BUILD.gn
@@ -21,6 +21,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/colors",
     "//ios/chrome/common/ui_util",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
index 6c925478..54fea4a 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
@@ -63,6 +63,10 @@
 - (void)showPasswordBreachForLeakType:(CredentialLeakType)leakType
                                   URL:(const GURL&)URL {
   self.viewController = [[PasswordBreachViewController alloc] init];
+  self.viewController.modalPresentationStyle = UIModalPresentationFormSheet;
+  if (@available(iOS 13, *)) {
+    self.viewController.modalInPresentation = YES;
+  }
   id<ApplicationCommands> dispatcher =
       static_cast<id<ApplicationCommands>>(self.dispatcher);
   self.mediator =
diff --git a/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
index 01ab74a..1a49da6e 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/passwords/password_breach_view_controller.h"
 
 #import "ios/chrome/browser/ui/passwords/password_breach_action_handler.h"
+#include "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/common/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -17,10 +18,15 @@
 using l10n_util::GetNSString;
 
 namespace {
-constexpr CGFloat kStackViewSpacing = 16.0;
+constexpr CGFloat kButtonVerticalInsets = 17;
+constexpr CGFloat kPrimaryButtonCornerRadius = 13;
+constexpr CGFloat kStackViewSpacing = 8;
+constexpr CGFloat kStackViewSpacingAfterIllustration = 27;
+// The multiplier used when in regular horizontal size class.
+constexpr CGFloat kSafeAreaMultiplier = 0.8;
 }  // namespace
 
-@interface PasswordBreachViewController ()
+@interface PasswordBreachViewController () <UIToolbarDelegate>
 
 // Properties backing up the respective consumer setter.
 @property(nonatomic, strong) NSString* titleString;
@@ -28,6 +34,19 @@
 @property(nonatomic, strong) NSString* primaryActionString;
 @property(nonatomic) BOOL primaryActionAvailable;
 
+// References to the UI properties that need to be updated when the trait
+// collection changes.
+@property(nonatomic, strong) UIButton* primaryActionButton;
+@property(nonatomic, strong) UIImageView* imageView;
+// Constraints.
+@property(nonatomic, strong)
+    NSArray<NSLayoutConstraint*>* compactWidthConstraints;
+@property(nonatomic, strong)
+    NSArray<NSLayoutConstraint*>* regularWidthConstraints;
+@property(nonatomic, strong)
+    NSLayoutConstraint* scrollViewBottomVerticalConstraint;
+@property(nonatomic, strong)
+    NSLayoutConstraint* primaryButtonBottomVerticalConstraint;
 @end
 
 @implementation PasswordBreachViewController
@@ -38,72 +57,166 @@
   [super viewDidLoad];
   self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
 
-  UIButton* doneButton = [UIButton buttonWithType:UIButtonTypeSystem];
-  [doneButton addTarget:self
-                 action:@selector(didTapDoneButton)
-       forControlEvents:UIControlEventTouchUpInside];
-  [doneButton setTitle:GetNSString(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)
-              forState:UIControlStateNormal];
-  doneButton.translatesAutoresizingMaskIntoConstraints = NO;
-  [self.view addSubview:doneButton];
+  UIToolbar* topToolbar = [self createTopToolbar];
+  [self.view addSubview:topToolbar];
 
-  UIImage* image = [UIImage imageNamed:@"password_breach_illustration"];
-  UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
-  imageView.contentMode = UIViewContentModeScaleAspectFit;
-  imageView.translatesAutoresizingMaskIntoConstraints = NO;
+  self.imageView = [self createImageView];
+  UILabel* title = [self createTitleLabel];
+  UILabel* subtitle = [self createSubtitleLabel];
 
-  UILabel* title = [[UILabel alloc] init];
-  title.numberOfLines = 0;
-  title.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
-  title.textColor = [UIColor colorNamed:kTextPrimaryColor];
-  title.text = self.titleString;
-  title.translatesAutoresizingMaskIntoConstraints = NO;
+  UIStackView* stackView = [self
+      createStackViewWithArrangedSubviews:@[ self.imageView, title, subtitle ]];
 
-  UILabel* subtitle = [[UILabel alloc] init];
-  subtitle.numberOfLines = 0;
-  subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
-  subtitle.text = self.subtitleString;
-  subtitle.translatesAutoresizingMaskIntoConstraints = NO;
+  UIScrollView* scrollView = [self createScrollView];
+  [scrollView addSubview:stackView];
+  [self.view addSubview:scrollView];
 
-  UIStackView* stackView = [[UIStackView alloc]
-      initWithArrangedSubviews:@[ imageView, title, subtitle ]];
-  stackView.axis = UILayoutConstraintAxisVertical;
-  stackView.alignment = UIStackViewAlignmentFill;
-  stackView.translatesAutoresizingMaskIntoConstraints = NO;
-  stackView.spacing = kStackViewSpacing;
-  [self.view addSubview:stackView];
-
+  self.view.preservesSuperviewLayoutMargins = YES;
   UILayoutGuide* margins = self.view.layoutMarginsGuide;
 
+  // Toolbar constraints to the top.
+  AddSameConstraintsToSides(
+      topToolbar, self.view.safeAreaLayoutGuide,
+      LayoutSides::kTrailing | LayoutSides::kTop | LayoutSides::kLeading);
+
+  // Scroll View constraints to the height of its content. Can be overridden.
+  NSLayoutConstraint* heightConstraint = [scrollView.heightAnchor
+      constraintEqualToAnchor:scrollView.contentLayoutGuide.heightAnchor];
+  // UILayoutPriorityDefaultHigh is the default priority for content
+  // compression. Setting this lower avoids compressing the content of the
+  // scroll view.
+  heightConstraint.priority = UILayoutPriorityDefaultHigh - 1;
+  heightConstraint.active = YES;
+
+  // Scroll View constraint to the vertical center. Can be overridden.
+  NSLayoutConstraint* centerYConstraint =
+      [scrollView.centerYAnchor constraintEqualToAnchor:margins.centerYAnchor];
+  // This needs to be lower than the height constraint, so it's deprioritized.
+  // If this breaks, the scroll view is still constrained to the top toolbar and
+  // the bottom safe area or button.
+  centerYConstraint.priority = heightConstraint.priority - 1;
+  centerYConstraint.active = YES;
+
+  // Constraint the content of the scroll view to the size of the stack view.
+  // This defines the content area.
+  AddSameConstraints(stackView, scrollView);
+
+  // Disable horizontal scrolling and constraint the content size to the scroll
+  // view size.
+  [scrollView.widthAnchor
+      constraintEqualToAnchor:scrollView.contentLayoutGuide.widthAnchor]
+      .active = YES;
+
+  [scrollView.centerXAnchor constraintEqualToAnchor:margins.centerXAnchor]
+      .active = YES;
+
+  // Width Scroll View constraint. It changes based on the size class.
+  self.compactWidthConstraints = @[
+    [scrollView.widthAnchor constraintEqualToAnchor:margins.widthAnchor],
+  ];
+  self.regularWidthConstraints = @[
+    [scrollView.widthAnchor constraintEqualToAnchor:margins.widthAnchor
+                                         multiplier:kSafeAreaMultiplier],
+  ];
+
+  // The bottom anchor for the scroll view. It will be updated to the button top
+  // anchor if it exists.
+  NSLayoutYAxisAnchor* scrollViewBottomAnchor =
+      self.view.safeAreaLayoutGuide.bottomAnchor;
+
   if (self.primaryActionAvailable) {
-    UIButton* primaryActionButton =
-        [UIButton buttonWithType:UIButtonTypeSystem];
-    [primaryActionButton addTarget:self
-                            action:@selector(didTapPrimaryActionButton)
-                  forControlEvents:UIControlEventTouchUpInside];
-    [primaryActionButton setTitle:self.primaryActionString
-                         forState:UIControlStateNormal];
-    primaryActionButton.translatesAutoresizingMaskIntoConstraints = NO;
+    UIButton* primaryActionButton = [self createPrimaryActionButton];
     [self.view addSubview:primaryActionButton];
 
     // Primary Action Button constraints.
-    AddSameConstraintsToSides(
-        margins, primaryActionButton,
-        LayoutSides::kLeading | LayoutSides::kTrailing | LayoutSides::kBottom);
+    self.primaryButtonBottomVerticalConstraint =
+        [primaryActionButton.bottomAnchor
+            constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
+    [NSLayoutConstraint activateConstraints:@[
+      [primaryActionButton.leadingAnchor
+          constraintEqualToAnchor:scrollView.leadingAnchor],
+      [primaryActionButton.trailingAnchor
+          constraintEqualToAnchor:scrollView.trailingAnchor],
+      self.primaryButtonBottomVerticalConstraint,
+    ]];
+
+    scrollViewBottomAnchor = primaryActionButton.topAnchor;
+    self.primaryActionButton = primaryActionButton;
   }
 
-  // Done Button constraints.
-  AddSameConstraintsToSides(margins, doneButton,
-                            LayoutSides::kTrailing | LayoutSides::kTop);
+  // Constraing the image to the scroll view size and its aspect ratio.
+  [self.imageView
+      setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+                                      forAxis:UILayoutConstraintAxisHorizontal];
+  [self.imageView
+      setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+                                      forAxis:UILayoutConstraintAxisVertical];
+  CGFloat imageAspectRatio =
+      self.imageView.image.size.width / self.imageView.image.size.height;
+  self.scrollViewBottomVerticalConstraint = [scrollView.bottomAnchor
+      constraintLessThanOrEqualToAnchor:scrollViewBottomAnchor];
 
-  // Stack View (and its contents) constraints.
-  CGFloat imageAspectRatio = image.size.width / image.size.height;
-  [imageView.widthAnchor constraintEqualToAnchor:imageView.heightAnchor
-                                      multiplier:imageAspectRatio]
-      .active = YES;
-  AddSameCenterConstraints(margins, stackView);
-  AddSameConstraintsToSides(margins, stackView,
-                            LayoutSides::kLeading | LayoutSides::kTrailing);
+  [NSLayoutConstraint activateConstraints:@[
+    [self.imageView.widthAnchor
+        constraintEqualToAnchor:self.imageView.heightAnchor
+                     multiplier:imageAspectRatio],
+    [scrollView.topAnchor
+        constraintGreaterThanOrEqualToAnchor:topToolbar.bottomAnchor],
+    self.scrollViewBottomVerticalConstraint,
+  ]];
+}
+
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+
+  // Update fonts for specific content sizes.
+  if (previousTraitCollection.preferredContentSizeCategory !=
+      self.traitCollection.preferredContentSizeCategory) {
+    self.primaryActionButton.titleLabel.font =
+        PreferredFontForTextStyleWithMaxCategory(
+            UIFontTextStyleHeadline,
+            self.traitCollection.preferredContentSizeCategory,
+            UIContentSizeCategoryExtraExtraExtraLarge);
+  }
+
+  // Update constraints for different size classes.
+  BOOL hasNewHorizontalSizeClass =
+      previousTraitCollection.horizontalSizeClass !=
+      self.traitCollection.horizontalSizeClass;
+  BOOL hasNewVerticalSizeClass = previousTraitCollection.verticalSizeClass !=
+                                 self.traitCollection.verticalSizeClass;
+
+  if (hasNewHorizontalSizeClass || hasNewVerticalSizeClass) {
+    [self.view setNeedsUpdateConstraints];
+  }
+}
+
+- (void)viewSafeAreaInsetsDidChange {
+  [super viewSafeAreaInsetsDidChange];
+  [self.view setNeedsUpdateConstraints];
+}
+
+- (void)viewLayoutMarginsDidChange {
+  [super viewLayoutMarginsDidChange];
+  [self.view setNeedsUpdateConstraints];
+}
+
+- (void)updateViewConstraints {
+  CGFloat marginValue =
+      self.view.layoutMargins.left - self.view.safeAreaInsets.left;
+  self.scrollViewBottomVerticalConstraint.constant = -marginValue;
+  self.primaryButtonBottomVerticalConstraint.constant = -marginValue;
+  if (self.traitCollection.horizontalSizeClass ==
+      UIUserInterfaceSizeClassCompact) {
+    [NSLayoutConstraint deactivateConstraints:self.regularWidthConstraints];
+    [NSLayoutConstraint activateConstraints:self.compactWidthConstraints];
+  } else {
+    [NSLayoutConstraint deactivateConstraints:self.compactWidthConstraints];
+    [NSLayoutConstraint activateConstraints:self.regularWidthConstraints];
+  }
+  self.imageView.hidden =
+      self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;
+  [super updateViewConstraints];
 }
 
 #pragma mark - PasswordBreachConsumer
@@ -118,6 +231,12 @@
   self.primaryActionAvailable = primaryActionAvailable;
 }
 
+#pragma mark - UIToolbarDelegate
+
+- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
+  return UIBarPositionTopAttached;
+}
+
 #pragma mark - Private
 
 // Handle taps on the done button.
@@ -130,4 +249,110 @@
   [self.actionHandler passwordBreachPrimaryAction];
 }
 
+// Helper to create the top toolbar.
+- (UIToolbar*)createTopToolbar {
+  UIToolbar* topToolbar = [[UIToolbar alloc] init];
+  topToolbar.translucent = NO;
+  [topToolbar setShadowImage:[[UIImage alloc] init]
+          forToolbarPosition:UIBarPositionAny];
+  [topToolbar setBarTintColor:[UIColor colorNamed:kBackgroundColor]];
+  topToolbar.delegate = self;
+  UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                           target:self
+                           action:@selector(didTapDoneButton)];
+  UIBarButtonItem* spacer = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+                           target:nil
+                           action:nil];
+  topToolbar.items = @[ spacer, doneButton ];
+  topToolbar.translatesAutoresizingMaskIntoConstraints = NO;
+  return topToolbar;
+}
+
+// Helper to create the image view.
+- (UIImageView*)createImageView {
+  UIImage* image = [UIImage imageNamed:@"password_breach_illustration"];
+  UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
+  imageView.contentMode = UIViewContentModeScaleAspectFit;
+  imageView.translatesAutoresizingMaskIntoConstraints = NO;
+  return imageView;
+}
+
+// Helper to create the title label.
+- (UILabel*)createTitleLabel {
+  UILabel* title = [[UILabel alloc] init];
+  title.numberOfLines = 0;
+  UIFontDescriptor* descriptor = [UIFontDescriptor
+      preferredFontDescriptorWithTextStyle:UIFontTextStyleTitle1];
+  UIFont* font = [UIFont systemFontOfSize:descriptor.pointSize
+                                   weight:UIFontWeightBold];
+  UIFontMetrics* fontMetrics =
+      [UIFontMetrics metricsForTextStyle:UIFontTextStyleTitle1];
+  title.font = [fontMetrics scaledFontForFont:font];
+  title.textColor = [UIColor colorNamed:kTextPrimaryColor];
+  title.text = self.titleString;
+  title.textAlignment = NSTextAlignmentCenter;
+  title.translatesAutoresizingMaskIntoConstraints = NO;
+  title.adjustsFontForContentSizeCategory = YES;
+  return title;
+}
+
+// Helper to create the subtitle label.
+- (UILabel*)createSubtitleLabel {
+  UILabel* subtitle = [[UILabel alloc] init];
+  subtitle.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+  subtitle.numberOfLines = 0;
+  subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
+  subtitle.text = self.subtitleString;
+  subtitle.textAlignment = NSTextAlignmentCenter;
+  subtitle.translatesAutoresizingMaskIntoConstraints = NO;
+  subtitle.adjustsFontForContentSizeCategory = YES;
+  return subtitle;
+}
+
+// Helper to create the scroll view.
+- (UIScrollView*)createScrollView {
+  UIScrollView* scrollView = [[UIScrollView alloc] init];
+  scrollView.alwaysBounceVertical = NO;
+  scrollView.showsHorizontalScrollIndicator = NO;
+  scrollView.translatesAutoresizingMaskIntoConstraints = NO;
+  return scrollView;
+}
+
+// Helper to create the stack view.
+- (UIStackView*)createStackViewWithArrangedSubviews:
+    (NSArray<UIView*>*)subviews {
+  UIStackView* stackView =
+      [[UIStackView alloc] initWithArrangedSubviews:subviews];
+  [stackView setCustomSpacing:kStackViewSpacingAfterIllustration
+                    afterView:self.imageView];
+  stackView.axis = UILayoutConstraintAxisVertical;
+  stackView.alignment = UIStackViewAlignmentFill;
+  stackView.translatesAutoresizingMaskIntoConstraints = NO;
+  stackView.spacing = kStackViewSpacing;
+  return stackView;
+}
+
+// Helper to create the primary action button.
+- (UIButton*)createPrimaryActionButton {
+  UIButton* primaryActionButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  [primaryActionButton addTarget:self
+                          action:@selector(didTapPrimaryActionButton)
+                forControlEvents:UIControlEventTouchUpInside];
+  [primaryActionButton setTitle:self.primaryActionString
+                       forState:UIControlStateNormal];
+  primaryActionButton.contentEdgeInsets =
+      UIEdgeInsetsMake(kButtonVerticalInsets, 0, kButtonVerticalInsets, 0);
+  [primaryActionButton setBackgroundColor:[UIColor colorNamed:kBlueColor]];
+  UIColor* titleColor = [UIColor colorNamed:kSolidButtonTextColor];
+  [primaryActionButton setTitleColor:titleColor forState:UIControlStateNormal];
+  primaryActionButton.titleLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+  primaryActionButton.layer.cornerRadius = kPrimaryButtonCornerRadius;
+  primaryActionButton.titleLabel.adjustsFontForContentSizeCategory = NO;
+  primaryActionButton.translatesAutoresizingMaskIntoConstraints = NO;
+  return primaryActionButton;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png
index 5eb2ae9..3897e9f 100644
--- a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png
+++ b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png
Binary files differ
diff --git a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png
index 75f9b19..4cf2283 100644
--- a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png
+++ b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png
Binary files differ
diff --git a/ios/chrome/browser/ui/payments/payment_items_display_view_controller.mm b/ios/chrome/browser/ui/payments/payment_items_display_view_controller.mm
index 0431450..0122032 100644
--- a/ios/chrome/browser/ui/payments/payment_items_display_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_items_display_view_controller.mm
@@ -168,6 +168,7 @@
   self.styler.cellStyle = MDCCollectionViewCellStyleCard;
   self.styler.separatorInset =
       UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
+  self.styler.separatorColor = [UIColor colorNamed:kSeparatorColor];
 }
 
 #pragma mark UICollectionViewDataSource
diff --git a/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm
index f772c687..1d8caa1 100644
--- a/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm
@@ -250,6 +250,7 @@
   self.styler.cellStyle = MDCCollectionViewCellStyleCard;
   self.styler.separatorInset =
       UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
+  self.styler.separatorColor = [UIColor colorNamed:kSeparatorColor];
 }
 
 - (void)viewDidAppear:(BOOL)animated {
diff --git a/ios/chrome/browser/ui/payments/payment_request_error_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_error_view_controller.mm
index 00228c1..a078f78e 100644
--- a/ios/chrome/browser/ui/payments/payment_request_error_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_error_view_controller.mm
@@ -98,6 +98,7 @@
   self.styler.cellStyle = MDCCollectionViewCellStyleCard;
   self.styler.separatorInset =
       UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
+  self.styler.separatorColor = [UIColor colorNamed:kSeparatorColor];
 }
 
 #pragma mark UICollectionViewDataSource
diff --git a/ios/chrome/browser/ui/payments/payment_request_selector_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_selector_view_controller.mm
index 3534523..ad50c16 100644
--- a/ios/chrome/browser/ui/payments/payment_request_selector_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_selector_view_controller.mm
@@ -185,6 +185,7 @@
   self.styler.cellStyle = MDCCollectionViewCellStyleCard;
   self.styler.separatorInset =
       UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
+  self.styler.separatorColor = [UIColor colorNamed:kSeparatorColor];
 }
 
 #pragma mark UICollectionViewDataSource
diff --git a/ios/chrome/browser/ui/payments/payment_request_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_view_controller.mm
index f0e8e15..28c2d2c 100644
--- a/ios/chrome/browser/ui/payments/payment_request_view_controller.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_view_controller.mm
@@ -239,6 +239,7 @@
   self.styler.cellStyle = MDCCollectionViewCellStyleCard;
   self.styler.separatorInset =
       UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
+  self.styler.separatorColor = [UIColor colorNamed:kSeparatorColor];
 }
 
 - (void)updatePaymentSummaryItem {
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 6a9ae1f..3673d56 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -32,6 +32,7 @@
 #import "ios/chrome/browser/passwords/save_passwords_consumer.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
+#import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
@@ -155,6 +156,7 @@
 
 @interface PasswordsTableViewController () <
     BooleanObserver,
+    ChromeIdentityServiceObserver,
     PasswordDetailsTableViewControllerDelegate,
     PasswordExporterDelegate,
     PasswordExportActivityViewControllerDelegate,
@@ -192,6 +194,8 @@
   password_manager::DuplicatesMap blacklistedPasswordDuplicates_;
   // The current Chrome browser state.
   ios::ChromeBrowserState* browserState_;
+  // Authentication Service Observer.
+  std::unique_ptr<ChromeIdentityServiceObserverBridge> identityServiceObserver_;
   // Object storing the time of the previous successful re-authentication.
   // This is meant to be used by the |ReauthenticationModule| for keeping
   // re-authentications valid for a certain time interval within the scope
@@ -260,6 +264,8 @@
                      prefName:password_manager::prefs::
                                   kPasswordLeakDetectionEnabled];
       [passwordLeakCheckEnabled_ setObserver:self];
+      identityServiceObserver_.reset(
+          new ChromeIdentityServiceObserverBridge(self));
     }
     [self getLoginsFromPasswordStore];
     [self updateUIForEditState];
@@ -369,6 +375,7 @@
     if (base::FeatureList::IsEnabled(
             password_manager::features::kLeakDetection)) {
       leakCheckItem_ = [self leakCheckItem];
+      [self updateDetailTextLeakCheckItem];
       [model addItem:leakCheckItem_
           toSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
     }
@@ -532,6 +539,7 @@
     if ([self.tableViewModel
             hasItemForItemType:ItemTypePasswordLeakCheckSwitch
              sectionIdentifier:SectionIdentifierSavePasswordsSwitch]) {
+      [self updateDetailTextLeakCheckItem];
       [self reconfigureCellsForItems:@[ leakCheckItem_ ]];
     }
   } else {
@@ -555,6 +563,8 @@
 
   // Update the item.
   leakCheckItem_.on = [passwordLeakCheckEnabled_ value];
+  [self updateDetailTextLeakCheckItem];
+  [self reconfigureCellsForItems:@[ leakCheckItem_ ]];
 }
 
 #pragma mark - SavePasswordsConsumerDelegate
@@ -1258,6 +1268,7 @@
   AuthenticationService* authService =
       AuthenticationServiceFactory::GetForBrowserState(browserState_);
   [leakCheckItem_ setEnabled:enabled && authService->IsAuthenticated()];
+  [self updateDetailTextLeakCheckItem];
   [self reconfigureCellsForItems:@[ leakCheckItem_ ]];
 }
 
@@ -1273,6 +1284,29 @@
   }
 }
 
+// Updates the detail text of the leak check item based on the state.
+- (void)updateDetailTextLeakCheckItem {
+  if (!leakCheckItem_) {
+    return;
+  }
+  if (self.editing) {
+    // When editing keep the current detail text.
+    return;
+  }
+  if (leakCheckItem_.enabled) {
+    leakCheckItem_.detailText =
+        l10n_util::GetNSString(IDS_IOS_LEAK_CHECK_SIGNED_IN_DESC);
+    return;
+  }
+  if (leakCheckItem_.on) {
+    leakCheckItem_.detailText =
+        l10n_util::GetNSString(IDS_IOS_LEAK_CHECK_SIGNED_OUT_ENABLED_DESC);
+    return;
+  }
+  leakCheckItem_.detailText =
+      l10n_util::GetNSString(IDS_IOS_LEAK_CHECK_SIGNED_OUT_DISABLED_DESC);
+}
+
 #pragma mark - Testing
 
 - (void)setReauthenticationModuleForExporter:
@@ -1286,4 +1320,14 @@
   return _passwordExporter;
 }
 
+#pragma mark - ChromeIdentityServiceObserver
+
+- (void)identityListChanged {
+  [self reloadData];
+}
+
+- (void)chromeIdentityServiceWillBeDestroyed {
+  identityServiceObserver_.reset();
+}
+
 @end
diff --git a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn b/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
index 67b18a2..1fb9f69 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
@@ -14,7 +14,6 @@
     "//components/browser_sync",
     "//components/resources",
     "//components/sync",
-    "//components/sync/driver:resources",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
index 1036f667..92afd10 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
+++ b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "components/grit/sync_driver_resources.h"
+#include "components/grit/components_resources.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index f14c15e..918ab33 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -224,8 +224,9 @@
 }
 
 + (id<GREYMatcher>)closeButton {
-  return
-      [ChromeMatchersAppInterface buttonWithAccessibilityLabelID:(IDS_CLOSE)];
+  return grey_allOf(
+      [ChromeMatchersAppInterface buttonWithAccessibilityLabelID:(IDS_CLOSE)],
+      grey_not(grey_accessibilityTrait(UIAccessibilityTraitNotEnabled)), nil);
 }
 
 + (id<GREYMatcher>)forwardButton {
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index e93d785..e5a4e7e 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -133,7 +133,7 @@
  private:
   ~VaapiEncodeJob() override = default;
 
-  // Input surface for video frame data.
+  // Input surface for video frame data or scaled data.
   const scoped_refptr<VASurface> input_surface_;
 
   // Surface for the reconstructed picture, used for reference
@@ -383,39 +383,48 @@
     return;
   }
 
-  aligned_input_size_ =
-      GetInputFrameSize(config.input_format, config.input_visible_size);
-  if (aligned_input_size_.IsEmpty()) {
-    NOTIFY_ERROR(kPlatformFailureError, "Failed to get frame size");
-    return;
-  }
-
   output_buffer_byte_size_ = encoder_->GetBitstreamBufferSize();
-  const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames();
-
-  // Use at least kMinNumFramesInFlight if encoder requested less for
-  // pipeline depth.
-  const size_t num_frames_in_flight =
-      std::max(kMinNumFramesInFlight, max_ref_frames);
-  DVLOGF(1) << "Frames in flight: " << num_frames_in_flight;
 
   va_surface_release_cb_ = BindToCurrentLoop(
       base::BindRepeating(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
                           base::Unretained(this)));
+  vpp_va_surface_release_cb_ = BindToCurrentLoop(
+      base::BindRepeating(&VaapiVideoEncodeAccelerator::RecycleVPPVASurfaceID,
+                          base::Unretained(this)));
 
-  // In native input mode, an input surface is needed only if scaling
-  // is not required. Since we cannot find the necessity of the scaling here,
-  // we allocate input surfaces always, which is redundant.
-  //
-  // TODO(hiroh): Think about moving this surface creation in the first
-  // Encode().
-  va_surfaces_per_video_frame_ =
-      kNumSurfacesForOutputPicture + kNumSurfacesPerInputVideoFrame;
+  // The surface size for a reconstructed surface is a coded size.
+  gfx::Size reconstructed_surface_size = encoder_->GetCodedSize();
+  if (native_input_mode_) {
+    // In native input mode, we do not need surfaces for input frames.
+    va_surfaces_per_video_frame_ = kNumSurfacesForOutputPicture;
+    // The aligned input size must be the same as a size of a native graphic
+    // buffer.
+    aligned_input_size_ =
+        GetInputFrameSize(config.input_format, config.input_visible_size);
+    if (aligned_input_size_.IsEmpty()) {
+      NOTIFY_ERROR(kPlatformFailureError, "Failed to get frame size");
+      return;
+    }
+  } else {
+    // In non-native mode, we need to create additional surfaces for input
+    // frames.
+    va_surfaces_per_video_frame_ =
+        kNumSurfacesForOutputPicture + kNumSurfacesPerInputVideoFrame;
+    // There is no way to know aligned size that a client provided, so we
+    // request coded size.
+    aligned_input_size_ = encoder_->GetCodedSize();
+  }
+
+  // The number of required buffers is the number of required reference frames
+  // + 1 for the current frame to be encoded.
+  const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames();
+  num_frames_in_flight_ = std::max(kMinNumFramesInFlight, max_ref_frames);
+  DVLOGF(1) << "Frames in flight: " << num_frames_in_flight_;
 
   if (!vaapi_wrapper_->CreateContextAndSurfaces(
-          kVaSurfaceFormat, aligned_input_size_,
+          kVaSurfaceFormat, reconstructed_surface_size,
           VaapiWrapper::SurfaceUsageHint::kVideoEncoder,
-          (num_frames_in_flight + 1) * va_surfaces_per_video_frame_,
+          (num_frames_in_flight_ + 1) * va_surfaces_per_video_frame_,
           &available_va_surface_ids_)) {
     NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
     return;
@@ -423,7 +432,7 @@
 
   child_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, client_,
-                                num_frames_in_flight, aligned_input_size_,
+                                num_frames_in_flight_, aligned_input_size_,
                                 output_buffer_byte_size_));
 
   SetState(kEncoding);
@@ -438,6 +447,15 @@
   EncodePendingInputs();
 }
 
+void VaapiVideoEncodeAccelerator::RecycleVPPVASurfaceID(
+    VASurfaceID va_surface_id) {
+  DVLOGF(4) << "va_surface_id: " << va_surface_id;
+  DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+
+  available_vpp_va_surface_ids_.push_back(va_surface_id);
+  EncodePendingInputs();
+}
+
 void VaapiVideoEncodeAccelerator::ExecuteEncode(VASurfaceID va_surface_id) {
   DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
   if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(va_surface_id))
@@ -566,7 +584,8 @@
     return nullptr;
   }
 
-  if (available_va_surface_ids_.size() < va_surfaces_per_video_frame_) {
+  if (available_va_surface_ids_.size() < va_surfaces_per_video_frame_ ||
+      (vpp_vaapi_wrapper_ && available_vpp_va_surface_ids_.empty())) {
     DVLOGF(4) << "Not enough surfaces available";
     return nullptr;
   }
@@ -618,15 +637,27 @@
                      "Failed to initialize VppVaapiWrapper");
         return nullptr;
       }
+
+      // Allocate the same number of surfaces as reconstructed surfaces.
+      if (vpp_vaapi_wrapper_->CreateContextAndSurfaces(
+              kVaSurfaceFormat, aligned_input_size_,
+              VaapiWrapper::SurfaceUsageHint::kVideoProcessWrite,
+              num_frames_in_flight_ + 1, &available_vpp_va_surface_ids_)) {
+        NOTIFY_ERROR(kPlatformFailureError,
+                     "Failed creating VASurfaces for scaling");
+        vpp_vaapi_wrapper_ = nullptr;
+        return nullptr;
+      };
     }
-    scoped_refptr<VASurface> scaled_surface =
-        new VASurface(available_va_surface_ids_.back(), aligned_input_size_,
-                      kVaSurfaceFormat, base::BindOnce(va_surface_release_cb_));
-    available_va_surface_ids_.pop_back();
+
+    scoped_refptr<VASurface> scaled_surface = new VASurface(
+        available_vpp_va_surface_ids_.back(), aligned_input_size_,
+        kVaSurfaceFormat, base::BindOnce(vpp_va_surface_release_cb_));
+    available_vpp_va_surface_ids_.pop_back();
     // Scale frame->coded_size() -> |aligned_input_size_| here.
     vpp_vaapi_wrapper_->BlitSurface(input_surface, scaled_surface);
-    // We can destroy the original |input_surface| because the buffer is alive
-    // as long as |frame| is alive.
+    // We can destroy the original |input_surface| because the buffer is already
+    // copied to scaled_surface.
     input_surface = std::move(scaled_surface);
   }
 
@@ -815,6 +846,11 @@
   // Clean up members that are to be accessed on the encoder thread only.
   if (vaapi_wrapper_)
     vaapi_wrapper_->DestroyContextAndSurfaces(available_va_surface_ids_);
+  if (vpp_vaapi_wrapper_) {
+    vpp_vaapi_wrapper_->DestroyContextAndSurfaces(
+        available_vpp_va_surface_ids_);
+  }
+
   available_va_buffer_ids_.clear();
 
   while (!available_bitstream_buffers_.empty())
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index 8ba3d9d..0f77a2e3 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -109,9 +109,13 @@
   void ExecuteEncode(VASurfaceID va_surface_id);
 
   // Callback that returns a no longer used VASurfaceID to
-  // available_va_surface_ids_ for reuse.
+  // |available_va_surface_ids_| for reuse.
   void RecycleVASurfaceID(VASurfaceID va_surface_id);
 
+  // Callback that returns a no longer used VASurfaceID to
+  // |available_vpp_va_surface_ids_| for reuse.
+  void RecycleVPPVASurfaceID(VASurfaceID va_surface_id);
+
   // Returns a bitstream buffer to the client if both a previously executed job
   // awaits to be completed and we have bitstream buffers available to download
   // the encoded data into.
@@ -163,6 +167,9 @@
   // and two otherwise.
   size_t va_surfaces_per_video_frame_;
 
+  // The number of frames that needs to be held on encoding.
+  size_t num_frames_in_flight_;
+
   // All of the members below must be accessed on the encoder_thread_,
   // while it is running.
 
@@ -172,8 +179,10 @@
   // Encoder instance managing video codec state and preparing encode jobs.
   std::unique_ptr<AcceleratedVideoEncoder> encoder_;
 
-  // VA surfaces available for reuse.
+  // VA surfaces available for encoding.
   std::vector<VASurfaceID> available_va_surface_ids_;
+  // VA surfaces available for scaling.
+  std::vector<VASurfaceID> available_vpp_va_surface_ids_;
 
   // VASurfaceIDs internal format.
   static constexpr unsigned int kVaSurfaceFormat = VA_RT_FORMAT_YUV420;
@@ -183,6 +192,7 @@
 
   // Callback via which finished VA surfaces are returned to us.
   base::RepeatingCallback<void(VASurfaceID)> va_surface_release_cb_;
+  base::RepeatingCallback<void(VASurfaceID)> vpp_va_surface_release_cb_;
 
   // Queue of input frames to be encoded.
   base::queue<std::unique_ptr<InputFrameRef>> input_queue_;
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e92b43d..0c3328d 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -2114,6 +2114,9 @@
     case SurfaceUsageHint::kVideoEncoder:
       attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
       break;
+    case SurfaceUsageHint::kVideoProcessWrite:
+      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
+      break;
     case SurfaceUsageHint::kGeneric:
       attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
       break;
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index f0cbe42..16b0134f 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -98,6 +98,7 @@
   enum class SurfaceUsageHint : uint8_t {
     kVideoDecoder,
     kVideoEncoder,
+    kVideoProcessWrite,
     kGeneric,
   };
 
diff --git a/media/midi/BUILD.gn b/media/midi/BUILD.gn
index 60005a4..8d11456 100644
--- a/media/midi/BUILD.gn
+++ b/media/midi/BUILD.gn
@@ -240,3 +240,13 @@
     ":midi",
   ]
 }
+
+fuzzer_test("usb_midi_descriptor_parser_fuzzer") {
+  sources = [ "usb_midi_descriptor_parser_fuzzer.cc" ] + usb_midi_sources
+  seed_corpus = "fuzz/usb_midi_descriptor_corpus"
+  deps = [
+    ":midi",
+    ":mojo",
+    "//base",
+  ]
+}
diff --git a/media/midi/fuzz/usb_midi_descriptor_corpus/a b/media/midi/fuzz/usb_midi_descriptor_corpus/a
new file mode 100644
index 0000000..45a8ca0
--- /dev/null
+++ b/media/midi/fuzz/usb_midi_descriptor_corpus/a
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/media/midi/fuzz/usb_midi_descriptor_corpus/b b/media/midi/fuzz/usb_midi_descriptor_corpus/b
new file mode 100644
index 0000000..4937eea
--- /dev/null
+++ b/media/midi/fuzz/usb_midi_descriptor_corpus/b
Binary files differ
diff --git a/media/midi/fuzz/usb_midi_descriptor_corpus/c b/media/midi/fuzz/usb_midi_descriptor_corpus/c
new file mode 100644
index 0000000..5218137
--- /dev/null
+++ b/media/midi/fuzz/usb_midi_descriptor_corpus/c
Binary files differ
diff --git a/media/midi/fuzz/usb_midi_descriptor_corpus/d b/media/midi/fuzz/usb_midi_descriptor_corpus/d
new file mode 100644
index 0000000..95af454
--- /dev/null
+++ b/media/midi/fuzz/usb_midi_descriptor_corpus/d
Binary files differ
diff --git a/media/midi/fuzz/usb_midi_descriptor_corpus/e b/media/midi/fuzz/usb_midi_descriptor_corpus/e
new file mode 100644
index 0000000..33fd3688
--- /dev/null
+++ b/media/midi/fuzz/usb_midi_descriptor_corpus/e
Binary files differ
diff --git a/media/midi/usb_midi_descriptor_parser_fuzzer.cc b/media/midi/usb_midi_descriptor_parser_fuzzer.cc
new file mode 100644
index 0000000..4d851d3
--- /dev/null
+++ b/media/midi/usb_midi_descriptor_parser_fuzzer.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "media/midi/usb_midi_descriptor_parser.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  midi::UsbMidiDescriptorParser parser;
+  std::vector<midi::UsbMidiJack> jacks;
+  parser.Parse(nullptr, data, size, &jacks);
+
+  return 0;
+}
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageValidator.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageValidator.java
index 03e95b0..0511236 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageValidator.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageValidator.java
@@ -35,6 +35,12 @@
     private static boolean isValid(NdefRecord record) {
         if (record == null) return false;
         if (record.recordType.equals(NdefMessageUtils.RECORD_TYPE_EMPTY)) return true;
-        return record.data != null && record.mediaType != null && !record.mediaType.isEmpty();
+        if (record.data == null) return false;
+        if ((record.recordType.equals(NdefMessageUtils.RECORD_TYPE_JSON)
+                    || record.recordType.equals(NdefMessageUtils.RECORD_TYPE_OPAQUE))
+                && (record.mediaType == null || record.mediaType.isEmpty())) {
+            return false;
+        }
+        return true;
     }
 }
diff --git a/services/preferences/tracked/interceptable_pref_filter.cc b/services/preferences/tracked/interceptable_pref_filter.cc
index 8eaeabe9..64d13cb 100644
--- a/services/preferences/tracked/interceptable_pref_filter.cc
+++ b/services/preferences/tracked/interceptable_pref_filter.cc
@@ -12,10 +12,10 @@
 InterceptablePrefFilter::~InterceptablePrefFilter() {}
 
 void InterceptablePrefFilter::FilterOnLoad(
-    const PostFilterOnLoadCallback& post_filter_on_load_callback,
+    PostFilterOnLoadCallback post_filter_on_load_callback,
     std::unique_ptr<base::DictionaryValue> pref_store_contents) {
   if (filter_on_load_interceptor_.is_null()) {
-    FinalizeFilterOnLoad(post_filter_on_load_callback,
+    FinalizeFilterOnLoad(std::move(post_filter_on_load_callback),
                          std::move(pref_store_contents), false);
   } else {
     // Note, in practice (in the implementation as it was in May 2014) it would
@@ -23,18 +23,19 @@
     // having to augment the API everywhere to explicitly enforce the ownership
     // model as it happens to currently be: make the relationship simpler by
     // weakly binding the FinalizeFilterOnLoadCallback below to |this|.
-    const FinalizeFilterOnLoadCallback finalize_filter_on_load(
-        base::Bind(&InterceptablePrefFilter::FinalizeFilterOnLoad, AsWeakPtr(),
-                   post_filter_on_load_callback));
+    FinalizeFilterOnLoadCallback finalize_filter_on_load(
+        base::BindOnce(&InterceptablePrefFilter::FinalizeFilterOnLoad,
+                       AsWeakPtr(), std::move(post_filter_on_load_callback)));
     std::move(filter_on_load_interceptor_)
-        .Run(finalize_filter_on_load, std::move(pref_store_contents));
+        .Run(std::move(finalize_filter_on_load),
+             std::move(pref_store_contents));
   }
 }
 
 void InterceptablePrefFilter::InterceptNextFilterOnLoad(
-    const FilterOnLoadInterceptor& filter_on_load_interceptor) {
+    FilterOnLoadInterceptor filter_on_load_interceptor) {
   DCHECK(filter_on_load_interceptor_.is_null());
-  filter_on_load_interceptor_ = filter_on_load_interceptor;
+  filter_on_load_interceptor_ = std::move(filter_on_load_interceptor);
 }
 
 void InterceptablePrefFilter::OnStoreDeletionFromDisk() {}
diff --git a/services/preferences/tracked/interceptable_pref_filter.h b/services/preferences/tracked/interceptable_pref_filter.h
index 0c3af40d..8844ff0 100644
--- a/services/preferences/tracked/interceptable_pref_filter.h
+++ b/services/preferences/tracked/interceptable_pref_filter.h
@@ -24,31 +24,30 @@
   // hand back the |prefs| it was handed for early filtering. |prefs_altered|
   // indicates whether the |prefs| were actually altered by the
   // FilterOnLoadInterceptor before being handed back.
-  typedef base::Callback<void(std::unique_ptr<base::DictionaryValue> prefs,
-                              bool prefs_altered)>
-      FinalizeFilterOnLoadCallback;
+  using FinalizeFilterOnLoadCallback =
+      base::OnceCallback<void(std::unique_ptr<base::DictionaryValue> prefs,
+                              bool prefs_altered)>;
 
   // A callback to be invoked from FilterOnLoad. It takes ownership of prefs
   // and may modify them before handing them back to this
   // InterceptablePrefFilter via |finalize_filter_on_load|.
-  typedef base::Callback<void(
-      const FinalizeFilterOnLoadCallback& finalize_filter_on_load,
-      std::unique_ptr<base::DictionaryValue> prefs)>
-      FilterOnLoadInterceptor;
+  using FilterOnLoadInterceptor = base::OnceCallback<void(
+      FinalizeFilterOnLoadCallback finalize_filter_on_load,
+      std::unique_ptr<base::DictionaryValue> prefs)>;
 
   InterceptablePrefFilter();
   ~InterceptablePrefFilter() override;
 
   // PrefFilter partial implementation.
   void FilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents) override;
 
   // Registers |filter_on_load_interceptor| to intercept the next FilterOnLoad
   // event. At most one FilterOnLoadInterceptor should be registered per
   // PrefFilter.
   void InterceptNextFilterOnLoad(
-      const FilterOnLoadInterceptor& filter_on_load_interceptor);
+      FilterOnLoadInterceptor filter_on_load_interceptor);
 
   void OnStoreDeletionFromDisk() override;
 
@@ -57,7 +56,7 @@
   // InterceptablePrefFilter and hands back the |pref_store_contents| to the
   // initial caller of FilterOnLoad.
   virtual void FinalizeFilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents,
       bool prefs_altered) = 0;
 
diff --git a/services/preferences/tracked/interceptable_pref_filter_unittest.cc b/services/preferences/tracked/interceptable_pref_filter_unittest.cc
index a8bf7b8..8c687982 100644
--- a/services/preferences/tracked/interceptable_pref_filter_unittest.cc
+++ b/services/preferences/tracked/interceptable_pref_filter_unittest.cc
@@ -16,11 +16,11 @@
 class TestInterceptablePrefFilter : public InterceptablePrefFilter {
  public:
   void FinalizeFilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents,
       bool prefs_altered) override {
-    post_filter_on_load_callback.Run(std::move(pref_store_contents),
-                                     prefs_altered);
+    std::move(post_filter_on_load_callback)
+        .Run(std::move(pref_store_contents), prefs_altered);
   }
 
   void FilterUpdate(const std::string& path) override {}
@@ -31,10 +31,10 @@
   }
 };
 
-void NoOpIntercept(const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
+void NoOpIntercept(InterceptablePrefFilter::FinalizeFilterOnLoadCallback
                        finalize_filter_on_load,
                    std::unique_ptr<base::DictionaryValue> prefs) {
-  finalize_filter_on_load.Run(std::move(prefs), false);
+  std::move(finalize_filter_on_load).Run(std::move(prefs), false);
 }
 
 void DeleteFilter(std::unique_ptr<TestInterceptablePrefFilter>* filter,
@@ -45,8 +45,8 @@
 
 TEST(InterceptablePrefFilterTest, CallbackDeletes) {
   auto filter = std::make_unique<TestInterceptablePrefFilter>();
-  filter->InterceptNextFilterOnLoad(base::Bind(&NoOpIntercept));
-  filter->FilterOnLoad(base::Bind(&DeleteFilter, &filter),
+  filter->InterceptNextFilterOnLoad(base::BindOnce(&NoOpIntercept));
+  filter->FilterOnLoad(base::BindOnce(&DeleteFilter, &filter),
                        std::make_unique<base::DictionaryValue>());
   EXPECT_FALSE(filter);
 }
diff --git a/services/preferences/tracked/pref_hash_filter.cc b/services/preferences/tracked/pref_hash_filter.cc
index 7c479e28c..c3ce65e8 100644
--- a/services/preferences/tracked/pref_hash_filter.cc
+++ b/services/preferences/tracked/pref_hash_filter.cc
@@ -210,7 +210,7 @@
 }
 
 void PrefHashFilter::FinalizeFilterOnLoad(
-    const PostFilterOnLoadCallback& post_filter_on_load_callback,
+    PostFilterOnLoadCallback post_filter_on_load_callback,
     std::unique_ptr<base::DictionaryValue> pref_store_contents,
     bool prefs_altered) {
   DCHECK(pref_store_contents);
@@ -259,8 +259,8 @@
   UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
                       base::TimeTicks::Now() - checkpoint);
 
-  post_filter_on_load_callback.Run(std::move(pref_store_contents),
-                                   prefs_altered);
+  std::move(post_filter_on_load_callback)
+      .Run(std::move(pref_store_contents), prefs_altered);
 }
 
 // static
diff --git a/services/preferences/tracked/pref_hash_filter.h b/services/preferences/tracked/pref_hash_filter.h
index 5dc93094..ae0cbd5d 100644
--- a/services/preferences/tracked/pref_hash_filter.h
+++ b/services/preferences/tracked/pref_hash_filter.h
@@ -97,7 +97,7 @@
  private:
   // InterceptablePrefFilter implementation.
   void FinalizeFilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents,
       bool prefs_altered) override;
 
diff --git a/services/preferences/tracked/pref_hash_filter_unittest.cc b/services/preferences/tracked/pref_hash_filter_unittest.cc
index c5f80b2..8bdeff77 100644
--- a/services/preferences/tracked/pref_hash_filter_unittest.cc
+++ b/services/preferences/tracked/pref_hash_filter_unittest.cc
@@ -605,8 +605,8 @@
   // |pref_hash_filter_|.
   void DoFilterOnLoad(bool expect_prefs_modifications) {
     pref_hash_filter_->FilterOnLoad(
-        base::Bind(&PrefHashFilterTest::GetPrefsBack, base::Unretained(this),
-                   expect_prefs_modifications),
+        base::BindOnce(&PrefHashFilterTest::GetPrefsBack,
+                       base::Unretained(this), expect_prefs_modifications),
         std::move(pref_store_contents_));
   }
 
diff --git a/services/preferences/tracked/tracked_preferences_migration.cc b/services/preferences/tracked/tracked_preferences_migration.cc
index ac8e39d..32a3bd3 100644
--- a/services/preferences/tracked/tracked_preferences_migration.cc
+++ b/services/preferences/tracked/tracked_preferences_migration.cc
@@ -45,7 +45,7 @@
   // class and then calls MigrateIfReady();
   void InterceptFilterOnLoad(
       PrefFilterID id,
-      const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
+      InterceptablePrefFilter::FinalizeFilterOnLoadCallback
           finalize_filter_on_load,
       std::unique_ptr<base::DictionaryValue> prefs);
 
@@ -220,16 +220,16 @@
 
 void TrackedPreferencesMigrator::InterceptFilterOnLoad(
     PrefFilterID id,
-    const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
+    InterceptablePrefFilter::FinalizeFilterOnLoadCallback
         finalize_filter_on_load,
     std::unique_ptr<base::DictionaryValue> prefs) {
   switch (id) {
     case UNPROTECTED_PREF_FILTER:
-      finalize_unprotected_filter_on_load_ = finalize_filter_on_load;
+      finalize_unprotected_filter_on_load_ = std::move(finalize_filter_on_load);
       unprotected_prefs_ = std::move(prefs);
       break;
     case PROTECTED_PREF_FILTER:
-      finalize_protected_filter_on_load_ = finalize_filter_on_load;
+      finalize_protected_filter_on_load_ = std::move(finalize_filter_on_load);
       protected_prefs_ = std::move(prefs);
       break;
   }
@@ -271,10 +271,10 @@
   }
 
   // Hand the processed prefs back to their respective filters.
-  finalize_unprotected_filter_on_load_.Run(std::move(unprotected_prefs_),
-                                           unprotected_prefs_altered);
-  finalize_protected_filter_on_load_.Run(std::move(protected_prefs_),
-                                         protected_prefs_altered);
+  std::move(finalize_unprotected_filter_on_load_)
+      .Run(std::move(unprotected_prefs_), unprotected_prefs_altered);
+  std::move(finalize_protected_filter_on_load_)
+      .Run(std::move(protected_prefs_), protected_prefs_altered);
 
   if (unprotected_prefs_need_cleanup) {
     // Schedule a cleanup of the |protected_pref_names_| from the unprotected
@@ -324,10 +324,10 @@
 
   // The callbacks bound below will own this TrackedPreferencesMigrator by
   // reference.
-  unprotected_pref_filter->InterceptNextFilterOnLoad(base::Bind(
+  unprotected_pref_filter->InterceptNextFilterOnLoad(base::BindOnce(
       &TrackedPreferencesMigrator::InterceptFilterOnLoad, prefs_migrator,
       TrackedPreferencesMigrator::UNPROTECTED_PREF_FILTER));
-  protected_pref_filter->InterceptNextFilterOnLoad(base::Bind(
+  protected_pref_filter->InterceptNextFilterOnLoad(base::BindOnce(
       &TrackedPreferencesMigrator::InterceptFilterOnLoad, prefs_migrator,
       TrackedPreferencesMigrator::PROTECTED_PREF_FILTER));
 }
diff --git a/services/preferences/tracked/tracked_preferences_migration_unittest.cc b/services/preferences/tracked/tracked_preferences_migration_unittest.cc
index 9d1df31..87500d7 100644
--- a/services/preferences/tracked/tracked_preferences_migration_unittest.cc
+++ b/services/preferences/tracked/tracked_preferences_migration_unittest.cc
@@ -56,11 +56,11 @@
  private:
   // InterceptablePrefFilter implementation.
   void FinalizeFilterOnLoad(
-      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      PostFilterOnLoadCallback post_filter_on_load_callback,
       std::unique_ptr<base::DictionaryValue> pref_store_contents,
       bool prefs_altered) override {
-    post_filter_on_load_callback.Run(std::move(pref_store_contents),
-                                     prefs_altered);
+    std::move(post_filter_on_load_callback)
+        .Run(std::move(pref_store_contents), prefs_altered);
   }
 };
 
@@ -228,14 +228,14 @@
     switch (store_id) {
       case MOCK_UNPROTECTED_PREF_STORE:
         mock_unprotected_pref_filter_.FilterOnLoad(
-            base::Bind(&TrackedPreferencesMigrationTest::GetPrefsBack,
-                       base::Unretained(this), MOCK_UNPROTECTED_PREF_STORE),
+            base::BindOnce(&TrackedPreferencesMigrationTest::GetPrefsBack,
+                           base::Unretained(this), MOCK_UNPROTECTED_PREF_STORE),
             std::move(unprotected_prefs_));
         break;
       case MOCK_PROTECTED_PREF_STORE:
         mock_protected_pref_filter_.FilterOnLoad(
-            base::Bind(&TrackedPreferencesMigrationTest::GetPrefsBack,
-                       base::Unretained(this), MOCK_PROTECTED_PREF_STORE),
+            base::BindOnce(&TrackedPreferencesMigrationTest::GetPrefsBack,
+                           base::Unretained(this), MOCK_PROTECTED_PREF_STORE),
             std::move(protected_prefs_));
         break;
     }
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 868ad5a..3a4aeae3 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -104,7 +104,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.android_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -151,7 +151,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.chrome_public_test_apk.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -199,7 +199,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -247,7 +247,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_shell_test_apk.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -295,7 +295,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_unittests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
@@ -343,7 +343,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.unit_tests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 0f6e0f90..d677c7f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -10634,7 +10634,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.browser_tests.filter"
         ],
         "merge": {
@@ -10655,7 +10655,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_browsertests.filter"
         ],
         "merge": {
@@ -10676,7 +10676,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_unittests.filter"
         ],
         "merge": {
@@ -10697,7 +10697,7 @@
       },
       {
         "args": [
-          "--enable-features=BackForwardCache",
+          "--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction",
           "--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.unit_tests.filter"
         ],
         "merge": {
diff --git a/testing/buildbot/filters/bfcache.content_browsertests.filter b/testing/buildbot/filters/bfcache.content_browsertests.filter
index d56c279..6ecf849 100644
--- a/testing/buildbot/filters/bfcache.content_browsertests.filter
+++ b/testing/buildbot/filters/bfcache.content_browsertests.filter
@@ -80,10 +80,6 @@
 # https://crbug.com/1004786
 -SitePerProcessBrowserTest.TestChildProcessImportance
 
-# In debug mode. The FrameHostInterceptor fails, because it doesn't take into
-# account pages in the BackForwardCache.
--SecurityExploitBrowserTest.InvalidBeginNavigationInitiator
-
 # NOTREACHED() is hit in RenderFrameHostManager::GetFrameHostForNavigation,
 # because, "A frame that's pending deletion should never be navigated.".
 -NavigationControllerBrowserTest.PageStateWithIframeAfterForwardInCompetingFrames
@@ -113,3 +109,7 @@
 -RenderViewHostTest.IsFocusedElementEditable
 -TouchInputBrowserTest.*
 -TouchpadPinchBrowserTest.*
+
+# Started failing around when https://crrev.com/c/1819251 was submitted.
+# https://crbug.com/1013550
+-SessionHistoryTest.GoBackToCrossSitePostWithRedirect
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 8bf82e7b..ae3d6d5 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2103,11 +2103,6 @@
     "label": "//ios/testing:ocmock_support_unittests",
     "type": "raw",
   },
-  "opus_decoder_unittests": {
-    "args": [],
-    "label": "//third_party/opus:test_opus_decode",
-    "type": "raw",
-  },
   "origin_policy_parser_fuzzer": {
     "label": "//content/test/fuzzer:origin_policy_parser_fuzzer",
     "type": "fuzzer",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d450a419..6861d96f 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -235,14 +235,14 @@
     'bfcache_android_specific_gtests': {
       'bf_cache_android_browsertests': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.android_browsertests.filter'
         ],
         'test': 'android_browsertests',
       },
       'bf_cache_content_shell_test_apk': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_shell_test_apk.filter'
         ],
         'swarming': {
@@ -252,7 +252,7 @@
       },
       'bf_cache_chrome_public_test_apk': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.chrome_public_test_apk.filter'
         ],
         'swarming': {
@@ -265,7 +265,7 @@
     'bfcache_generic_gtests': {
       'bf_cache_content_unittests': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_unittests.filter'
         ],
         'swarming': {
@@ -275,7 +275,7 @@
       },
       'bf_cache_unit_tests': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.unit_tests.filter'
         ],
         'swarming': {
@@ -285,7 +285,7 @@
       },
       'bf_cache_content_browsertests': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.content_browsertests.filter'
         ],
         'swarming': {
@@ -298,7 +298,7 @@
     'bfcache_linux_specific_gtests': {
       'bf_cache_browser_tests': {
         'args': [
-          '--enable-features=BackForwardCache',
+          '--enable-features=BackForwardCache,BackForwardCacheNoTimeEviction',
           '--test-launcher-filter-file=../../testing/buildbot/filters/bfcache.browser_tests.filter'
         ],
         'swarming': {
@@ -4570,13 +4570,6 @@
       }
     },
 
-    'opus_roll_validation_tests': {
-      'opus_decoder_unittests': {
-        # Ad-hoc binary which returns 0 on success.
-       'test': 'audio_decoder_unittests',
-      },
-    },
-
     'ozone_linux_gtests': {
       'services_unittests': {},
       'ozone_unittests': {},
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index a014863..1a12e436 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -376,30 +376,6 @@
       }
     ]
   },
-  "android-opus-kitkat-arm-rel": {
-    "isolated_scripts": [
-      {
-        "isolate_name": "opus_decoder_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "opus_decoder_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ]
-        },
-        "test": "audio_decoder_unittests"
-      }
-    ]
-  },
   "android_blink_rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 81dc75b..37f16c8d 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -4430,16 +4430,6 @@
         },
         'os_type': 'android',
       },
-      'android-opus-kitkat-arm-rel': {
-        'mixins': [
-          'kitkat',
-          'hammerhead',
-        ],
-        'os_type': 'android',
-        'test_suites': {
-          'isolated_scripts': 'opus_roll_validation_tests',
-        },
-      },
       'android_blink_rel': {
         'mixins': [
           'kitkat',
diff --git a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
index fea69827..0bebb931 100644
--- a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
+++ b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
@@ -73,6 +73,8 @@
       return "WebVR";
     case WebSchedulerTrackedFeature::kWebXR:
       return "WebXR";
+    case WebSchedulerTrackedFeature::kWebLocks:
+      return "WebLocks";
   }
 }
 
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 7a4d269..9e28e12 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -108,9 +108,11 @@
     # TODO(crbug.com/787254): Remove the group of mock_.h files
     # below when content/renderer/media/webrtc Onion souping is
     # done.
+    "web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h",
     "web/modules/peerconnection/mock_data_channel_impl.h",
     "web/modules/peerconnection/mock_peer_connection_dependency_factory.h",
     "web/modules/peerconnection/mock_peer_connection_impl.h",
+    "web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h",
     "web/modules/peerconnection/webrtc_stats_report_obtainer.h",
   ]
   deps = [
@@ -387,6 +389,9 @@
     "web/modules/peerconnection/media_stream_video_webrtc_sink.h",
     "web/modules/peerconnection/peer_connection_dependency_factory.h",
     "web/modules/peerconnection/rtc_rtp_receiver_impl.h",
+    "web/modules/peerconnection/rtc_rtp_sender_impl.h",
+    "web/modules/peerconnection/rtc_rtp_transceiver_impl.h",
+    "web/modules/peerconnection/transceiver_state_surfacer.h",
     "web/modules/peerconnection/webrtc_media_stream_track_adapter.h",
     "web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h",
     "web/modules/service_worker/web_service_worker_context_client.h",
diff --git a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
index 53f27e4..8572a80 100644
--- a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
+++ b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
@@ -71,8 +71,10 @@
 
   kSharedWorker = 32,
 
+  kWebLocks = 33,
+
   // NB: This enum is used in a bitmask, so kMaxValue must be less than 64.
-  kMaxValue = kSharedWorker
+  kMaxValue = kWebLocks
 };
 
 static_assert(static_cast<uint32_t>(WebSchedulerTrackedFeature::kMaxValue) < 64,
diff --git a/third_party/blink/public/mojom/installedapp/installed_app_provider.mojom b/third_party/blink/public/mojom/installedapp/installed_app_provider.mojom
index 2bcfc0d4..d3c24e7 100644
--- a/third_party/blink/public/mojom/installedapp/installed_app_provider.mojom
+++ b/third_party/blink/public/mojom/installedapp/installed_app_provider.mojom
@@ -11,6 +11,7 @@
 interface InstalledAppProvider {
   // Filters |related_apps|, keeping only those which are both installed on the
   // user's system, and related to the web origin of the requesting page.
+  // Also appends the app version to the filtered apps.
   FilterInstalledApps(array<RelatedApplication> related_apps)
       => (array<RelatedApplication> installedApps);
 };
diff --git a/third_party/blink/public/mojom/installedapp/related_application.mojom b/third_party/blink/public/mojom/installedapp/related_application.mojom
index be3d3903..b4b0270 100644
--- a/third_party/blink/public/mojom/installedapp/related_application.mojom
+++ b/third_party/blink/public/mojom/installedapp/related_application.mojom
@@ -13,4 +13,5 @@
   // WebRelatedApplication as well).
   string? url;
   string? id;
+  string? version;
 };
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 98c9dcdf..ce069ce 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2437,6 +2437,10 @@
   kUserTimingL3 = 3053,
   kGetGamepadsFromCrossOriginSubframe = 3054,
   kGetGamepadsFromInsecureContext = 3055,
+  kOriginCleanImageBitmapSerialization = 3056,
+  kNonOriginCleanImageBitmapSerialization = 3057,
+  kOriginCleanImageBitmapTransfer = 3058,
+  kNonOriginCleanImageBitmapTransfer = 3059,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h b/third_party/blink/public/web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
similarity index 74%
rename from content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
rename to third_party/blink/public/web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
index 46e5cca..1865224 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
+++ b/third_party/blink/public/web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_FAKE_RTC_RTP_TRANSCEIVER_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_FAKE_RTC_RTP_TRANSCEIVER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_FAKE_RTC_RTP_TRANSCEIVER_IMPL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_FAKE_RTC_RTP_TRANSCEIVER_IMPL_H_
 
 #include <memory>
 #include <string>
@@ -19,22 +19,22 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_source.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 
-namespace content {
+namespace blink {
 
-// TODO(https://crbug.com/868868): Similar methods to this exist in many content
+// TODO(https://crbug.com/868868): Similar methods to this exist in many blink
 // unittests. Move to a separate file and reuse it in all of them.
 blink::WebMediaStreamTrack CreateWebMediaStreamTrack(
     const std::string& id,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
-class FakeRTCRtpSender : public blink::WebRTCRtpSender {
+class FakeRTCRtpSenderImpl : public blink::WebRTCRtpSender {
  public:
-  FakeRTCRtpSender(base::Optional<std::string> track_id,
-                   std::vector<std::string> stream_ids,
-                   scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-  FakeRTCRtpSender(const FakeRTCRtpSender&);
-  ~FakeRTCRtpSender() override;
-  FakeRTCRtpSender& operator=(const FakeRTCRtpSender&);
+  FakeRTCRtpSenderImpl(base::Optional<std::string> track_id,
+                       std::vector<std::string> stream_ids,
+                       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  FakeRTCRtpSenderImpl(const FakeRTCRtpSenderImpl&);
+  ~FakeRTCRtpSenderImpl() override;
+  FakeRTCRtpSenderImpl& operator=(const FakeRTCRtpSenderImpl&);
 
   std::unique_ptr<blink::WebRTCRtpSender> ShallowCopy() const override;
   uintptr_t Id() const override;
@@ -61,14 +61,15 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
 
-class FakeRTCRtpReceiver : public blink::WebRTCRtpReceiver {
+class FakeRTCRtpReceiverImpl : public blink::WebRTCRtpReceiver {
  public:
-  FakeRTCRtpReceiver(const std::string& track_id,
-                     std::vector<std::string> stream_ids,
-                     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-  FakeRTCRtpReceiver(const FakeRTCRtpReceiver&);
-  ~FakeRTCRtpReceiver() override;
-  FakeRTCRtpReceiver& operator=(const FakeRTCRtpReceiver&);
+  FakeRTCRtpReceiverImpl(
+      const std::string& track_id,
+      std::vector<std::string> stream_ids,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  FakeRTCRtpReceiverImpl(const FakeRTCRtpReceiverImpl&);
+  ~FakeRTCRtpReceiverImpl() override;
+  FakeRTCRtpReceiverImpl& operator=(const FakeRTCRtpReceiverImpl&);
 
   std::unique_ptr<blink::WebRTCRtpReceiver> ShallowCopy() const override;
   uintptr_t Id() const override;
@@ -89,16 +90,16 @@
   std::vector<std::string> stream_ids_;
 };
 
-class FakeRTCRtpTransceiver : public blink::WebRTCRtpTransceiver {
+class FakeRTCRtpTransceiverImpl : public blink::WebRTCRtpTransceiver {
  public:
-  FakeRTCRtpTransceiver(
+  FakeRTCRtpTransceiverImpl(
       base::Optional<std::string> mid,
-      FakeRTCRtpSender sender,
-      FakeRTCRtpReceiver receiver,
+      FakeRTCRtpSenderImpl sender,
+      FakeRTCRtpReceiverImpl receiver,
       bool stopped,
       webrtc::RtpTransceiverDirection direction,
       base::Optional<webrtc::RtpTransceiverDirection> current_direction);
-  ~FakeRTCRtpTransceiver() override;
+  ~FakeRTCRtpTransceiverImpl() override;
 
   blink::WebRTCRtpTransceiverImplementationType ImplementationType()
       const override;
@@ -116,13 +117,13 @@
 
  private:
   base::Optional<std::string> mid_;
-  FakeRTCRtpSender sender_;
-  FakeRTCRtpReceiver receiver_;
+  FakeRTCRtpSenderImpl sender_;
+  FakeRTCRtpReceiverImpl receiver_;
   bool stopped_;
   webrtc::RtpTransceiverDirection direction_;
   base::Optional<webrtc::RtpTransceiverDirection> current_direction_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_FAKE_RTC_RTP_TRANSCEIVER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_FAKE_RTC_RTP_TRANSCEIVER_IMPL_H_
diff --git a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h b/third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h
similarity index 91%
rename from content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
rename to third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h
index 1a9c6445..f6fdf049 100644
--- a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
+++ b/third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
 
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
@@ -18,7 +17,7 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 
-namespace content {
+namespace blink {
 
 class MockWebRTCPeerConnectionHandlerClient
     : public blink::WebRTCPeerConnectionHandlerClient {
@@ -87,7 +86,7 @@
   const base::Optional<uint16_t>& candidate_mlineindex() const {
     return candidate_mline_index_;
   }
-  const std::string& candidate_mid() const { return candidate_mid_ ; }
+  const std::string& candidate_mid() const { return candidate_mid_; }
   const blink::WebString& remote_stream_id() const { return remote_stream_id_; }
 
  private:
@@ -99,6 +98,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockWebRTCPeerConnectionHandlerClient);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_WEB_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h
similarity index 87%
rename from content/renderer/media/webrtc/rtc_rtp_sender.h
rename to third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h
index e47e339..dc3c784 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.h
+++ b/third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_SENDER_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_SENDER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_SENDER_IMPL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_SENDER_IMPL_H_
 
 #include <memory>
 #include <string>
@@ -11,7 +11,7 @@
 
 #include "base/callback.h"
 #include "base/single_thread_task_runner.h"
-#include "content/common/content_export.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
@@ -21,7 +21,7 @@
 #include "third_party/webrtc/api/rtp_sender_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
-namespace content {
+namespace blink {
 
 // This class represents the state of a sender; a snapshot of what a
 // webrtc-layer sender looked like when it was inspected on the signaling thread
@@ -54,10 +54,12 @@
 // Except for initialization logic and operator=(), the RtpSenderState is
 // immutable and only accessible on the main thread.
 //
-// TODO(hbos): [Onion Soup] When the sender implementation is moved to blink
-// this will be part of the blink sender instead of the content sender.
-// https://crbug.com/787254
-class CONTENT_EXPORT RtpSenderState {
+// TODO(crbug.com/787254): Move the classes below out of the Blink exposed API.
+// Also, consider merging RTCRtpSenderImpl and RTCRtpReceiver, and removing
+// WebRTCRtpSender when all its clients are Onion soup'ed.
+//
+// Last, move away from using std::vector.
+class BLINK_MODULES_EXPORT RtpSenderState {
  public:
   RtpSenderState(
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
@@ -105,7 +107,7 @@
 };
 
 // Used to surface |webrtc::RtpSenderInterface| to blink. Multiple
-// |RTCRtpSender|s could reference the same webrtc sender; |id| is the value
+// |RTCRtpSenderImpl|s could reference the same webrtc sender; |id| is the value
 // of the pointer to the webrtc sender.
 // TODO(hbos): [Onion Soup] Move all of the implementation inside the blink
 // object and remove this class and interface. The blink object is reference
@@ -113,17 +115,17 @@
 // all the blink object will need is the RtpSenderState. Requires coordination
 // with transceivers and receivers since these are tightly coupled.
 // https://crbug.com/787254
-class CONTENT_EXPORT RTCRtpSender : public blink::WebRTCRtpSender {
+class BLINK_MODULES_EXPORT RTCRtpSenderImpl : public blink::WebRTCRtpSender {
  public:
   static uintptr_t getId(const webrtc::RtpSenderInterface* webrtc_sender);
 
-  RTCRtpSender(
+  RTCRtpSenderImpl(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
       RtpSenderState state);
-  RTCRtpSender(const RTCRtpSender& other);
-  ~RTCRtpSender() override;
-  RTCRtpSender& operator=(const RTCRtpSender& other);
+  RTCRtpSenderImpl(const RTCRtpSenderImpl& other);
+  ~RTCRtpSenderImpl() override;
+  RTCRtpSenderImpl& operator=(const RTCRtpSenderImpl& other);
 
   const RtpSenderState& state() const;
   void set_state(RtpSenderState state);
@@ -163,7 +165,7 @@
   scoped_refptr<RTCRtpSenderInternal> internal_;
 };
 
-class CONTENT_EXPORT RTCRtpSenderOnlyTransceiver
+class BLINK_MODULES_EXPORT RTCRtpSenderOnlyTransceiver
     : public blink::WebRTCRtpTransceiver {
  public:
   explicit RTCRtpSenderOnlyTransceiver(
@@ -190,6 +192,6 @@
   std::unique_ptr<blink::WebRTCRtpSender> sender_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_SENDER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_SENDER_IMPL_H_
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver.h b/third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h
similarity index 80%
rename from content/renderer/media/webrtc/rtc_rtp_transceiver.h
rename to third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h
index ad4c873..57a9511 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver.h
+++ b/third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_TRANSCEIVER_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_TRANSCEIVER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_IMPL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_IMPL_H_
 
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
-#include "content/renderer/media/webrtc/rtc_rtp_sender.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_receiver_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
-namespace content {
+namespace blink {
 
 // This class represents the state of a transceiver; a snapshot of what a
 // webrtc-layer transceiver looked like when it was inspected on the signaling
@@ -49,16 +50,19 @@
 // Except for initialization logic and operator=(), the RtpTransceiverState is
 // immutable and only accessible on the main thread.
 //
-// TODO(hbos): [Onion Soup] When the transceiver implementation is moved to
-// blink this will be part of the blink transceiver instead of the content
-// transceiver. https://crbug.com/787254
-class CONTENT_EXPORT RtpTransceiverState {
+// TODO(crbug.com/787254): Move the classes below out of the Blink exposed API.
+// Also, consider merging RTCRtpTransceiverImpl and RTCRtpTransceiver
+// (requires coordination with senders and receivers) and
+// removing WebRTCRtpTransceiver when all its clients are Onion soup'ed.
+//
+// Last, move away from using std::vector.
+class BLINK_MODULES_EXPORT RtpTransceiverState {
  public:
   RtpTransceiverState(
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver,
-      base::Optional<RtpSenderState> sender_state,
+      base::Optional<blink::RtpSenderState> sender_state,
       base::Optional<blink::RtpReceiverState> receiver_state,
       base::Optional<std::string> mid,
       bool stopped,
@@ -81,8 +85,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() const;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const;
   scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver() const;
-  const base::Optional<RtpSenderState>& sender_state() const;
-  RtpSenderState MoveSenderState();
+  const base::Optional<blink::RtpSenderState>& sender_state() const;
+  blink::RtpSenderState MoveSenderState();
   const base::Optional<blink::RtpReceiverState>& receiver_state() const;
   blink::RtpReceiverState MoveReceiverState();
   base::Optional<std::string> mid() const;
@@ -97,7 +101,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver_;
   bool is_initialized_;
-  base::Optional<RtpSenderState> sender_state_;
+  base::Optional<blink::RtpSenderState> sender_state_;
   base::Optional<blink::RtpReceiverState> receiver_state_;
   base::Optional<std::string> mid_;
   bool stopped_;
@@ -106,13 +110,13 @@
   base::Optional<webrtc::RtpTransceiverDirection> fired_direction_;
 };
 
-// RTCRtpTransceiver::set_state() performs differently depending on the update
-// mode. The update mode exists to get around problems with the webrtc threading
-// model: https://crbug.com/webrtc/8692.
+// RTCRtpTransceiverImpl::set_state() performs differently depending on the
+// update mode. The update mode exists to get around problems with the webrtc
+// threading model: https://crbug.com/webrtc/8692.
 //
 // Transceiver state information can be surfaced as a result of invoking a
 // number of different JavaScript APIs. The way states are surfaced from webrtc
-// to content/blink fall into two categories:
+// to blink fall into two categories:
 //   Blocking operations and callback-based operations.
 //
 // When a blocking operation is invoked, the main thread is blocked on the
@@ -124,7 +128,7 @@
 // addTrack() - doesn't happen in-between the posting of the task and the
 // execution of it. In such cases, the state information surfaced might not be
 // up-to-date (in edge cases). Examples of callback-based operations include
-// setLocalDescription() and setRemoteDescription().
+// setLocalDescripti.on() and setRemoteDescription().
 enum class TransceiverStateUpdateMode {
   // In this mode, all state information is updated. Use this enum unless
   // a different update mode applies.
@@ -137,32 +141,30 @@
 };
 
 // Used to surface |webrtc::RtpTransceiverInterface| to blink. Multiple
-// |RTCRtpTransceiver|s could reference the same webrtc transceiver; |id| is
+// |RTCRtpTransceiverImpl|s could reference the same webrtc transceiver; |id| is
 // unique per webrtc transceiver.
 // Its methods are accessed on the main thread, internally also performs
 // operations on the signaling thread.
-// TODO(hbos): [Onion Soup] Remove the content layer versions of this class and
-// rely on webrtc directly from blink. Requires coordination with senders and
-// receivers. https://crbug.com/787254
-class CONTENT_EXPORT RTCRtpTransceiver : public blink::WebRTCRtpTransceiver {
+class BLINK_MODULES_EXPORT RTCRtpTransceiverImpl
+    : public blink::WebRTCRtpTransceiver {
  public:
   static uintptr_t GetId(
       const webrtc::RtpTransceiverInterface* webrtc_transceiver);
 
-  RTCRtpTransceiver(
+  RTCRtpTransceiverImpl(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
       RtpTransceiverState state);
-  RTCRtpTransceiver(const RTCRtpTransceiver& other);
-  ~RTCRtpTransceiver() override;
+  RTCRtpTransceiverImpl(const RTCRtpTransceiverImpl& other);
+  ~RTCRtpTransceiverImpl() override;
 
-  RTCRtpTransceiver& operator=(const RTCRtpTransceiver& other);
-  std::unique_ptr<RTCRtpTransceiver> ShallowCopy() const;
+  RTCRtpTransceiverImpl& operator=(const RTCRtpTransceiverImpl& other);
+  std::unique_ptr<RTCRtpTransceiverImpl> ShallowCopy() const;
 
   const RtpTransceiverState& state() const;
   void set_state(RtpTransceiverState state,
                  TransceiverStateUpdateMode update_mode);
-  RTCRtpSender* content_sender();
+  blink::RTCRtpSenderImpl* content_sender();
   blink::RTCRtpReceiverImpl* content_receiver();
 
   blink::WebRTCRtpTransceiverImplementationType ImplementationType()
@@ -188,6 +190,6 @@
   scoped_refptr<RTCRtpTransceiverInternal> internal_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_TRANSCEIVER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_IMPL_H_
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.h b/third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h
similarity index 83%
rename from content/renderer/media/webrtc/transceiver_state_surfacer.h
rename to third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h
index 61eaaa8c..1668faa 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.h
+++ b/third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h
@@ -2,18 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_TRANSCEIVER_STATE_SURFACER_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_TRANSCEIVER_STATE_SURFACER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_TRANSCEIVER_STATE_SURFACER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_TRANSCEIVER_STATE_SURFACER_H_
 
-#include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
+#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
 #include "third_party/webrtc/api/sctp_transport_interface.h"
 #include "third_party/webrtc/rtc_base/ref_count.h"
 #include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
-namespace content {
+namespace blink {
 
 // Takes care of creating and initializing transceiver states (including sender
 // and receiver states). This is usable for both blocking and non-blocking calls
@@ -22,7 +23,10 @@
 // The surfacer is initialized on the signaling thread and states are obtained
 // on the main thread. It is designed to be initialized and handed off; it is
 // not thread safe for concurrent thread usage.
-class CONTENT_EXPORT TransceiverStateSurfacer {
+//
+// TODO(crbug.com/787254): Move this class out of the Blink API when all its
+// client get moved to Blink. Also move it away from using std::vector.
+class BLINK_MODULES_EXPORT TransceiverStateSurfacer {
  public:
   TransceiverStateSurfacer(
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
@@ -48,7 +52,7 @@
 
   // Must be invoked on the main thread.
   blink::WebRTCSctpTransportSnapshot SctpTransportSnapshot();
-  std::vector<RtpTransceiverState> ObtainStates();
+  std::vector<blink::RtpTransceiverState> ObtainStates();
 
  protected:
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
@@ -56,13 +60,13 @@
   bool is_initialized_;
   bool states_obtained_;
   blink::WebRTCSctpTransportSnapshot sctp_transport_snapshot_;
-  std::vector<RtpTransceiverState> transceiver_states_;
+  std::vector<blink::RtpTransceiverState> transceiver_states_;
 };
 
 // A dummy implementation of a transceiver used to surface sender state
 // information only. It is not thread safe, only designed to be passed on to
 // TransceiverStateSurfacer::Initialize().
-class CONTENT_EXPORT SurfaceSenderStateOnly
+class BLINK_MODULES_EXPORT SurfaceSenderStateOnly
     : public rtc::RefCountedObject<webrtc::RtpTransceiverInterface> {
  public:
   SurfaceSenderStateOnly(rtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
@@ -86,7 +90,7 @@
 // A dummy implementation of a transceiver used to surface receiver state
 // information only. It is not thread safe, only designed to be passed on to
 // TransceiverStateSurfacer::Initialize().
-class CONTENT_EXPORT SurfaceReceiverStateOnly
+class BLINK_MODULES_EXPORT SurfaceReceiverStateOnly
     : public rtc::RefCountedObject<webrtc::RtpTransceiverInterface> {
  public:
   SurfaceReceiverStateOnly(
@@ -108,6 +112,6 @@
   rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_TRANSCEIVER_STATE_SURFACER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_TRANSCEIVER_STATE_SURFACER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc
index 8b8ff3a6..835b86b 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc
@@ -121,6 +121,7 @@
 void ScriptPromiseResolver::Trace(blink::Visitor* visitor) {
   visitor->Trace(script_state_);
   visitor->Trace(resolver_);
+  visitor->Trace(value_);
   ContextLifecycleObserver::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h
index a83957e..929535af 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h
@@ -148,7 +148,7 @@
   const Member<ScriptState> script_state_;
   TaskHandle deferred_resolve_task_;
   Resolver resolver_;
-  ScopedPersistent<v8::Value> value_;
+  TraceWrapperV8Reference<v8::Value> value_;
 
   // To support keepAliveWhilePending(), this object needs to keep itself
   // alive while in that state.
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
index 58c8eb2..52d1f2a 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
@@ -74,8 +74,7 @@
   using ArrayBufferContentsArray = Vector<WTF::ArrayBufferContents, 1>;
   using SharedArrayBufferContentsArray = Vector<WTF::ArrayBufferContents, 1>;
   using ImageBitmapContentsArray = Vector<scoped_refptr<StaticBitmapImage>, 1>;
-  using TransferredWasmModulesArray =
-      WTF::Vector<v8::WasmModuleObject::TransferrableModule>;
+  using TransferredWasmModulesArray = WTF::Vector<v8::CompiledWasmModule>;
   using MessagePortChannelArray = Vector<MessagePortChannel>;
 
   // Increment this for each incompatible change to the wire format.
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index 240e1b3..1b6b02bf3 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -693,7 +693,7 @@
 V8ScriptValueDeserializer::GetWasmModuleFromId(v8::Isolate* isolate,
                                                uint32_t id) {
   if (id < serialized_script_value_->WasmModules().size()) {
-    return v8::WasmModuleObject::FromTransferrableModule(
+    return v8::WasmModuleObject::FromCompiledModule(
         isolate, serialized_script_value_->WasmModules()[id]);
   }
   CHECK(serialized_script_value_->WasmModules().IsEmpty());
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
index 3bf48003..abb7627 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h"
 
 #include "base/auto_reset.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
@@ -28,6 +29,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_transform_stream.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix_read_only.h"
 #include "third_party/blink/renderer/core/geometry/dom_point.h"
@@ -42,6 +44,7 @@
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_base.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
@@ -258,11 +261,20 @@
       return false;
     }
 
+    auto* execution_context = ExecutionContext::From(script_state_.Get());
     // If this ImageBitmap was transferred, it can be serialized by index.
     size_t index = kNotFound;
     if (transferables_)
       index = transferables_->image_bitmaps.Find(image_bitmap);
     if (index != kNotFound) {
+      if (image_bitmap->OriginClean()) {
+        execution_context->CountUse(
+            mojom::WebFeature::kOriginCleanImageBitmapTransfer);
+      } else {
+        execution_context->CountUse(
+            mojom::WebFeature::kNonOriginCleanImageBitmapTransfer);
+      }
+
       DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
       WriteTag(kImageBitmapTransferTag);
       WriteUint32(static_cast<uint32_t>(index));
@@ -270,6 +282,13 @@
     }
 
     // Otherwise, it must be fully serialized.
+    if (image_bitmap->OriginClean()) {
+      execution_context->CountUse(
+          mojom::WebFeature::kOriginCleanImageBitmapSerialization);
+    } else {
+      execution_context->CountUse(
+          mojom::WebFeature::kNonOriginCleanImageBitmapSerialization);
+    }
     WriteTag(kImageBitmapTag);
     SerializedColorParams color_params(image_bitmap->GetCanvasColorParams());
     WriteUint32Enum(ImageSerializationTag::kCanvasColorSpaceTag);
@@ -735,7 +754,7 @@
       // around. Most likely, we'll have one module. The vector approach is
       // simple and should perform sufficiently well under these expectations.
       serialized_script_value_->WasmModules().push_back(
-          module->GetTransferrableModule());
+          module->GetCompiledModule());
       uint32_t size =
           static_cast<uint32_t>(serialized_script_value_->WasmModules().size());
       DCHECK_GE(size, 1u);
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.cc b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.cc
index fe042695..00a872a9 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.cc
@@ -106,19 +106,15 @@
     : V0CustomElementLifecycleCallbacks(
           FlagSet(attached, detached, attribute_changed)),
       script_state_(script_state),
-      prototype_(script_state->GetIsolate(), prototype),
-      created_(script_state->GetIsolate(), created),
-      attached_(script_state->GetIsolate(), attached),
-      detached_(script_state->GetIsolate(), detached),
-      attribute_changed_(script_state->GetIsolate(), attribute_changed) {
-  prototype_.SetPhantom();
+      prototype_(script_state->GetIsolate(), prototype){
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::Local<v8::Function> function;
+#define SET_FIELD(maybe, ignored)    \
+  if (maybe.ToLocal(&function))      \
+    maybe##_.Set(isolate, function);
 
-#define MAKE_WEAK(Var, Ignored) \
-  if (!Var##_.IsEmpty())        \
-    Var##_.SetPhantom();
-
-  CALLBACK_LIST(MAKE_WEAK)
-#undef MAKE_WEAK
+  CALLBACK_LIST(SET_FIELD)
+#undef SET_FIELD
 }
 
 V8PerContextData* V8V0CustomElementLifecycleCallbacks::CreationContextData() {
@@ -228,7 +224,7 @@
 }
 
 void V8V0CustomElementLifecycleCallbacks::Call(
-    const ScopedPersistent<v8::Function>& weak_callback,
+    const TraceWrapperV8Reference<v8::Function>& callback_reference,
     Element* element) {
   // FIXME: callbacks while paused should be queued up for execution to
   // continue then be delivered in order rather than delivered immediately.
@@ -238,7 +234,7 @@
   ScriptState::Scope scope(script_state_);
   v8::Isolate* isolate = script_state_->GetIsolate();
   v8::Local<v8::Context> context = script_state_->GetContext();
-  v8::Local<v8::Function> callback = weak_callback.NewLocal(isolate);
+  v8::Local<v8::Function> callback = callback_reference.NewLocal(isolate);
   if (callback.IsEmpty())
     return;
 
@@ -254,6 +250,11 @@
 
 void V8V0CustomElementLifecycleCallbacks::Trace(blink::Visitor* visitor) {
   visitor->Trace(script_state_);
+  visitor->Trace(prototype_);
+  visitor->Trace(created_);
+  visitor->Trace(attached_);
+  visitor->Trace(detached_);
+  visitor->Trace(attribute_changed_);
   V0CustomElementLifecycleCallbacks::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h
index c37f788..f295bfb 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h
@@ -35,8 +35,8 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/html/custom/v0_custom_element_lifecycle_callbacks.h"
-#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -79,16 +79,17 @@
                         const AtomicString& old_value,
                         const AtomicString& new_value) override;
 
-  void Call(const ScopedPersistent<v8::Function>& weak_callback, Element*);
+  void Call(const TraceWrapperV8Reference<v8::Function>& callback_reference,
+            Element*);
 
   V8PerContextData* CreationContextData();
 
   Member<ScriptState> script_state_;
-  ScopedPersistent<v8::Object> prototype_;
-  ScopedPersistent<v8::Function> created_;
-  ScopedPersistent<v8::Function> attached_;
-  ScopedPersistent<v8::Function> detached_;
-  ScopedPersistent<v8::Function> attribute_changed_;
+  TraceWrapperV8Reference<v8::Object> prototype_;
+  TraceWrapperV8Reference<v8::Function> created_;
+  TraceWrapperV8Reference<v8::Function> attached_;
+  TraceWrapperV8Reference<v8::Function> detached_;
+  TraceWrapperV8Reference<v8::Function> attribute_changed_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index 5a64e36..099c6f1 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -1542,6 +1542,8 @@
     if (pending()) {
       // TODO(crbug.com/916117): Rejecting the ready promise should be performed
       // inside reset pending tasks once aligned with the spec.
+      // TODO(crbug.com/1013351): Add test for rejection and reset of cancel
+      // promise. Requires further cleanup of PlayStateUpdateScope.
       if (ready_promise_)
         RejectAndResetPromiseMaybeAsync(ready_promise_.Get());
     }
@@ -1692,8 +1694,6 @@
       animation_->ResetPendingTasks();
       animation_->ResolvePromiseMaybeAsync(animation_->ready_promise_.Get());
     } else if (new_play_state == kPending) {
-      DCHECK_NE(animation_->ready_promise_->GetState(),
-                AnimationPromise::kPending);
       animation_->ready_promise_->Reset();
     }
   }
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 9cb7bec1..c2180f4 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -187,6 +187,7 @@
                     "frame/deprecation_report_body.idl",
                     "frame/external.idl",
                     "frame/feature_policy_violation_report_body.idl",
+                    "frame/fragment_directive.idl",
                     "frame/history.idl",
                     "frame/intervention_report_body.idl",
                     "frame/location.idl",
@@ -194,7 +195,6 @@
                     "frame/report_body.idl",
                     "frame/reporting_observer.idl",
                     "frame/scheduling.idl",
-                    "frame/selector.idl",
                     "frame/test_report_body.idl",
                     "frame/user_activation.idl",
                     "frame/visual_viewport.idl",
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 1f89e1d4..0e9a706 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -11,6 +11,7 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/request_mode.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
diff --git a/third_party/blink/renderer/core/fetch/request.cc b/third_party/blink/renderer/core/fetch/request.cc
index 08f6f8be..7b1106a8 100644
--- a/third_party/blink/renderer/core/fetch/request.cc
+++ b/third_party/blink/renderer/core/fetch/request.cc
@@ -7,6 +7,7 @@
 #include "services/network/public/cpp/request_mode.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
 #include "third_party/blink/public/common/loader/request_destination.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index 54db2cf..e901abf 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -57,6 +57,7 @@
     "feature_policy_violation_report_body.h",
     "find_in_page.cc",
     "find_in_page.h",
+    "fragment_directive.h",
     "frame.cc",
     "frame.h",
     "frame_client.h",
@@ -168,7 +169,6 @@
     "screen.h",
     "screen_orientation_controller.cc",
     "screen_orientation_controller.h",
-    "selector.h",
     "settings.cc",
     "settings.h",
     "settings_delegate.cc",
diff --git a/third_party/blink/renderer/core/frame/fragment_directive.h b/third_party/blink/renderer/core/frame/fragment_directive.h
new file mode 100644
index 0000000..dd3a604
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/fragment_directive.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAGMENT_DIRECTIVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAGMENT_DIRECTIVE_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+// TODO(crbug/1000308): Implement the FragmentDirective type. This member
+// currently serves as a feature detectable API for the Text Fragment
+// Identifiers feature.
+class FragmentDirective : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAGMENT_DIRECTIVE_H_
diff --git a/third_party/blink/renderer/core/frame/selector.idl b/third_party/blink/renderer/core/frame/fragment_directive.idl
similarity index 89%
rename from third_party/blink/renderer/core/frame/selector.idl
rename to third_party/blink/renderer/core/frame/fragment_directive.idl
index 13eadaf..3f91db9 100644
--- a/third_party/blink/renderer/core/frame/selector.idl
+++ b/third_party/blink/renderer/core/frame/fragment_directive.idl
@@ -4,5 +4,5 @@
 
 // https://github.com/WICG/ScrollToTextFragment
 [RuntimeEnabled=TextFragmentIdentifiers]
-interface Selector {
+interface FragmentDirective {
 };
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
index 82a0b0c..3a4dcf3 100644
--- a/third_party/blink/renderer/core/frame/frame.cc
+++ b/third_party/blink/renderer/core/frame/frame.cc
@@ -261,6 +261,27 @@
   }
 }
 
+void Frame::UpdateVisibleToHitTesting() {
+  bool parent_visible_to_hit_testing = true;
+  if (auto* parent = Tree().Parent())
+    parent_visible_to_hit_testing = parent->GetVisibleToHitTesting();
+
+  bool self_visible_to_hit_testing = true;
+  if (auto* local_owner = DynamicTo<HTMLFrameOwnerElement>(owner_.Get())) {
+    self_visible_to_hit_testing =
+        local_owner->GetLayoutObject()
+            ? local_owner->GetLayoutObject()->Style()->VisibleToHitTesting()
+            : true;
+  }
+
+  bool visible_to_hit_testing =
+      parent_visible_to_hit_testing && self_visible_to_hit_testing;
+  bool changed = visible_to_hit_testing_ != visible_to_hit_testing;
+  visible_to_hit_testing_ = visible_to_hit_testing;
+  if (changed)
+    DidChangeVisibleToHitTesting();
+}
+
 const std::string& Frame::ToTraceValue() {
   // token's ToString() is latin1.
   if (!trace_value_)
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index b0f4e14..6437f572 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -234,6 +234,9 @@
     return *window_agent_factory_;
   }
 
+  bool GetVisibleToHitTesting() const { return visible_to_hit_testing_; }
+  void UpdateVisibleToHitTesting();
+
  protected:
   // |inheriting_agent_factory| should basically be set to the parent frame or
   // opener's WindowAgentFactory. Pass nullptr if the frame is isolated from
@@ -260,6 +263,8 @@
     return lifecycle_.GetState() == FrameLifecycle::kDetached;
   }
 
+  virtual void DidChangeVisibleToHitTesting() = 0;
+
   mutable FrameTree tree_node_;
 
   Member<Page> page_;
@@ -279,6 +284,8 @@
 
   TouchAction inherited_effective_touch_action_ = TouchAction::kTouchActionAuto;
 
+  bool visible_to_hit_testing_ = true;
+
  private:
   Member<FrameClient> client_;
   const Member<WindowProxyManager> window_proxy_manager_;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 4fb727d..ca2d302 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1787,4 +1787,13 @@
   Client()->EvictFromBackForwardCache();
 }
 
+void LocalFrame::DidChangeVisibleToHitTesting() {
+  // LayoutEmbeddedContent does not propagate style updates to descendants.
+  // Need to update the field manually.
+  for (Frame* child = Tree().FirstChild(); child;
+       child = child->Tree().NextSibling()) {
+    child->UpdateVisibleToHitTesting();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 312d11b..0e916bd 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -447,6 +447,8 @@
   void SetIsCapturingMediaCallback(IsCapturingMediaCallback callback);
   bool IsCapturingMedia() const;
 
+  void DidChangeVisibleToHitTesting() override;
+
  private:
   friend class FrameNavigationDisabler;
 
diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc
index dcd0a33..3acc96e 100644
--- a/third_party/blink/renderer/core/frame/location.cc
+++ b/third_party/blink/renderer/core/frame/location.cc
@@ -33,8 +33,8 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/dom_window.h"
+#include "third_party/blink/renderer/core/frame/fragment_directive.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/frame/selector.h"
 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
@@ -47,11 +47,12 @@
 namespace blink {
 
 Location::Location(DOMWindow* dom_window)
-    : dom_window_(dom_window), selector_(MakeGarbageCollected<Selector>()) {}
+    : dom_window_(dom_window),
+      fragment_directive_(MakeGarbageCollected<FragmentDirective>()) {}
 
 void Location::Trace(blink::Visitor* visitor) {
   visitor->Trace(dom_window_);
-  visitor->Trace(selector_);
+  visitor->Trace(fragment_directive_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -98,8 +99,8 @@
   return DOMURLUtilsReadOnly::origin(Url());
 }
 
-Selector* Location::selector() const {
-  return selector_;
+FragmentDirective* Location::fragmentDirective() const {
+  return fragment_directive_;
 }
 
 DOMStringList* Location::ancestorOrigins() const {
diff --git a/third_party/blink/renderer/core/frame/location.h b/third_party/blink/renderer/core/frame/location.h
index e042d15c..12b6870b 100644
--- a/third_party/blink/renderer/core/frame/location.h
+++ b/third_party/blink/renderer/core/frame/location.h
@@ -33,7 +33,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_string_list.h"
 #include "third_party/blink/renderer/core/frame/dom_window.h"
-#include "third_party/blink/renderer/core/frame/selector.h"
+#include "third_party/blink/renderer/core/frame/fragment_directive.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -83,7 +83,7 @@
 
   DOMStringList* ancestorOrigins() const;
 
-  Selector* selector() const;
+  FragmentDirective* fragmentDirective() const;
 
   // Just return the |this| object the way the normal valueOf function on the
   // Object prototype would.  The valueOf function is only added to make sure
@@ -116,7 +116,7 @@
 
   const Member<DOMWindow> dom_window_;
 
-  Member<Selector> selector_;
+  Member<FragmentDirective> fragment_directive_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/location.idl b/third_party/blink/renderer/core/frame/location.idl
index da798f6..bfcda81 100644
--- a/third_party/blink/renderer/core/frame/location.idl
+++ b/third_party/blink/renderer/core/frame/location.idl
@@ -59,7 +59,7 @@
     [SetterCallWith=Isolate, RaisesException=Setter, Unforgeable] attribute USVString search;
     [SetterCallWith=Isolate, RaisesException=Setter, Unforgeable] attribute USVString hash;
 
-    [RuntimeEnabled=TextFragmentIdentifiers] readonly attribute Selector selector;
+    [RuntimeEnabled=TextFragmentIdentifiers] readonly attribute FragmentDirective fragmentDirective;
 
     // TODO(foolip): Location does not have a valueOf() override in the spec.
     // See the comment in Location.h for the purpose of this.
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
index 0ff6b8d..bf27a5e2 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
@@ -87,9 +87,11 @@
 
   Vector<v8::Local<v8::Value>> Execute(LocalFrame*) override;
 
+  void Trace(Visitor*) override;
+
  private:
-  ScopedPersistent<v8::Function> function_;
-  ScopedPersistent<v8::Value> receiver_;
+  TraceWrapperV8Reference<v8::Function> function_;
+  TraceWrapperV8Reference<v8::Value> receiver_;
   V8PersistentValueVector<v8::Value> args_;
 };
 
@@ -126,6 +128,12 @@
   return results;
 }
 
+void V8FunctionExecutor::Trace(Visitor* visitor) {
+  visitor->Trace(function_);
+  visitor->Trace(receiver_);
+  PausableScriptExecutor::Executor::Trace(visitor);
+}
+
 }  // namespace
 
 void PausableScriptExecutor::CreateAndRun(
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 3ed7f51..c8f00ab 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -46,6 +46,7 @@
   dom_window_ = MakeGarbageCollected<RemoteDOMWindow>(*this);
   UpdateInertIfPossible();
   UpdateInheritedEffectiveTouchActionIfPossible();
+  UpdateVisibleToHitTesting();
 
   Initialize();
 }
@@ -214,7 +215,7 @@
   return static_cast<RemoteFrameClient*>(Frame::Client());
 }
 
-void RemoteFrame::PointerEventsChanged() {
+void RemoteFrame::DidChangeVisibleToHitTesting() {
   if (!cc_layer_ || !is_surface_layer_)
     return;
 
@@ -226,9 +227,9 @@
   HTMLFrameOwnerElement* owner = DeprecatedLocalOwner();
   if (!owner || !owner->GetLayoutObject())
     return false;
+
   return owner->OwnerType() == FrameOwnerElementType::kPortal ||
-         (owner->GetLayoutObject()->Style()->PointerEvents() ==
-          EPointerEvents::kNone);
+         !visible_to_hit_testing_;
 }
 
 void RemoteFrame::SetCcLayer(cc::Layer* cc_layer,
@@ -243,7 +244,10 @@
   is_surface_layer_ = is_surface_layer;
   if (cc_layer_) {
     GraphicsLayer::RegisterContentsLayer(cc_layer_);
-    PointerEventsChanged();
+    if (is_surface_layer) {
+      static_cast<cc::SurfaceLayer*>(cc_layer_)->SetHasPointerEventsNone(
+          IsIgnoredForHitTest());
+    }
   }
 
   To<HTMLFrameOwnerElement>(Owner())->SetNeedsCompositingUpdate();
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index 45851ef8..300b422 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -64,9 +64,10 @@
 
   RemoteFrameClient* Client() const;
 
-  void PointerEventsChanged();
   bool IsIgnoredForHitTest() const;
 
+  void DidChangeVisibleToHitTesting() override;
+
  private:
   // Frame protected overrides:
   void DetachImpl(FrameDetachType) override;
diff --git a/third_party/blink/renderer/core/frame/selector.h b/third_party/blink/renderer/core/frame/selector.h
deleted file mode 100644
index f006f48..0000000
--- a/third_party/blink/renderer/core/frame/selector.h
+++ /dev/null
@@ -1,21 +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_CORE_FRAME_SELECTOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SELECTOR_H_
-
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-
-namespace blink {
-
-// TODO(crbug/1000308): Implement the Selector type. This member currently
-// serves as a feature detectable API for the Text Fragment Identifiers
-// feature.
-class Selector : public ScriptWrappable {
-  DEFINE_WRAPPERTYPEINFO();
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SELECTOR_H_
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 1ed1164..cf61cf2 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -308,11 +308,6 @@
   }
 }
 
-void HTMLFrameOwnerElement::PointerEventsChanged() {
-  if (auto* remote_frame = DynamicTo<RemoteFrame>(ContentFrame()))
-    remote_frame->PointerEventsChanged();
-}
-
 void HTMLFrameOwnerElement::FrameOwnerPropertiesChanged() {
   // Don't notify about updates if ContentFrame() is null, for example when
   // the subframe hasn't been created yet; or if we are in the middle of
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index 4201b02..3d73041 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -119,10 +119,6 @@
   // For unit tests, manually trigger the UpdateContainerPolicy method.
   void UpdateContainerPolicyForTests() { UpdateContainerPolicy(); }
 
-  // This function is to notify ChildFrameCompositor of pointer-events changes
-  // of an OOPIF.
-  void PointerEventsChanged();
-
   void CancelPendingLazyLoad();
 
   void ParseAttribute(const AttributeModificationParams&) override;
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index 557c3f4..523a69a 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/remote_frame.h"
 #include "third_party/blink/renderer/core/frame/remote_frame_view.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
 #include "third_party/blink/renderer/core/html/html_plugin_element.h"
@@ -254,20 +255,28 @@
                                            const ComputedStyle* old_style) {
   LayoutReplaced::StyleDidChange(diff, old_style);
 
-  if (!old_style || Style()->PointerEvents() != old_style->PointerEvents()) {
-    if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(GetNode()))
-      frame_owner->PointerEventsChanged();
+  if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
+    if (StyleRef().Visibility() != EVisibility::kVisible) {
+      embedded_content_view->Hide();
+    } else {
+      embedded_content_view->Show();
+    }
   }
 
-  EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
-  if (!embedded_content_view)
+  if (old_style &&
+      StyleRef().VisibleToHitTesting() == old_style->VisibleToHitTesting()) {
+    return;
+  }
+
+  auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(GetNode());
+  if (!frame_owner)
     return;
 
-  if (StyleRef().Visibility() != EVisibility::kVisible) {
-    embedded_content_view->Hide();
-  } else {
-    embedded_content_view->Show();
-  }
+  auto* frame = frame_owner->ContentFrame();
+  if (!frame)
+    return;
+
+  frame->UpdateVisibleToHitTesting();
 }
 
 void LayoutEmbeddedContent::UpdateLayout() {
diff --git a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
index cd27fbf5..3ac1c1f 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
+++ b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
@@ -236,6 +236,7 @@
 
 void CSSLayoutDefinition::Instance::Trace(blink::Visitor* visitor) {
   visitor->Trace(definition_);
+  visitor->Trace(instance_);
 }
 
 void CSSLayoutDefinition::Trace(Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
index c7223e9..b8690a9 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
+++ b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
@@ -68,7 +68,7 @@
     void ReportException(ExceptionState*);
 
     Member<CSSLayoutDefinition> definition_;
-    ScopedPersistent<v8::Value> instance_;
+    TraceWrapperV8Reference<v8::Value> instance_;
   };
 
   // Creates an instance of the web developer defined class. May return a
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 5cd12de..4f6223c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -186,12 +186,14 @@
   break_iterator_.SetBreakSpace(BreakSpaceType::kBeforeSpaceRun);
 
   if (break_token) {
-    current_style_ = break_token->Style();
     item_index_ = break_token->ItemIndex();
     offset_ = break_token->TextOffset();
     break_iterator_.SetStartOffset(offset_);
     is_after_forced_break_ = break_token->IsForcedBreak();
     items_data_.AssertOffset(item_index_, offset_);
+    // TODO(crbug.com/1013040): |break_token->Style()| should not be nullptr.
+    if (const ComputedStyle* line_initial_style = break_token->Style())
+      SetCurrentStyle(*line_initial_style);
   }
 }
 
@@ -287,11 +289,13 @@
     line_info->SetTextIndent(MinimumValueForLength(length, maximum_value));
   }
 
-  // Set the initial style of this line from the break token. Example:
+  // Set the initial style of this line from the line style, if the style from
+  // the end of previous line is not available. Example:
   //   <p>...<span>....</span></p>
   // When the line wraps in <span>, the 2nd line needs to start with the style
   // of the <span>.
-  SetCurrentStyle(current_style_ ? *current_style_ : line_info->LineStyle());
+  if (!current_style_)
+    SetCurrentStyle(line_info->LineStyle());
   ComputeBaseDirection();
   line_info->SetBaseDirection(base_direction_);
 
@@ -1826,6 +1830,23 @@
 }
 
 void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) {
+  if (&style == current_style_.get()) {
+#if DCHECK_IS_ON()
+    // Check that cache fields are already setup correctly.
+    DCHECK_EQ(auto_wrap_, style.AutoWrap());
+    if (auto_wrap_) {
+      DCHECK_EQ(enable_soft_hyphen_, style.GetHyphens() != Hyphens::kNone);
+      DCHECK_EQ(break_iterator_.Locale(), style.LocaleForLineBreakIterator());
+    }
+    ShapeResultSpacing<String> spacing(spacing_.Text());
+    spacing.SetSpacing(style.GetFontDescription());
+    DCHECK_EQ(spacing.LetterSpacing(), spacing_.LetterSpacing());
+    DCHECK_EQ(spacing.WordSpacing(), spacing_.WordSpacing());
+#endif
+    return;
+  }
+  current_style_ = &style;
+
   auto_wrap_ = style.AutoWrap();
 
   if (auto_wrap_) {
@@ -1861,17 +1882,10 @@
 
     if (style.WhiteSpace() == EWhiteSpace::kBreakSpaces)
       break_iterator_.SetBreakSpace(BreakSpaceType::kAfterEverySpace);
+
+    break_iterator_.SetLocale(style.LocaleForLineBreakIterator());
   }
 
-  // The above calls are cheap & necessary. But the following are expensive
-  // and do not need to be reset every time if the style doesn't change,
-  // so avoid them if possible.
-  if (&style == current_style_.get())
-    return;
-
-  current_style_ = &style;
-  if (auto_wrap_)
-    break_iterator_.SetLocale(style.LocaleForLineBreakIterator());
   spacing_.SetSpacing(style.GetFontDescription());
 }
 
@@ -1898,7 +1912,9 @@
 
 scoped_refptr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken(
     const NGLineInfo& line_info) const {
+  DCHECK(current_style_);
   const Vector<NGInlineItem>& items = Items();
+  DCHECK_LE(item_index_, items.size());
   if (item_index_ >= items.size())
     return NGInlineBreakToken::Create(node_);
   return NGInlineBreakToken::Create(
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
index 1cccec65..34eba29 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
@@ -306,6 +306,26 @@
   EXPECT_EQ("AAA BB CC", ToString(lines[1], node));
 }
 
+TEST_F(NGLineBreakerTest, WrapLetterSpacing) {
+  NGInlineNode node = CreateInlineNode(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    #container {
+      font: 10px/1 Times;
+      letter-spacing: 10px;
+      width: 0px;
+    }
+    </style>
+    <div id=container>Star Wars</div>
+  )HTML");
+
+  Vector<NGInlineItemResults> lines;
+  lines = BreakLines(node, LayoutUnit(100));
+  EXPECT_EQ(2u, lines.size());
+  EXPECT_EQ("Star", ToString(lines[0], node));
+  EXPECT_EQ("Wars", ToString(lines[1], node));
+}
+
 TEST_F(NGLineBreakerTest, BoundaryInWord) {
   LoadAhem();
   NGInlineNode node = CreateInlineNode(R"HTML(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 764cf34..07f5dcc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -441,6 +441,9 @@
     // business, but we store its appeal, so that we don't look for breakpoints
     // with lower appeal than that.
     container_builder_.SetBreakAppeal(ConstraintSpace().EarlyBreakAppeal());
+
+    if (ConstraintSpace().IsInitialColumnBalancingPass())
+      container_builder_.SetIsInitialColumnBalancingPass();
   }
   container_builder_.SetBfcLineOffset(
       ConstraintSpace().BfcOffset().line_offset);
@@ -2091,12 +2094,26 @@
     appeal_before = kBreakAppealLastResort;
   }
 
-  // We only care about soft breaks if we have a fragmentainer block-size.
-  // During column balancing this may be unknown.
-  if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
-    return kContinueWithoutBreaking;
-
   const auto& physical_fragment = layout_result.PhysicalFragment();
+  NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment);
+
+  if (!ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+    if (ConstraintSpace().IsInitialColumnBalancingPass()) {
+      if (child.IsMonolithic() ||
+          (child.IsBlock() &&
+           IsAvoidBreakValue(ConstraintSpace(), child.Style().BreakInside()))) {
+        // If this is the initial column balancing pass, attempt to make the
+        // column block-size at least as large as the tallest piece of
+        // monolithic content and/or block with break-inside:avoid.
+        container_builder_.PropagateTallestUnbreakableBlockSize(
+            fragment.BlockSize());
+      }
+    }
+    // We only care about soft breaks if we have a fragmentainer block-size.
+    // During column balancing this may be unknown.
+    return kContinueWithoutBreaking;
+  }
+
   if (IsA<NGBlockBreakToken>(physical_fragment.BreakToken())) {
     // The block child broke inside. We now need to decide whether to keep that
     // break, or if it would be better to break before it.
@@ -2116,8 +2133,6 @@
     if (child.IsMonolithic()) {
       // If the monolithic piece of content (e.g. a line, or block-level
       // replaced content) doesn't fit, we need a break.
-      NGFragment fragment(ConstraintSpace().GetWritingMode(),
-                          physical_fragment);
       want_break = fragment.BlockSize() > space_left;
     } else {
       // If the block-offset is past the fragmentainer boundary (or exactly at
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 50441fb..c245e90a6 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -189,10 +189,14 @@
     const auto* token = child_layout_result.PhysicalFragment().BreakToken();
     did_break_ = token && !token->IsFinished();
   }
-  if (child_layout_result.HasForcedBreak())
+  if (child_layout_result.HasForcedBreak()) {
     SetHasForcedBreak();
-  else
+  } else if (IsInitialColumnBalancingPass()) {
+    PropagateTallestUnbreakableBlockSize(
+        child_layout_result.TallestUnbreakableBlockSize());
+  } else {
     PropagateSpaceShortage(child_layout_result.MinimalSpaceShortage());
+  }
 }
 
 scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::ToBoxFragment(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 97cc52e2..1cff4292 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -124,11 +124,41 @@
   // used by the column balancer to stretch columns.
   void PropagateSpaceShortage(LayoutUnit space_shortage) {
     DCHECK_GT(space_shortage, LayoutUnit());
+
+    // Space shortage should only be reported when we already have a tentative
+    // fragmentainer block-size. It's meaningless to talk about space shortage
+    // in the initial column balancing pass, because then we have no
+    // fragmentainer block-size at all, so who's to tell what's too short or
+    // not?
+    DCHECK(!IsInitialColumnBalancingPass());
+
     if (minimal_space_shortage_ > space_shortage)
       minimal_space_shortage_ = space_shortage;
   }
   LayoutUnit MinimalSpaceShortage() const { return minimal_space_shortage_; }
 
+  void PropagateTallestUnbreakableBlockSize(LayoutUnit unbreakable_block_size) {
+    // We should only calculate the block-size of the tallest piece of
+    // unbreakable content during the initial column balancing pass, when we
+    // haven't set a tentative fragmentainer block-size yet.
+    DCHECK(IsInitialColumnBalancingPass());
+
+    tallest_unbreakable_block_size_ =
+        std::max(tallest_unbreakable_block_size_, unbreakable_block_size);
+  }
+
+  void SetIsInitialColumnBalancingPass() {
+    // Note that we have no dedicated flag for being in the initial column
+    // balancing pass here. We'll just bump tallest_unbreakable_block_size_ to
+    // 0, so that NGLayoutResult knows that we need to store unbreakable
+    // block-size.
+    DCHECK_EQ(tallest_unbreakable_block_size_, LayoutUnit::Min());
+    tallest_unbreakable_block_size_ = LayoutUnit();
+  }
+  bool IsInitialColumnBalancingPass() const {
+    return tallest_unbreakable_block_size_ >= LayoutUnit();
+  }
+
   void SetInitialBreakBefore(EBreakBetween break_before) {
     initial_break_before_ = break_before;
   }
@@ -280,6 +310,7 @@
   LayoutUnit consumed_block_size_;
 
   LayoutUnit minimal_space_shortage_ = LayoutUnit::Max();
+  LayoutUnit tallest_unbreakable_block_size_ = LayoutUnit::Min();
 
   // The break-before value on the initial child we cannot honor. There's no
   // valid class A break point before a first child, only *between* siblings.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 9ff6544..e751a83 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -629,6 +629,7 @@
   // First split into content runs at explicit (forced) breaks.
   ContentRuns content_runs;
   scoped_refptr<const NGBlockBreakToken> break_token = child_break_token;
+  LayoutUnit tallest_unbreakable_block_size;
   do {
     NGBlockLayoutAlgorithm balancing_algorithm(
         {Node(), fragment_geometry, space, break_token.get()});
@@ -639,6 +640,9 @@
         fragment, IsHorizontalWritingMode(space.GetWritingMode()));
     content_runs.emplace_back(column_block_size);
 
+    tallest_unbreakable_block_size = std::max(
+        tallest_unbreakable_block_size, result->TallestUnbreakableBlockSize());
+
     // Stop when we reach a spanner. That's where this row of columns will end.
     if (result->ColumnSpanner())
       break;
@@ -665,7 +669,9 @@
   // lay out into columns to figure out if they are tall enough or not (and
   // stretch and retry if not). Also honor {,min-,max-}{height,width} properties
   // before returning.
-  LayoutUnit block_size = content_runs.TallestColumnBlockSize();
+  LayoutUnit block_size = std::max(content_runs.TallestColumnBlockSize(),
+                                   tallest_unbreakable_block_size);
+
   return ConstrainColumnBlockSize(block_size);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
index 3e71299..dc6ad62f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
@@ -2798,6 +2798,34 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingSingleLine) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #parent {
+        columns: 3;
+        column-gap: 10px;
+        width: 320px;
+        line-height: 20px;
+      }
+    </style>
+    <div id="container">
+      <div id="parent">
+        <br>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x20
+    offset:0,0 size:320x20
+      offset:0,0 size:100x20
+        offset:0,0 size:0x20
+          offset:0,9 size:0x1
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingOverflow) {
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3165,6 +3193,56 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesAvoidBreakInside2) {
+  // We have 5 lines and 3 columns. If we make the columns tall enough to hold 2
+  // lines each, it should all fit. But then there's a block with 3 lines and
+  // break-inside:avoid...
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #parent {
+        columns: 3;
+        column-gap: 10px;
+        width: 320px;
+        line-height: 20px;
+        orphans: 1;
+        widows: 1;
+      }
+    </style>
+    <div id="container">
+      <div id="parent">
+        <br>
+        <div style="break-inside:avoid;">
+           <br><br><br>
+        </div>
+        <br>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x60
+    offset:0,0 size:320x60
+      offset:0,0 size:100x60
+        offset:0,0 size:100x20
+          offset:0,0 size:0x20
+            offset:0,9 size:0x1
+      offset:110,0 size:100x60
+        offset:0,0 size:100x60
+          offset:0,0 size:0x20
+            offset:0,9 size:0x1
+          offset:0,20 size:0x20
+            offset:0,9 size:0x1
+          offset:0,40 size:0x20
+            offset:0,9 size:0x1
+      offset:220,0 size:100x20
+        offset:0,0 size:100x20
+          offset:0,0 size:0x20
+            offset:0,9 size:0x1
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, ClassCBreakPointBeforeBfc) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 2ff1bd6..fa36fdc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -48,8 +48,20 @@
   bitfields_.subtree_modified_margin_strut =
       builder->subtree_modified_margin_strut_;
   intrinsic_block_size_ = builder->intrinsic_block_size_;
-  if (builder->minimal_space_shortage_ != LayoutUnit::Max())
+  if (builder->minimal_space_shortage_ != LayoutUnit::Max()) {
+#if DCHECK_IS_ON()
+    DCHECK(!HasRareData() || !rare_data_->has_tallest_unbreakable_block_size);
+#endif
     EnsureRareData()->minimal_space_shortage = builder->minimal_space_shortage_;
+  }
+  if (builder->tallest_unbreakable_block_size_ >= LayoutUnit()) {
+    auto* rare_data = EnsureRareData();
+    rare_data->tallest_unbreakable_block_size =
+        builder->tallest_unbreakable_block_size_;
+#if DCHECK_IS_ON()
+    rare_data->has_tallest_unbreakable_block_size = true;
+#endif
+  }
   if (builder->custom_layout_data_) {
     EnsureRareData()->custom_layout_data =
         std::move(builder->custom_layout_data_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index ed9f62e7..bfcc650 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -141,8 +141,23 @@
   }
 
   LayoutUnit MinimalSpaceShortage() const {
-    return HasRareData() ? rare_data_->minimal_space_shortage
-                         : LayoutUnit::Max();
+    if (!HasRareData())
+      return LayoutUnit::Max();
+#if DCHECK_IS_ON()
+    // This field shares storage with another field.
+    DCHECK(!rare_data_->has_tallest_unbreakable_block_size);
+#endif
+    return rare_data_->minimal_space_shortage;
+  }
+
+  LayoutUnit TallestUnbreakableBlockSize() const {
+    if (!HasRareData())
+      return LayoutUnit();
+#if DCHECK_IS_ON()
+    // This field shares storage with another field.
+    DCHECK(rare_data_->has_tallest_unbreakable_block_size);
+#endif
+    return rare_data_->tallest_unbreakable_block_size;
   }
 
   SerializedScriptValue* CustomLayoutData() const {
@@ -310,9 +325,24 @@
     NGMarginStrut end_margin_strut;
     NGUnpositionedListMarker unpositioned_list_marker;
     NGBlockNode column_spanner = nullptr;
-    LayoutUnit minimal_space_shortage = LayoutUnit::Max();
+    union {
+      // Only set in the initial column balancing layout pass, when we have no
+      // clue what the column block-size is going to be.
+      LayoutUnit tallest_unbreakable_block_size;
+
+      // Only set in subsequent column balancing passes, when we have set a
+      // tentative column block-size. At every column boundary we'll record
+      // space shortage, and store the smallest one here. If the columns
+      // couldn't fit all the content, and we're allowed to stretch columns
+      // further, we'll perform another pass with the column block-size
+      // increased by this amount.
+      LayoutUnit minimal_space_shortage = LayoutUnit::Max();
+    };
     NGExclusionSpace exclusion_space;
     scoped_refptr<SerializedScriptValue> custom_layout_data;
+#if DCHECK_IS_ON()
+    bool has_tallest_unbreakable_block_size = false;
+#endif
   };
 
   bool HasRareData() const { return bitfields_.has_rare_data; }
diff --git a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.cc b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.cc
index 4b89d66..13b5e510 100644
--- a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.cc
+++ b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.cc
@@ -38,6 +38,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "services/network/public/cpp/request_mode.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
 #include "third_party/blink/public/platform/task_type.h"
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/scheduler_affecting_features_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/scheduler_affecting_features_test.cc
index 7c1c09e..fd52e176 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/scheduler_affecting_features_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/scheduler_affecting_features_test.cc
@@ -270,4 +270,17 @@
                   SchedulingPolicy::Feature::kContainsPlugins));
 }
 
+TEST_F(SchedulingAffectingFeaturesTest, WebLocks) {
+  SimRequest main_resource("https://foo.com/", "text/html");
+  LoadURL("https://foo.com/");
+  main_resource.Complete(
+      "(<script>"
+      " navigator.locks.request('my_resource', async lock => {}); "
+      "</script>)");
+
+  EXPECT_THAT(
+      GetNonTrivialMainFrameFeatures(),
+      testing::UnorderedElementsAre(SchedulingPolicy::Feature::kWebLocks));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/writable_stream.h b/third_party/blink/renderer/core/streams/writable_stream.h
index 6934e623..047b661 100644
--- a/third_party/blink/renderer/core/streams/writable_stream.h
+++ b/third_party/blink/renderer/core/streams/writable_stream.h
@@ -32,6 +32,10 @@
                                 ScriptValue underlying_sink,
                                 ScriptValue strategy,
                                 ExceptionState&);
+
+  // Creates a WritableStream from C++. If |high_water_mark| is set to 0 then
+  // piping to this writable stream will not work, because there will always be
+  // backpressure. Usually 1 is the right choice.
   static WritableStream* CreateWithCountQueueingStrategy(
       ScriptState*,
       UnderlyingSinkBase*,
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index aee2de6c..5cc3e58 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -918,10 +918,18 @@
           ? interval_.end
           : SMILTime::Earliest();
   SMILInterval next_interval = ResolveInterval(begin_after, presentation_time);
-  if (!next_interval.IsResolved() || next_interval == interval_)
+  // It's the same interval that we resolved before. Do nothing.
+  if (next_interval == interval_)
     return;
   if (interval_.IsResolved())
     previous_interval_ = interval_;
+  // If there are no more intervals to resolve, we have to wait for an event to
+  // occur in order to get a new instance time.
+  if (!next_interval.IsResolved()) {
+    interval_ = next_interval;
+    next_interval_time_ = SMILTime::Unresolved();
+    return;
+  }
   SetNewInterval(next_interval, presentation_time);
   interval_has_changed_ = true;
 }
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 0b34a30..ff4785d1 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/public/common/blob/blob_utils.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_document_or_string_or_form_data_or_url_search_params.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/SourceMapManager.js b/third_party/blink/renderer/devtools/front_end/sdk/SourceMapManager.js
index c7015b4c..7d72611 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/SourceMapManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/SourceMapManager.js
@@ -20,14 +20,14 @@
     /** @type {!Map<!T, string>} */
     this._relativeSourceMapURL = new Map();
     /** @type {!Map<!T, string>} */
-    this._resolvedSourceMapURL = new Map();
+    this._resolvedSourceMapId = new Map();
 
     /** @type {!Map<string, !SDK.SourceMap>} */
-    this._sourceMapByURL = new Map();
+    this._sourceMapById = new Map();
     /** @type {!Platform.Multimap<string, !T>} */
-    this._sourceMapURLToLoadingClients = new Platform.Multimap();
+    this._sourceMapIdToLoadingClients = new Platform.Multimap();
     /** @type {!Platform.Multimap<string, !T>} */
-    this._sourceMapURLToClients = new Platform.Multimap();
+    this._sourceMapIdToClients = new Platform.Multimap();
 
     SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this);
   }
@@ -40,7 +40,10 @@
       return;
     }
     this._isEnabled = isEnabled;
-    const clients = Array.from(this._resolvedSourceMapURL.keys());
+    // We need this copy, because `this._resolvedSourceMapId` is getting modified
+    // in the loop body and trying to iterate over it at the same time leads to
+    // an infinite loop.
+    const clients = [...this._resolvedSourceMapId.keys()];
     for (const client of clients) {
       const relativeSourceURL = this._relativeSourceURL.get(client);
       const relativeSourceMapURL = this._relativeSourceMapURL.get(client);
@@ -57,13 +60,15 @@
       return;
     }
 
-    const clients = Array.from(this._resolvedSourceMapURL.keys());
-    for (const client of clients) {
+    // We need this copy, because `this._resolvedSourceMapId` is getting modified
+    // in the loop body and trying to iterate over it at the same time leads to
+    // an infinite loop.
+    const prevSourceMapIds = new Map(this._resolvedSourceMapId);
+    for (const [client, prevSourceMapId] of prevSourceMapIds) {
       const relativeSourceURL = this._relativeSourceURL.get(client);
       const relativeSourceMapURL = this._relativeSourceMapURL.get(client);
-      const resolvedSourceMapURL = this._resolvedSourceMapURL.get(client);
-      const sourceMapURL = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL).sourceMapURL;
-      if (sourceMapURL !== resolvedSourceMapURL) {
+      const {sourceMapId} = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL);
+      if (prevSourceMapId !== sourceMapId) {
         this.detachSourceMap(client);
         this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL);
       }
@@ -75,8 +80,11 @@
    * @return {?SDK.SourceMap}
    */
   sourceMapForClient(client) {
-    const sourceMapURL = this._resolvedSourceMapURL.get(client);
-    return sourceMapURL ? this._sourceMapByURL.get(sourceMapURL) || null : null;
+    const sourceMapId = this._resolvedSourceMapId.get(client);
+    if (!sourceMapId) {
+      return null;
+    }
+    return this._sourceMapById.get(sourceMapId) || null;
   }
 
   /**
@@ -84,82 +92,88 @@
    * @return {!Array<!T>}
    */
   clientsForSourceMap(sourceMap) {
-    if (this._sourceMapURLToClients.has(sourceMap.url())) {
-      return this._sourceMapURLToClients.get(sourceMap.url()).valuesArray();
+    const sourceMapId = this._getSourceMapId(sourceMap.compiledURL(), sourceMap.url());
+    if (this._sourceMapIdToClients.has(sourceMapId)) {
+      return this._sourceMapIdToClients.get(sourceMapId).valuesArray();
     }
-    return this._sourceMapURLToLoadingClients.get(sourceMap.url()).valuesArray();
-  }
-
-  /**
-   * @param {!SDK.SourceMap.EditResult} editResult
-   */
-  applySourceMapEdit(editResult) {
-    console.assert(
-        this._sourceMapByURL.has(editResult.map.url()), 'Cannot apply edit result for non-existing source map');
-    this._sourceMapByURL.set(editResult.map.url(), editResult.map);
-    this.dispatchEventToListeners(
-        Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources});
+    return this._sourceMapIdToLoadingClients.get(sourceMapId).valuesArray();
   }
 
   /**
    * @param {string} sourceURL
    * @param {string} sourceMapURL
-   * @return {!{sourceURL: ?string, sourceMapURL: ?string}}
+   */
+  _getSourceMapId(sourceURL, sourceMapURL) {
+    return `${sourceURL}:${sourceMapURL}`;
+  }
+
+  /**
+   * @param {string} sourceURL
+   * @param {string} sourceMapURL
+   * @return {?{sourceURL: string, sourceMapURL: string, sourceMapId: string}}
    */
   _resolveRelativeURLs(sourceURL, sourceMapURL) {
     // |sourceURL| can be a random string, but is generally an absolute path.
     // Complete it to inspected page url for relative links.
     const resolvedSourceURL = Common.ParsedURL.completeURL(this._target.inspectedURL(), sourceURL);
-    const resolvedSourceMapURL =
-        resolvedSourceURL ? Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL) : null;
-    return {sourceURL: resolvedSourceURL, sourceMapURL: resolvedSourceMapURL};
+    if (!resolvedSourceURL) {
+      return null;
+    }
+    const resolvedSourceMapURL = Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL);
+    if (!resolvedSourceMapURL) {
+      return null;
+    }
+    return {
+      sourceURL: resolvedSourceURL,
+      sourceMapURL: resolvedSourceMapURL,
+      sourceMapId: this._getSourceMapId(resolvedSourceURL, resolvedSourceMapURL)
+    };
   }
 
   /**
    * @param {!T} client
-   * @param {string} sourceURL
-   * @param {?string} sourceMapURL
+   * @param {string} relativeSourceURL
+   * @param {?string} relativeSourceMapURL
    */
-  attachSourceMap(client, sourceURL, sourceMapURL) {
-    if (!sourceMapURL) {
+  attachSourceMap(client, relativeSourceURL, relativeSourceMapURL) {
+    if (!relativeSourceMapURL) {
       return;
     }
-    console.assert(!this._resolvedSourceMapURL.has(client), 'SourceMap is already attached to client');
-    const resolvedURLs = this._resolveRelativeURLs(sourceURL, sourceMapURL);
-    if (!resolvedURLs.sourceURL || !resolvedURLs.sourceMapURL) {
+    console.assert(!this._resolvedSourceMapId.has(client), 'SourceMap is already attached to client');
+    const resolvedURLs = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL);
+    if (!resolvedURLs) {
       return;
     }
-    this._relativeSourceURL.set(client, sourceURL);
-    this._relativeSourceMapURL.set(client, sourceMapURL);
-    this._resolvedSourceMapURL.set(client, resolvedURLs.sourceMapURL);
+    this._relativeSourceURL.set(client, relativeSourceURL);
+    this._relativeSourceMapURL.set(client, relativeSourceMapURL);
 
-    sourceURL = resolvedURLs.sourceURL;
-    sourceMapURL = resolvedURLs.sourceMapURL;
+    const {sourceURL, sourceMapURL, sourceMapId} = resolvedURLs;
+    this._resolvedSourceMapId.set(client, sourceMapId);
+
     if (!this._isEnabled) {
       return;
     }
 
     this.dispatchEventToListeners(Events.SourceMapWillAttach, client);
 
-    if (this._sourceMapByURL.has(sourceMapURL)) {
-      attach.call(this, sourceMapURL, client);
+    if (this._sourceMapById.has(sourceMapId)) {
+      attach.call(this, sourceMapId, client);
       return;
     }
-    if (!this._sourceMapURLToLoadingClients.has(sourceMapURL)) {
-      SDK.TextSourceMap.load(sourceMapURL, sourceURL)
-          .then(onSourceMap.bind(this, sourceMapURL));
+    if (!this._sourceMapIdToLoadingClients.has(sourceMapId)) {
+      SDK.TextSourceMap.load(sourceMapURL, sourceURL).then(onSourceMap.bind(this, sourceMapId));
     }
-    this._sourceMapURLToLoadingClients.set(sourceMapURL, client);
+    this._sourceMapIdToLoadingClients.set(sourceMapId, client);
 
     /**
-     * @param {string} sourceMapURL
+     * @param {string} sourceMapId
      * @param {?SDK.SourceMap} sourceMap
      * @this {SourceMapManager}
      */
-    function onSourceMap(sourceMapURL, sourceMap) {
+    function onSourceMap(sourceMapId, sourceMap) {
       this._sourceMapLoadedForTest();
-      const clients = this._sourceMapURLToLoadingClients.get(sourceMapURL);
-      this._sourceMapURLToLoadingClients.deleteAll(sourceMapURL);
+      const clients = this._sourceMapIdToLoadingClients.get(sourceMapId);
+      this._sourceMapIdToLoadingClients.deleteAll(sourceMapId);
       if (!clients.size) {
         return;
       }
@@ -169,20 +183,20 @@
         }
         return;
       }
-      this._sourceMapByURL.set(sourceMapURL, sourceMap);
+      this._sourceMapById.set(sourceMapId, sourceMap);
       for (const client of clients) {
-        attach.call(this, sourceMapURL, client);
+        attach.call(this, sourceMapId, client);
       }
     }
 
     /**
-     * @param {string} sourceMapURL
+     * @param {string} sourceMapId
      * @param {!T} client
      * @this {SourceMapManager}
      */
-    function attach(sourceMapURL, client) {
-      this._sourceMapURLToClients.set(sourceMapURL, client);
-      const sourceMap = this._sourceMapByURL.get(sourceMapURL);
+    function attach(sourceMapId, client) {
+      this._sourceMapIdToClients.set(sourceMapId, client);
+      const sourceMap = this._sourceMapById.get(sourceMapId);
       this.dispatchEventToListeners(Events.SourceMapAttached, {client: client, sourceMap: sourceMap});
     }
   }
@@ -191,24 +205,24 @@
    * @param {!T} client
    */
   detachSourceMap(client) {
-    const sourceMapURL = this._resolvedSourceMapURL.get(client);
+    const sourceMapId = this._resolvedSourceMapId.get(client);
     this._relativeSourceURL.delete(client);
     this._relativeSourceMapURL.delete(client);
-    this._resolvedSourceMapURL.delete(client);
+    this._resolvedSourceMapId.delete(client);
 
-    if (!sourceMapURL) {
+    if (!sourceMapId) {
       return;
     }
-    if (!this._sourceMapURLToClients.hasValue(sourceMapURL, client)) {
-      if (this._sourceMapURLToLoadingClients.delete(sourceMapURL, client)) {
+    if (!this._sourceMapIdToClients.hasValue(sourceMapId, client)) {
+      if (this._sourceMapIdToLoadingClients.delete(sourceMapId, client)) {
         this.dispatchEventToListeners(Events.SourceMapFailedToAttach, client);
       }
       return;
     }
-    this._sourceMapURLToClients.delete(sourceMapURL, client);
-    const sourceMap = this._sourceMapByURL.get(sourceMapURL);
-    if (!this._sourceMapURLToClients.has(sourceMapURL)) {
-      this._sourceMapByURL.delete(sourceMapURL);
+    this._sourceMapIdToClients.delete(sourceMapId, client);
+    const sourceMap = this._sourceMapById.get(sourceMapId);
+    if (!this._sourceMapIdToClients.has(sourceMapId)) {
+      this._sourceMapById.delete(sourceMapId);
     }
     this.dispatchEventToListeners(Events.SourceMapDetached, {client: client, sourceMap: sourceMap});
   }
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 21d648b6..86b30b59 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -391,7 +391,10 @@
     "peerconnection/rtc_quic_transport_test.cc",
     "peerconnection/rtc_quic_transport_test.h",
     "peerconnection/rtc_rtp_receiver_impl_test.cc",
+    "peerconnection/rtc_rtp_sender_impl_test.cc",
+    "peerconnection/rtc_rtp_transceiver_impl_test.cc",
     "peerconnection/rtc_sctp_transport_test.cc",
+    "peerconnection/transceiver_state_surfacer_test.cc",
     "peerconnection/webrtc_media_stream_track_adapter_map_test.cc",
     "peerconnection/webrtc_media_stream_track_adapter_test.cc",
     "picture_in_picture/picture_in_picture_controller_test.cc",
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
index e69044c..c86254e 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
@@ -100,6 +100,14 @@
     return promise;
   }
 
+  // The context may be destroyed and the mojo connection unbound. However the
+  // object may live on, reject any requests after the context is destroyed.
+  if (!cache_storage_remote_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return promise;
+  }
+
   ever_used_ = true;
 
   // Make sure to bind the CacheStorage object to keep the mojo interface
@@ -166,6 +174,14 @@
     return promise;
   }
 
+  // The context may be destroyed and the mojo connection unbound. However the
+  // object may live on, reject any requests after the context is destroyed.
+  if (!cache_storage_remote_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return promise;
+  }
+
   ever_used_ = true;
 
   // Make sure to bind the CacheStorage object to keep the mojo interface
@@ -218,6 +234,14 @@
     return promise;
   }
 
+  // The context may be destroyed and the mojo connection unbound. However the
+  // object may live on, reject any requests after the context is destroyed.
+  if (!cache_storage_remote_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return promise;
+  }
+
   ever_used_ = true;
 
   // Make sure to bind the CacheStorage object to keep the mojo interface
@@ -270,6 +294,14 @@
     return promise;
   }
 
+  // The context may be destroyed and the mojo connection unbound. However the
+  // object may live on, reject any requests after the context is destroyed.
+  if (!cache_storage_remote_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return promise;
+  }
+
   ever_used_ = true;
 
   // Make sure to bind the CacheStorage object to keep the mojo interface
@@ -333,6 +365,14 @@
     return promise;
   }
 
+  // The context may be destroyed and the mojo connection unbound. However the
+  // object may live on, reject any requests after the context is destroyed.
+  if (!cache_storage_remote_) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError));
+    return promise;
+  }
+
   if (request->method() != http_names::kGET && !options->ignoreMethod()) {
     resolver->Resolve();
     return promise;
diff --git a/third_party/blink/renderer/modules/compression/BUILD.gn b/third_party/blink/renderer/modules/compression/BUILD.gn
index 04b392be..b5a2071 100644
--- a/third_party/blink/renderer/modules/compression/BUILD.gn
+++ b/third_party/blink/renderer/modules/compression/BUILD.gn
@@ -10,5 +10,7 @@
     "deflate_transformer.h",
     "inflate_transformer.cc",
     "inflate_transformer.h",
+    "zlib_partition_alloc.cc",
+    "zlib_partition_alloc.h",
   ]
 }
diff --git a/third_party/blink/renderer/modules/compression/deflate_transformer.cc b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
index e288321..f8ee728 100644
--- a/third_party/blink/renderer/modules/compression/deflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h"
 #include "third_party/blink/renderer/core/streams/transform_stream_transformer.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
+#include "third_party/blink/renderer/modules/compression/zlib_partition_alloc.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/to_v8.h"
 #include "v8/include/v8.h"
@@ -26,6 +27,7 @@
     : script_state_(script_state), out_buffer_(kBufferSize) {
   DCHECK(level >= 1 && level <= 9);
   memset(&stream_, 0, sizeof(z_stream));
+  ZlibPartitionAlloc::Configure(&stream_);
   constexpr int kWindowBits = 15;
   constexpr int kUseGzip = 16;
   int err;
diff --git a/third_party/blink/renderer/modules/compression/inflate_transformer.cc b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
index c4a47de3..372a56b 100644
--- a/third_party/blink/renderer/modules/compression/inflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h"
 #include "third_party/blink/renderer/core/streams/transform_stream_transformer.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
+#include "third_party/blink/renderer/modules/compression/zlib_partition_alloc.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/to_v8.h"
 #include "v8/include/v8.h"
@@ -19,6 +20,7 @@
 InflateTransformer::InflateTransformer(ScriptState* script_state, Format format)
     : script_state_(script_state), out_buffer_(kBufferSize) {
   memset(&stream_, 0, sizeof(z_stream));
+  ZlibPartitionAlloc::Configure(&stream_);
   constexpr int kWindowBits = 15;
   constexpr int kUseGzip = 16;
   int err;
diff --git a/third_party/blink/renderer/modules/compression/zlib_partition_alloc.cc b/third_party/blink/renderer/modules/compression/zlib_partition_alloc.cc
new file mode 100644
index 0000000..9929763
--- /dev/null
+++ b/third_party/blink/renderer/modules/compression/zlib_partition_alloc.cc
@@ -0,0 +1,20 @@
+#include "third_party/blink/renderer/modules/compression/zlib_partition_alloc.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+namespace blink {
+
+void ZlibPartitionAlloc::Configure(z_stream* stream) {
+  stream->zalloc = Alloc;
+  stream->zfree = Free;
+}
+
+void* ZlibPartitionAlloc::Alloc(void*, uint32_t items, uint32_t size) {
+  // BufferMalloc is safer than FastMalloc when handling untrusted data.
+  return WTF::Partitions::BufferMalloc(items * size, "zlib");
+}
+
+void ZlibPartitionAlloc::Free(void*, void* address) {
+  WTF::Partitions::BufferFree(address);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/compression/zlib_partition_alloc.h b/third_party/blink/renderer/modules/compression/zlib_partition_alloc.h
new file mode 100644
index 0000000..f013f1a
--- /dev/null
+++ b/third_party/blink/renderer/modules/compression/zlib_partition_alloc.h
@@ -0,0 +1,24 @@
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_COMPRESSION_ZLIB_PARTITION_ALLOC_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_COMPRESSION_ZLIB_PARTITION_ALLOC_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/zlib/zlib.h"
+
+namespace blink {
+
+// Use partition alloc for zlib allocations.
+class ZlibPartitionAlloc {
+  STATIC_ONLY(ZlibPartitionAlloc);
+
+ public:
+  static void Configure(z_stream* s);
+
+ private:
+  static void* Alloc(void*, uint32_t items, uint32_t size);
+
+  static void Free(void*, void*);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_COMPRESSION_ZLIB_PARTITION_ALLOC_H_
diff --git a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
index 28469681..64d7dba 100644
--- a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
+++ b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
@@ -100,6 +100,7 @@
     app->setPlatform(res->platform);
     app->setURL(res->url);
     app->setId(res->id);
+    app->setVersion(res->version);
     applications.push_back(app);
   }
   callbacks->OnSuccess(applications);
diff --git a/third_party/blink/renderer/modules/installedapp/related_application.idl b/third_party/blink/renderer/modules/installedapp/related_application.idl
index a19b4f8..2e2085bb 100644
--- a/third_party/blink/renderer/modules/installedapp/related_application.idl
+++ b/third_party/blink/renderer/modules/installedapp/related_application.idl
@@ -9,4 +9,5 @@
     required USVString platform;
     USVString url;
     DOMString id;
+    DOMString version;
 };
diff --git a/third_party/blink/renderer/modules/locks/lock_manager.cc b/third_party/blink/renderer/modules/locks/lock_manager.cc
index 7f3238d4..af4894223 100644
--- a/third_party/blink/renderer/modules/locks/lock_manager.cc
+++ b/third_party/blink/renderer/modules/locks/lock_manager.cc
@@ -23,6 +23,8 @@
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -215,6 +217,10 @@
   ExecutionContext* context = ExecutionContext::From(script_state);
   DCHECK(context->IsContextThread());
 
+  context->GetScheduler()->RegisterStickyFeature(
+      blink::SchedulingPolicy::Feature::kWebLocks,
+      {blink::SchedulingPolicy::RecordMetricsForBackForwardCache()});
+
   // 5. If origin is an opaque origin, then reject promise with a
   // "SecurityError" DOMException.
   if (!context->GetSecurityOrigin()->CanAccessLocks()) {
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 2a4e062..32ce674d 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -100,8 +100,10 @@
     "rtc_rtp_receiver_impl.cc",
     "rtc_rtp_sender.cc",
     "rtc_rtp_sender.h",
+    "rtc_rtp_sender_impl.cc",
     "rtc_rtp_transceiver.cc",
     "rtc_rtp_transceiver.h",
+    "rtc_rtp_transceiver_impl.cc",
     "rtc_sctp_transport.cc",
     "rtc_sctp_transport.h",
     "rtc_session_description.cc",
@@ -125,6 +127,7 @@
     "rtc_void_request_promise_impl.h",
     "rtc_void_request_script_promise_resolver_impl.cc",
     "rtc_void_request_script_promise_resolver_impl.h",
+    "transceiver_state_surfacer.cc",
     "web_rtc_stats_report_callback_resolver.cc",
     "web_rtc_stats_report_callback_resolver.h",
     "webrtc_media_stream_track_adapter.cc",
@@ -142,9 +145,11 @@
   testonly = true
 
   sources = [
+    "fake_rtc_rtp_transceiver_impl.cc",
     "mock_data_channel_impl.cc",
     "mock_peer_connection_dependency_factory.cc",
     "mock_peer_connection_impl.cc",
+    "mock_web_rtc_peer_connection_handler_client.cc",
     "webrtc_stats_report_obtainer.cc",
   ]
 
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
similarity index 60%
rename from content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
rename to third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
index 1c5cf82..c0b8bdf 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
@@ -4,10 +4,9 @@
 
 #include <utility>
 
-#include "content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h"
-#include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
+#include "third_party/blink/public/web/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h"
 
-namespace content {
+namespace blink {
 
 blink::WebMediaStreamTrack CreateWebMediaStreamTrack(
     const std::string& id,
@@ -29,7 +28,7 @@
   return web_track;
 }
 
-FakeRTCRtpSender::FakeRTCRtpSender(
+FakeRTCRtpSenderImpl::FakeRTCRtpSenderImpl(
     base::Optional<std::string> track_id,
     std::vector<std::string> stream_ids,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
@@ -37,41 +36,44 @@
       stream_ids_(std::move(stream_ids)),
       task_runner_(task_runner) {}
 
-FakeRTCRtpSender::FakeRTCRtpSender(const FakeRTCRtpSender&) = default;
-
-FakeRTCRtpSender::~FakeRTCRtpSender() {}
-
-FakeRTCRtpSender& FakeRTCRtpSender::operator=(const FakeRTCRtpSender&) =
+FakeRTCRtpSenderImpl::FakeRTCRtpSenderImpl(const FakeRTCRtpSenderImpl&) =
     default;
 
-std::unique_ptr<blink::WebRTCRtpSender> FakeRTCRtpSender::ShallowCopy() const {
-  return std::make_unique<FakeRTCRtpSender>(*this);
+FakeRTCRtpSenderImpl::~FakeRTCRtpSenderImpl() {}
+
+FakeRTCRtpSenderImpl& FakeRTCRtpSenderImpl::operator=(
+    const FakeRTCRtpSenderImpl&) = default;
+
+std::unique_ptr<blink::WebRTCRtpSender> FakeRTCRtpSenderImpl::ShallowCopy()
+    const {
+  return std::make_unique<FakeRTCRtpSenderImpl>(*this);
 }
 
-uintptr_t FakeRTCRtpSender::Id() const {
+uintptr_t FakeRTCRtpSenderImpl::Id() const {
   NOTIMPLEMENTED();
   return 0;
 }
 
 rtc::scoped_refptr<webrtc::DtlsTransportInterface>
-FakeRTCRtpSender::DtlsTransport() {
+FakeRTCRtpSenderImpl::DtlsTransport() {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-webrtc::DtlsTransportInformation FakeRTCRtpSender::DtlsTransportInformation() {
+webrtc::DtlsTransportInformation
+FakeRTCRtpSenderImpl::DtlsTransportInformation() {
   NOTIMPLEMENTED();
   static webrtc::DtlsTransportInformation dummy(
       webrtc::DtlsTransportState::kNew);
   return dummy;
 }
 
-blink::WebMediaStreamTrack FakeRTCRtpSender::Track() const {
+blink::WebMediaStreamTrack FakeRTCRtpSenderImpl::Track() const {
   return track_id_ ? CreateWebMediaStreamTrack(*track_id_, task_runner_)
                    : blink::WebMediaStreamTrack();  // null
 }
 
-blink::WebVector<blink::WebString> FakeRTCRtpSender::StreamIds() const {
+blink::WebVector<blink::WebString> FakeRTCRtpSenderImpl::StreamIds() const {
   blink::WebVector<blink::WebString> web_stream_ids(stream_ids_.size());
   for (size_t i = 0; i < stream_ids_.size(); ++i) {
     web_stream_ids[i] = blink::WebString::FromUTF8(stream_ids_[i]);
@@ -79,83 +81,85 @@
   return web_stream_ids;
 }
 
-void FakeRTCRtpSender::ReplaceTrack(blink::WebMediaStreamTrack with_track,
-                                    blink::WebRTCVoidRequest request) {
+void FakeRTCRtpSenderImpl::ReplaceTrack(blink::WebMediaStreamTrack with_track,
+                                        blink::WebRTCVoidRequest request) {
   NOTIMPLEMENTED();
 }
 
 std::unique_ptr<blink::WebRTCDTMFSenderHandler>
-FakeRTCRtpSender::GetDtmfSender() const {
+FakeRTCRtpSenderImpl::GetDtmfSender() const {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-std::unique_ptr<webrtc::RtpParameters> FakeRTCRtpSender::GetParameters() const {
+std::unique_ptr<webrtc::RtpParameters> FakeRTCRtpSenderImpl::GetParameters()
+    const {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-void FakeRTCRtpSender::SetParameters(
+void FakeRTCRtpSenderImpl::SetParameters(
     blink::WebVector<webrtc::RtpEncodingParameters>,
     webrtc::DegradationPreference,
     blink::WebRTCVoidRequest) {
   NOTIMPLEMENTED();
 }
 
-void FakeRTCRtpSender::GetStats(
+void FakeRTCRtpSenderImpl::GetStats(
     blink::WebRTCStatsReportCallback,
     const blink::WebVector<webrtc::NonStandardGroupId>&) {
   NOTIMPLEMENTED();
 }
 
-void FakeRTCRtpSender::SetStreams(
+void FakeRTCRtpSenderImpl::SetStreams(
     const blink::WebVector<blink::WebString>& stream_ids) {
   NOTIMPLEMENTED();
 }
 
-FakeRTCRtpReceiver::FakeRTCRtpReceiver(
+FakeRTCRtpReceiverImpl::FakeRTCRtpReceiverImpl(
     const std::string& track_id,
     std::vector<std::string> stream_ids,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : track_(CreateWebMediaStreamTrack(track_id, task_runner)),
       stream_ids_(std::move(stream_ids)) {}
 
-FakeRTCRtpReceiver::FakeRTCRtpReceiver(const FakeRTCRtpReceiver&) = default;
-
-FakeRTCRtpReceiver::~FakeRTCRtpReceiver() {}
-
-FakeRTCRtpReceiver& FakeRTCRtpReceiver::operator=(const FakeRTCRtpReceiver&) =
+FakeRTCRtpReceiverImpl::FakeRTCRtpReceiverImpl(const FakeRTCRtpReceiverImpl&) =
     default;
 
-std::unique_ptr<blink::WebRTCRtpReceiver> FakeRTCRtpReceiver::ShallowCopy()
+FakeRTCRtpReceiverImpl::~FakeRTCRtpReceiverImpl() {}
+
+FakeRTCRtpReceiverImpl& FakeRTCRtpReceiverImpl::operator=(
+    const FakeRTCRtpReceiverImpl&) = default;
+
+std::unique_ptr<blink::WebRTCRtpReceiver> FakeRTCRtpReceiverImpl::ShallowCopy()
     const {
-  return std::make_unique<FakeRTCRtpReceiver>(*this);
+  return std::make_unique<FakeRTCRtpReceiverImpl>(*this);
 }
 
-uintptr_t FakeRTCRtpReceiver::Id() const {
+uintptr_t FakeRTCRtpReceiverImpl::Id() const {
   NOTIMPLEMENTED();
   return 0;
 }
 
 rtc::scoped_refptr<webrtc::DtlsTransportInterface>
-FakeRTCRtpReceiver::DtlsTransport() {
+FakeRTCRtpReceiverImpl::DtlsTransport() {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
 webrtc::DtlsTransportInformation
-FakeRTCRtpReceiver::DtlsTransportInformation() {
+FakeRTCRtpReceiverImpl::DtlsTransportInformation() {
   NOTIMPLEMENTED();
   static webrtc::DtlsTransportInformation dummy(
       webrtc::DtlsTransportState::kNew);
   return dummy;
 }
 
-const blink::WebMediaStreamTrack& FakeRTCRtpReceiver::Track() const {
+const blink::WebMediaStreamTrack& FakeRTCRtpReceiverImpl::Track() const {
   return track_;
 }
 
-blink::WebVector<blink::WebString> FakeRTCRtpReceiver::StreamIds() const {
+blink::WebVector<blink::WebString> FakeRTCRtpReceiverImpl::StreamIds() const {
   blink::WebVector<blink::WebString> web_stream_ids(stream_ids_.size());
   for (size_t i = 0; i < stream_ids_.size(); ++i) {
     web_stream_ids[i] = blink::WebString::FromUTF8(stream_ids_[i]);
@@ -164,32 +168,32 @@
 }
 
 blink::WebVector<std::unique_ptr<blink::WebRTCRtpSource>>
-FakeRTCRtpReceiver::GetSources() {
+FakeRTCRtpReceiverImpl::GetSources() {
   NOTIMPLEMENTED();
   return {};
 }
 
-void FakeRTCRtpReceiver::GetStats(
+void FakeRTCRtpReceiverImpl::GetStats(
     blink::WebRTCStatsReportCallback,
     const blink::WebVector<webrtc::NonStandardGroupId>&) {
   NOTIMPLEMENTED();
 }
 
-std::unique_ptr<webrtc::RtpParameters> FakeRTCRtpReceiver::GetParameters()
+std::unique_ptr<webrtc::RtpParameters> FakeRTCRtpReceiverImpl::GetParameters()
     const {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-void FakeRTCRtpReceiver::SetJitterBufferMinimumDelay(
+void FakeRTCRtpReceiverImpl::SetJitterBufferMinimumDelay(
     base::Optional<double> delay_seconds) {
   NOTIMPLEMENTED();
 }
 
-FakeRTCRtpTransceiver::FakeRTCRtpTransceiver(
+FakeRTCRtpTransceiverImpl::FakeRTCRtpTransceiverImpl(
     base::Optional<std::string> mid,
-    FakeRTCRtpSender sender,
-    FakeRTCRtpReceiver receiver,
+    FakeRTCRtpSenderImpl sender,
+    FakeRTCRtpReceiverImpl receiver,
     bool stopped,
     webrtc::RtpTransceiverDirection direction,
     base::Optional<webrtc::RtpTransceiverDirection> current_direction)
@@ -200,53 +204,54 @@
       direction_(std::move(direction)),
       current_direction_(std::move(current_direction)) {}
 
-FakeRTCRtpTransceiver::~FakeRTCRtpTransceiver() {}
+FakeRTCRtpTransceiverImpl::~FakeRTCRtpTransceiverImpl() {}
 
 blink::WebRTCRtpTransceiverImplementationType
-FakeRTCRtpTransceiver::ImplementationType() const {
+FakeRTCRtpTransceiverImpl::ImplementationType() const {
   return blink::WebRTCRtpTransceiverImplementationType::kFullTransceiver;
 }
 
-uintptr_t FakeRTCRtpTransceiver::Id() const {
+uintptr_t FakeRTCRtpTransceiverImpl::Id() const {
   NOTIMPLEMENTED();
   return 0u;
 }
 
-blink::WebString FakeRTCRtpTransceiver::Mid() const {
+blink::WebString FakeRTCRtpTransceiverImpl::Mid() const {
   return mid_ ? blink::WebString::FromUTF8(*mid_) : blink::WebString();
 }
 
-std::unique_ptr<blink::WebRTCRtpSender> FakeRTCRtpTransceiver::Sender() const {
+std::unique_ptr<blink::WebRTCRtpSender> FakeRTCRtpTransceiverImpl::Sender()
+    const {
   return sender_.ShallowCopy();
 }
 
-std::unique_ptr<blink::WebRTCRtpReceiver> FakeRTCRtpTransceiver::Receiver()
+std::unique_ptr<blink::WebRTCRtpReceiver> FakeRTCRtpTransceiverImpl::Receiver()
     const {
   return receiver_.ShallowCopy();
 }
 
-bool FakeRTCRtpTransceiver::Stopped() const {
+bool FakeRTCRtpTransceiverImpl::Stopped() const {
   return stopped_;
 }
 
-webrtc::RtpTransceiverDirection FakeRTCRtpTransceiver::Direction() const {
+webrtc::RtpTransceiverDirection FakeRTCRtpTransceiverImpl::Direction() const {
   return direction_;
 }
 
-void FakeRTCRtpTransceiver::SetDirection(
+void FakeRTCRtpTransceiverImpl::SetDirection(
     webrtc::RtpTransceiverDirection direction) {
   NOTIMPLEMENTED();
 }
 
 base::Optional<webrtc::RtpTransceiverDirection>
-FakeRTCRtpTransceiver::CurrentDirection() const {
+FakeRTCRtpTransceiverImpl::CurrentDirection() const {
   return current_direction_;
 }
 
 base::Optional<webrtc::RtpTransceiverDirection>
-FakeRTCRtpTransceiver::FiredDirection() const {
+FakeRTCRtpTransceiverImpl::FiredDirection() const {
   NOTIMPLEMENTED();
   return base::nullopt;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc b/third_party/blink/renderer/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.cc
similarity index 88%
rename from content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc
rename to third_party/blink/renderer/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.cc
index 880ab858..81cfc28 100644
--- a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.cc
@@ -1,17 +1,16 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_web_rtc_peer_connection_handler_client.h"
 
 #include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_string.h"
 
 using testing::_;
 
-namespace content {
+namespace blink {
 
 MockWebRTCPeerConnectionHandlerClient::MockWebRTCPeerConnectionHandlerClient() {
   ON_CALL(*this, DidGenerateICECandidate(_))
@@ -28,7 +27,7 @@
 }
 
 MockWebRTCPeerConnectionHandlerClient::
-~MockWebRTCPeerConnectionHandlerClient() {}
+    ~MockWebRTCPeerConnectionHandlerClient() {}
 
 void MockWebRTCPeerConnectionHandlerClient::didGenerateICECandidateWorker(
     scoped_refptr<blink::WebRTCICECandidate> candidate) {
@@ -50,4 +49,4 @@
   remote_stream_id_ = blink::WebString();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
similarity index 82%
rename from content/renderer/media/webrtc/rtc_rtp_sender.cc
rename to third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
index 75f8bab..6936819 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/rtc_rtp_sender.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h"
 
 #include <memory>
 #include <utility>
@@ -11,8 +11,9 @@
 #include "base/logging.h"
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -20,11 +21,12 @@
 // on RTCError, as to surface both exception type and error message.
 // https://crbug.com/790007
 void OnReplaceTrackCompleted(blink::WebRTCVoidRequest request, bool result) {
-  if (result)
+  if (result) {
     request.RequestSucceeded();
-  else
+  } else {
     request.RequestFailed(
         webrtc::RTCError(webrtc::RTCErrorType::INVALID_MODIFICATION));
+  }
 }
 
 void OnSetParametersCompleted(blink::WebRTCVoidRequest request,
@@ -155,10 +157,10 @@
   return stream_ids_;
 }
 
-class RTCRtpSender::RTCRtpSenderInternal
-    : public base::RefCountedThreadSafe<
-          RTCRtpSender::RTCRtpSenderInternal,
-          RTCRtpSender::RTCRtpSenderInternalTraits> {
+class RTCRtpSenderImpl::RTCRtpSenderInternal
+    : public WTF::ThreadSafeRefCounted<
+          RTCRtpSenderImpl::RTCRtpSenderInternal,
+          RTCRtpSenderImpl::RTCRtpSenderInternalTraits> {
  public:
   RTCRtpSenderInternal(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
@@ -200,10 +202,10 @@
     }
     signaling_task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::ReplaceTrackOnSignalingThread,
-            this, std::move(track_ref), base::Unretained(webrtc_track),
-            std::move(callback)));
+        base::BindOnce(&RTCRtpSenderImpl::RTCRtpSenderInternal::
+                           ReplaceTrackOnSignalingThread,
+                       this, std::move(track_ref),
+                       base::Unretained(webrtc_track), std::move(callback)));
   }
 
   std::unique_ptr<blink::WebRTCDTMFSenderHandler> GetDtmfSender() const {
@@ -252,9 +254,9 @@
 
     signaling_task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::SetParametersOnSignalingThread,
-            this, std::move(new_parameters), std::move(callback)));
+        base::BindOnce(&RTCRtpSenderImpl::RTCRtpSenderInternal::
+                           SetParametersOnSignalingThread,
+                       this, std::move(new_parameters), std::move(callback)));
   }
 
   void GetStats(
@@ -263,7 +265,7 @@
     signaling_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::GetStatsOnSignalingThread,
+            &RTCRtpSenderImpl::RTCRtpSenderInternal::GetStatsOnSignalingThread,
             this, std::move(callback), exposed_group_ids));
   }
 
@@ -282,14 +284,15 @@
   void SetStreams(std::vector<std::string> stream_ids) {
     DCHECK(main_task_runner_->BelongsToCurrentThread());
     signaling_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::SetStreamsOnSignalingThread,
-            this, std::move(stream_ids)));
+        FROM_HERE, base::BindOnce(&RTCRtpSenderImpl::RTCRtpSenderInternal::
+                                      SetStreamsOnSignalingThread,
+                                  this, std::move(stream_ids)));
   }
 
  private:
-  friend struct RTCRtpSender::RTCRtpSenderInternalTraits;
+  friend class WTF::ThreadSafeRefCounted<RTCRtpSenderInternal,
+                                         RTCRtpSenderInternalTraits>;
+  friend struct RTCRtpSenderImpl::RTCRtpSenderInternalTraits;
 
   ~RTCRtpSenderInternal() {
     // Ensured by destructor traits.
@@ -308,7 +311,7 @@
     main_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::ReplaceTrackCallback, this,
+            &RTCRtpSenderImpl::RTCRtpSenderInternal::ReplaceTrackCallback, this,
             result, std::move(track_ref), std::move(callback)));
   }
 
@@ -340,8 +343,8 @@
     main_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
-            &RTCRtpSender::RTCRtpSenderInternal::SetParametersCallback, this,
-            std::move(result), std::move(callback)));
+            &RTCRtpSenderImpl::RTCRtpSenderInternal::SetParametersCallback,
+            this, std::move(result), std::move(callback)));
   }
 
   void SetParametersCallback(
@@ -368,78 +371,77 @@
   webrtc::RtpParameters parameters_;
 };
 
-struct RTCRtpSender::RTCRtpSenderInternalTraits {
- private:
-  friend class base::RefCountedThreadSafe<RTCRtpSenderInternal,
-                                          RTCRtpSenderInternalTraits>;
-
+struct RTCRtpSenderImpl::RTCRtpSenderInternalTraits {
   static void Destruct(const RTCRtpSenderInternal* sender) {
     // RTCRtpSenderInternal owns AdapterRefs which have to be destroyed on the
     // main thread, this ensures delete always happens there.
     if (!sender->main_task_runner_->BelongsToCurrentThread()) {
       sender->main_task_runner_->PostTask(
           FROM_HERE,
-          base::BindOnce(&RTCRtpSender::RTCRtpSenderInternalTraits::Destruct,
-                         base::Unretained(sender)));
+          base::BindOnce(
+              &RTCRtpSenderImpl::RTCRtpSenderInternalTraits::Destruct,
+              base::Unretained(sender)));
       return;
     }
     delete sender;
   }
 };
 
-uintptr_t RTCRtpSender::getId(const webrtc::RtpSenderInterface* webrtc_sender) {
+uintptr_t RTCRtpSenderImpl::getId(
+    const webrtc::RtpSenderInterface* webrtc_sender) {
   return reinterpret_cast<uintptr_t>(webrtc_sender);
 }
 
-RTCRtpSender::RTCRtpSender(
+RTCRtpSenderImpl::RTCRtpSenderImpl(
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
     RtpSenderState state)
-    : internal_(new RTCRtpSenderInternal(std::move(native_peer_connection),
-                                         std::move(track_map),
-                                         std::move(state))) {}
+    : internal_(base::MakeRefCounted<RTCRtpSenderInternal>(
+          std::move(native_peer_connection),
+          std::move(track_map),
+          std::move(state))) {}
 
-RTCRtpSender::RTCRtpSender(const RTCRtpSender& other)
+RTCRtpSenderImpl::RTCRtpSenderImpl(const RTCRtpSenderImpl& other)
     : internal_(other.internal_) {}
 
-RTCRtpSender::~RTCRtpSender() {}
+RTCRtpSenderImpl::~RTCRtpSenderImpl() {}
 
-RTCRtpSender& RTCRtpSender::operator=(const RTCRtpSender& other) {
+RTCRtpSenderImpl& RTCRtpSenderImpl::operator=(const RTCRtpSenderImpl& other) {
   internal_ = other.internal_;
   return *this;
 }
 
-const RtpSenderState& RTCRtpSender::state() const {
+const RtpSenderState& RTCRtpSenderImpl::state() const {
   return internal_->state();
 }
 
-void RTCRtpSender::set_state(RtpSenderState state) {
+void RTCRtpSenderImpl::set_state(RtpSenderState state) {
   internal_->set_state(std::move(state));
 }
 
-std::unique_ptr<blink::WebRTCRtpSender> RTCRtpSender::ShallowCopy() const {
-  return std::make_unique<RTCRtpSender>(*this);
+std::unique_ptr<blink::WebRTCRtpSender> RTCRtpSenderImpl::ShallowCopy() const {
+  return std::make_unique<RTCRtpSenderImpl>(*this);
 }
 
-uintptr_t RTCRtpSender::Id() const {
+uintptr_t RTCRtpSenderImpl::Id() const {
   return getId(internal_->state().webrtc_sender().get());
 }
 
 rtc::scoped_refptr<webrtc::DtlsTransportInterface>
-RTCRtpSender::DtlsTransport() {
+RTCRtpSenderImpl::DtlsTransport() {
   return internal_->state().webrtc_dtls_transport();
 }
 
-webrtc::DtlsTransportInformation RTCRtpSender::DtlsTransportInformation() {
+webrtc::DtlsTransportInformation RTCRtpSenderImpl::DtlsTransportInformation() {
   return internal_->state().webrtc_dtls_transport_information();
 }
 
-blink::WebMediaStreamTrack RTCRtpSender::Track() const {
+blink::WebMediaStreamTrack RTCRtpSenderImpl::Track() const {
   const auto& track_ref = internal_->state().track_ref();
   return track_ref ? track_ref->web_track() : blink::WebMediaStreamTrack();
 }
 
-blink::WebVector<blink::WebString> RTCRtpSender::StreamIds() const {
+blink::WebVector<blink::WebString> RTCRtpSenderImpl::StreamIds() const {
   const auto& stream_ids = internal_->state().stream_ids();
   blink::WebVector<blink::WebString> web_stream_ids(stream_ids.size());
   for (size_t i = 0; i < stream_ids.size(); ++i)
@@ -447,23 +449,23 @@
   return web_stream_ids;
 }
 
-void RTCRtpSender::ReplaceTrack(blink::WebMediaStreamTrack with_track,
-                                blink::WebRTCVoidRequest request) {
+void RTCRtpSenderImpl::ReplaceTrack(blink::WebMediaStreamTrack with_track,
+                                    blink::WebRTCVoidRequest request) {
   internal_->ReplaceTrack(
       std::move(with_track),
       base::BindOnce(&OnReplaceTrackCompleted, std::move(request)));
 }
 
-std::unique_ptr<blink::WebRTCDTMFSenderHandler> RTCRtpSender::GetDtmfSender()
-    const {
+std::unique_ptr<blink::WebRTCDTMFSenderHandler>
+RTCRtpSenderImpl::GetDtmfSender() const {
   return internal_->GetDtmfSender();
 }
 
-std::unique_ptr<webrtc::RtpParameters> RTCRtpSender::GetParameters() const {
+std::unique_ptr<webrtc::RtpParameters> RTCRtpSenderImpl::GetParameters() const {
   return internal_->GetParameters();
 }
 
-void RTCRtpSender::SetParameters(
+void RTCRtpSenderImpl::SetParameters(
     blink::WebVector<webrtc::RtpEncodingParameters> encodings,
     webrtc::DegradationPreference degradation_preference,
     blink::WebRTCVoidRequest request) {
@@ -472,13 +474,13 @@
       base::BindOnce(&OnSetParametersCompleted, std::move(request)));
 }
 
-void RTCRtpSender::GetStats(
+void RTCRtpSenderImpl::GetStats(
     blink::WebRTCStatsReportCallback callback,
     const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   internal_->GetStats(std::move(callback), exposed_group_ids);
 }
 
-void RTCRtpSender::SetStreams(
+void RTCRtpSenderImpl::SetStreams(
     const blink::WebVector<blink::WebString>& stream_ids) {
   std::vector<std::string> ids;
   for (auto stream_id : stream_ids)
@@ -487,12 +489,12 @@
   internal_->SetStreams(std::move(ids));
 }
 
-void RTCRtpSender::ReplaceTrack(blink::WebMediaStreamTrack with_track,
-                                base::OnceCallback<void(bool)> callback) {
+void RTCRtpSenderImpl::ReplaceTrack(blink::WebMediaStreamTrack with_track,
+                                    base::OnceCallback<void(bool)> callback) {
   internal_->ReplaceTrack(std::move(with_track), std::move(callback));
 }
 
-bool RTCRtpSender::RemoveFromPeerConnection(
+bool RTCRtpSenderImpl::RemoveFromPeerConnection(
     webrtc::PeerConnectionInterface* pc) {
   return internal_->RemoveFromPeerConnection(pc);
 }
@@ -564,4 +566,4 @@
   return {};
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
similarity index 86%
rename from content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
rename to third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
index ce7bd41..e952807c 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl_test.cc
@@ -2,17 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/rtc_rtp_sender.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_sender_impl.h"
 
 #include <memory>
 
 #include "base/bind.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
-#include "base/test/task_environment.h"
 #include "build/build_config.h"
-#include "content/child/child_process.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -26,6 +24,7 @@
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_stats_report_obtainer.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 #include "third_party/webrtc/api/stats/rtc_stats_report.h"
 #include "third_party/webrtc/api/stats/rtcstats_objects.h"
 #include "third_party/webrtc/api/test/mock_rtpsender.h"
@@ -33,9 +32,9 @@
 using ::testing::_;
 using ::testing::Return;
 
-namespace content {
+namespace blink {
 
-class RTCRtpSenderTest : public ::testing::Test {
+class RTCRtpSenderImplTest : public ::testing::Test {
  public:
   void SetUp() override {
     dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
@@ -82,7 +81,7 @@
     return web_track;
   }
 
-  std::unique_ptr<RTCRtpSender> CreateSender(
+  std::unique_ptr<RTCRtpSenderImpl> CreateSender(
       blink::WebMediaStreamTrack web_track) {
     std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
         track_ref;
@@ -95,8 +94,8 @@
                                 mock_webrtc_sender_.get(), std::move(track_ref),
                                 std::vector<std::string>());
     sender_state.Initialize();
-    return std::make_unique<RTCRtpSender>(peer_connection_.get(), track_map_,
-                                          std::move(sender_state));
+    return std::make_unique<RTCRtpSenderImpl>(
+        peer_connection_.get(), track_map_, std::move(sender_state));
   }
 
   // Calls replaceTrack(), which is asynchronous, returning a callback that when
@@ -109,12 +108,12 @@
     // On complete, |*result_holder| is set with the result of replaceTrack()
     // and the |run_loop| quit.
     sender_->ReplaceTrack(
-        web_track, base::BindOnce(&RTCRtpSenderTest::CallbackOnComplete,
+        web_track, base::BindOnce(&RTCRtpSenderImplTest::CallbackOnComplete,
                                   base::Unretained(this), result_holder.get(),
                                   run_loop.get()));
     // When the resulting callback is invoked, waits for |run_loop| to complete
     // and returns |*result_holder|.
-    return base::BindOnce(&RTCRtpSenderTest::RunLoopAndReturnResult,
+    return base::BindOnce(&RTCRtpSenderImplTest::RunLoopAndReturnResult,
                           base::Unretained(this), std::move(result_holder),
                           std::move(run_loop));
   }
@@ -140,11 +139,7 @@
     return *result_holder;
   }
 
-  // Code under test expects to be run in a process with an initialized
-  // ChildProcess, which requires ThreadPool, and a main-thread MessageLoop,
-  // which the TaskEnvironment also provides.
-  base::test::TaskEnvironment task_environment_;
-  ChildProcess child_process_;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
 
   std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
       dependency_factory_;
@@ -152,23 +147,23 @@
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map_;
   rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
   rtc::scoped_refptr<webrtc::MockRtpSender> mock_webrtc_sender_;
-  std::unique_ptr<RTCRtpSender> sender_;
+  std::unique_ptr<RTCRtpSenderImpl> sender_;
 };
 
-TEST_F(RTCRtpSenderTest, CreateSender) {
+TEST_F(RTCRtpSenderImplTest, CreateSender) {
   auto web_track = CreateWebTrack("track_id");
   sender_ = CreateSender(web_track);
   EXPECT_FALSE(sender_->Track().IsNull());
   EXPECT_EQ(web_track.UniqueId(), sender_->Track().UniqueId());
 }
 
-TEST_F(RTCRtpSenderTest, CreateSenderWithNullTrack) {
+TEST_F(RTCRtpSenderImplTest, CreateSenderWithNullTrack) {
   blink::WebMediaStreamTrack null_track;
   sender_ = CreateSender(null_track);
   EXPECT_TRUE(sender_->Track().IsNull());
 }
 
-TEST_F(RTCRtpSenderTest, ReplaceTrackSetsTrack) {
+TEST_F(RTCRtpSenderImplTest, ReplaceTrackSetsTrack) {
   auto web_track1 = CreateWebTrack("track1");
   sender_ = CreateSender(web_track1);
 
@@ -180,7 +175,7 @@
   EXPECT_EQ(web_track2.UniqueId(), sender_->Track().UniqueId());
 }
 
-TEST_F(RTCRtpSenderTest, ReplaceTrackWithNullTrack) {
+TEST_F(RTCRtpSenderImplTest, ReplaceTrackWithNullTrack) {
   auto web_track = CreateWebTrack("track_id");
   sender_ = CreateSender(web_track);
 
@@ -191,7 +186,7 @@
   EXPECT_TRUE(sender_->Track().IsNull());
 }
 
-TEST_F(RTCRtpSenderTest, ReplaceTrackCanFail) {
+TEST_F(RTCRtpSenderImplTest, ReplaceTrackCanFail) {
   auto web_track = CreateWebTrack("track_id");
   sender_ = CreateSender(web_track);
   ASSERT_FALSE(sender_->Track().IsNull());
@@ -206,7 +201,7 @@
   EXPECT_EQ(web_track.UniqueId(), sender_->Track().UniqueId());
 }
 
-TEST_F(RTCRtpSenderTest, ReplaceTrackIsNotSetSynchronously) {
+TEST_F(RTCRtpSenderImplTest, ReplaceTrackIsNotSetSynchronously) {
   auto web_track1 = CreateWebTrack("track1");
   sender_ = CreateSender(web_track1);
 
@@ -220,7 +215,7 @@
   std::move(replaceTrackRunLoopAndGetResult).Run();
 }
 
-TEST_F(RTCRtpSenderTest, GetStats) {
+TEST_F(RTCRtpSenderImplTest, GetStats) {
   auto web_track = CreateWebTrack("track_id");
   sender_ = CreateSender(web_track);
 
@@ -246,10 +241,10 @@
   EXPECT_EQ(stats->Timestamp(), 1.234);
 }
 
-TEST_F(RTCRtpSenderTest, CopiedSenderSharesInternalStates) {
+TEST_F(RTCRtpSenderImplTest, CopiedSenderSharesInternalStates) {
   auto web_track = CreateWebTrack("track_id");
   sender_ = CreateSender(web_track);
-  auto copy = std::make_unique<RTCRtpSender>(*sender_);
+  auto copy = std::make_unique<RTCRtpSenderImpl>(*sender_);
   // Copy shares original's ID.
   EXPECT_EQ(sender_->Id(), copy->Id());
 
@@ -263,4 +258,4 @@
   EXPECT_TRUE(copy->Track().IsNull());
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
similarity index 78%
rename from content/renderer/media/webrtc/rtc_rtp_transceiver.cc
rename to third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
index 093213b..0a68985 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/webrtc/api/scoped_refptr.h"
 
-namespace content {
+namespace blink {
 
 RtpTransceiverState::RtpTransceiverState(
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
     scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver,
-    base::Optional<RtpSenderState> sender_state,
+    base::Optional<blink::RtpSenderState> sender_state,
     base::Optional<blink::RtpReceiverState> receiver_state,
     base::Optional<std::string> mid,
     bool stopped,
@@ -114,15 +115,15 @@
   return webrtc_transceiver_;
 }
 
-const base::Optional<RtpSenderState>& RtpTransceiverState::sender_state()
+const base::Optional<blink::RtpSenderState>& RtpTransceiverState::sender_state()
     const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   return sender_state_;
 }
 
-RtpSenderState RtpTransceiverState::MoveSenderState() {
+blink::RtpSenderState RtpTransceiverState::MoveSenderState() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-  base::Optional<RtpSenderState> temp(base::nullopt);
+  base::Optional<blink::RtpSenderState> temp(base::nullopt);
   sender_state_.swap(temp);
   return *std::move(temp);
 }
@@ -173,10 +174,10 @@
   return fired_direction_;
 }
 
-class RTCRtpTransceiver::RTCRtpTransceiverInternal
-    : public base::RefCountedThreadSafe<
-          RTCRtpTransceiver::RTCRtpTransceiverInternal,
-          RTCRtpTransceiver::RTCRtpTransceiverInternalTraits> {
+class RTCRtpTransceiverImpl::RTCRtpTransceiverInternal
+    : public WTF::ThreadSafeRefCounted<
+          RTCRtpTransceiverImpl::RTCRtpTransceiverInternal,
+          RTCRtpTransceiverImpl::RTCRtpTransceiverInternalTraits> {
  public:
   RTCRtpTransceiverInternal(
       scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
@@ -186,8 +187,8 @@
         signaling_task_runner_(state.signaling_task_runner()),
         webrtc_transceiver_(state.webrtc_transceiver()),
         state_(std::move(state)) {
-    sender_ = std::make_unique<RTCRtpSender>(native_peer_connection, track_map,
-                                             state_.MoveSenderState());
+    sender_ = std::make_unique<blink::RTCRtpSenderImpl>(
+        native_peer_connection, track_map, state_.MoveSenderState());
     receiver_ = std::make_unique<blink::RTCRtpReceiverImpl>(
         native_peer_connection, state_.MoveReceiverState());
   }
@@ -222,7 +223,7 @@
     receiver_->set_state(state_.MoveReceiverState());
   }
 
-  RTCRtpSender* content_sender() {
+  blink::RTCRtpSenderImpl* content_sender() {
     DCHECK(main_task_runner_->BelongsToCurrentThread());
     return sender_.get();
   }
@@ -246,7 +247,9 @@
   }
 
  private:
-  friend struct RTCRtpTransceiver::RTCRtpTransceiverInternalTraits;
+  friend class WTF::ThreadSafeRefCounted<RTCRtpTransceiverInternal,
+                                         RTCRtpTransceiverInternalTraits>;
+  friend struct RTCRtpTransceiverImpl::RTCRtpTransceiverInternalTraits;
 
   ~RTCRtpTransceiverInternal() {
     // Ensured by destructor traits.
@@ -260,15 +263,11 @@
   const scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   const scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver_;
   RtpTransceiverState state_;
-  std::unique_ptr<RTCRtpSender> sender_;
+  std::unique_ptr<blink::RTCRtpSenderImpl> sender_;
   std::unique_ptr<blink::RTCRtpReceiverImpl> receiver_;
 };
 
-struct RTCRtpTransceiver::RTCRtpTransceiverInternalTraits {
- private:
-  friend class base::RefCountedThreadSafe<RTCRtpTransceiverInternal,
-                                          RTCRtpTransceiverInternalTraits>;
-
+struct RTCRtpTransceiverImpl::RTCRtpTransceiverInternalTraits {
   static void Destruct(const RTCRtpTransceiverInternal* transceiver) {
     // RTCRtpTransceiverInternal owns AdapterRefs which have to be destroyed on
     // the main thread, this ensures delete always happens there.
@@ -276,7 +275,7 @@
       transceiver->main_task_runner_->PostTask(
           FROM_HERE,
           base::BindOnce(
-              &RTCRtpTransceiver::RTCRtpTransceiverInternalTraits::Destruct,
+              &RTCRtpTransceiverImpl::RTCRtpTransceiverInternalTraits::Destruct,
               base::Unretained(transceiver)));
       return;
     }
@@ -284,99 +283,102 @@
   }
 };
 
-uintptr_t RTCRtpTransceiver::GetId(
+uintptr_t RTCRtpTransceiverImpl::GetId(
     const webrtc::RtpTransceiverInterface* webrtc_transceiver) {
   return reinterpret_cast<uintptr_t>(webrtc_transceiver);
 }
 
-RTCRtpTransceiver::RTCRtpTransceiver(
+RTCRtpTransceiverImpl::RTCRtpTransceiverImpl(
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map,
     RtpTransceiverState transceiver_state)
-    : internal_(new RTCRtpTransceiverInternal(std::move(native_peer_connection),
-                                              std::move(track_map),
-                                              std::move(transceiver_state))) {}
+    : internal_(base::MakeRefCounted<RTCRtpTransceiverInternal>(
+          std::move(native_peer_connection),
+          std::move(track_map),
+          std::move(transceiver_state))) {}
 
-RTCRtpTransceiver::RTCRtpTransceiver(const RTCRtpTransceiver& other)
+RTCRtpTransceiverImpl::RTCRtpTransceiverImpl(const RTCRtpTransceiverImpl& other)
     : internal_(other.internal_) {}
 
-RTCRtpTransceiver::~RTCRtpTransceiver() {}
+RTCRtpTransceiverImpl::~RTCRtpTransceiverImpl() {}
 
-RTCRtpTransceiver& RTCRtpTransceiver::operator=(
-    const RTCRtpTransceiver& other) {
+RTCRtpTransceiverImpl& RTCRtpTransceiverImpl::operator=(
+    const RTCRtpTransceiverImpl& other) {
   internal_ = other.internal_;
   return *this;
 }
 
-std::unique_ptr<RTCRtpTransceiver> RTCRtpTransceiver::ShallowCopy() const {
-  return std::make_unique<RTCRtpTransceiver>(*this);
+std::unique_ptr<RTCRtpTransceiverImpl> RTCRtpTransceiverImpl::ShallowCopy()
+    const {
+  return std::make_unique<RTCRtpTransceiverImpl>(*this);
 }
 
-const RtpTransceiverState& RTCRtpTransceiver::state() const {
+const RtpTransceiverState& RTCRtpTransceiverImpl::state() const {
   return internal_->state();
 }
 
-RTCRtpSender* RTCRtpTransceiver::content_sender() {
+blink::RTCRtpSenderImpl* RTCRtpTransceiverImpl::content_sender() {
   return internal_->content_sender();
 }
 
-blink::RTCRtpReceiverImpl* RTCRtpTransceiver::content_receiver() {
+blink::RTCRtpReceiverImpl* RTCRtpTransceiverImpl::content_receiver() {
   return internal_->content_receiver();
 }
 
-void RTCRtpTransceiver::set_state(RtpTransceiverState transceiver_state,
-                                  TransceiverStateUpdateMode update_mode) {
+void RTCRtpTransceiverImpl::set_state(RtpTransceiverState transceiver_state,
+                                      TransceiverStateUpdateMode update_mode) {
   internal_->set_state(std::move(transceiver_state), update_mode);
 }
 
 blink::WebRTCRtpTransceiverImplementationType
-RTCRtpTransceiver::ImplementationType() const {
+RTCRtpTransceiverImpl::ImplementationType() const {
   return blink::WebRTCRtpTransceiverImplementationType::kFullTransceiver;
 }
 
-uintptr_t RTCRtpTransceiver::Id() const {
+uintptr_t RTCRtpTransceiverImpl::Id() const {
   return GetId(internal_->state().webrtc_transceiver().get());
 }
 
-blink::WebString RTCRtpTransceiver::Mid() const {
+blink::WebString RTCRtpTransceiverImpl::Mid() const {
   const auto& mid = internal_->state().mid();
   return mid ? blink::WebString::FromUTF8(*mid)
              : blink::WebString();  // IsNull()
 }
 
-std::unique_ptr<blink::WebRTCRtpSender> RTCRtpTransceiver::Sender() const {
+std::unique_ptr<blink::WebRTCRtpSender> RTCRtpTransceiverImpl::Sender() const {
   return internal_->content_sender()->ShallowCopy();
 }
 
-std::unique_ptr<blink::WebRTCRtpReceiver> RTCRtpTransceiver::Receiver() const {
+std::unique_ptr<blink::WebRTCRtpReceiver> RTCRtpTransceiverImpl::Receiver()
+    const {
   return internal_->content_receiver()->ShallowCopy();
 }
 
-bool RTCRtpTransceiver::Stopped() const {
+bool RTCRtpTransceiverImpl::Stopped() const {
   return internal_->state().stopped();
 }
 
-webrtc::RtpTransceiverDirection RTCRtpTransceiver::Direction() const {
+webrtc::RtpTransceiverDirection RTCRtpTransceiverImpl::Direction() const {
   return internal_->state().direction();
 }
 
-void RTCRtpTransceiver::SetDirection(
+void RTCRtpTransceiverImpl::SetDirection(
     webrtc::RtpTransceiverDirection direction) {
   internal_->SetDirection(direction);
 }
 
 base::Optional<webrtc::RtpTransceiverDirection>
-RTCRtpTransceiver::CurrentDirection() const {
+RTCRtpTransceiverImpl::CurrentDirection() const {
   return internal_->state().current_direction();
 }
 
 base::Optional<webrtc::RtpTransceiverDirection>
-RTCRtpTransceiver::FiredDirection() const {
+RTCRtpTransceiverImpl::FiredDirection() const {
   return internal_->state().fired_direction();
 }
 
-webrtc::RTCError RTCRtpTransceiver::SetCodecPreferences(
+webrtc::RTCError RTCRtpTransceiverImpl::SetCodecPreferences(
     blink::WebVector<webrtc::RtpCodecCapability> codec_preferences) {
   return internal_->setCodecPreferences(codec_preferences.ReleaseVector());
 }
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
similarity index 89%
rename from content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
rename to third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
index 9bac393..5dcdfcf 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl_test.cc
@@ -2,20 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
+#include "third_party/blink/public/web/modules/peerconnection/rtc_rtp_transceiver_impl.h"
 
 #include <memory>
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/test/task_environment.h"
 #include "build/build_config.h"
-#include "content/child/child_process.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
@@ -26,12 +24,13 @@
 #include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 #include "third_party/webrtc/api/test/mock_rtpreceiver.h"
 #include "third_party/webrtc/api/test/mock_rtpsender.h"
 
-namespace content {
+namespace blink {
 
-class RTCRtpTransceiverTest : public ::testing::Test {
+class RTCRtpTransceiverImplTest : public ::testing::Test {
  public:
   void SetUp() override {
     dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
@@ -78,10 +77,11 @@
     base::RunLoop run_loop;
     signaling_task_runner()->PostTask(
         FROM_HERE,
-        base::BindOnce(
-            &RTCRtpTransceiverTest::CreateRemoteTrackAdapterOnSignalingThread,
-            base::Unretained(this), std::move(webrtc_track),
-            base::Unretained(&track_ref), base::Unretained(&run_loop)));
+        base::BindOnce(&RTCRtpTransceiverImplTest::
+                           CreateRemoteTrackAdapterOnSignalingThread,
+                       base::Unretained(this), std::move(webrtc_track),
+                       base::Unretained(&track_ref),
+                       base::Unretained(&run_loop)));
     run_loop.Run();
     DCHECK(track_ref);
     return track_ref;
@@ -135,10 +135,10 @@
     }
     return RtpTransceiverState(
         main_task_runner_, signaling_task_runner(), webrtc_transceiver.get(),
-        RtpSenderState(main_task_runner_, signaling_task_runner(),
-                       webrtc_transceiver->sender().get(),
-                       std::move(sender_track_ref),
-                       webrtc_transceiver->sender()->stream_ids()),
+        blink::RtpSenderState(main_task_runner_, signaling_task_runner(),
+                              webrtc_transceiver->sender().get(),
+                              std::move(sender_track_ref),
+                              webrtc_transceiver->sender()->stream_ids()),
         blink::RtpReceiverState(main_task_runner_, signaling_task_runner(),
                                 webrtc_transceiver->receiver().get(),
                                 std::move(receiver_track_ref),
@@ -177,7 +177,7 @@
   }
 
  private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
 
  protected:
   std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
@@ -187,7 +187,7 @@
   rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
 };
 
-TEST_F(RTCRtpTransceiverTest, InitializeTransceiverState) {
+TEST_F(RTCRtpTransceiverImplTest, InitializeTransceiverState) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
   auto webrtc_transceiver = CreateWebRtcTransceiver(
@@ -238,7 +238,7 @@
                                     webrtc_transceiver->fired_direction()));
 }
 
-TEST_F(RTCRtpTransceiverTest, CreateTranceiver) {
+TEST_F(RTCRtpTransceiverImplTest, CreateTranceiver) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
   auto webrtc_transceiver = CreateWebRtcTransceiver(
@@ -253,8 +253,8 @@
   EXPECT_FALSE(transceiver_state.is_initialized());
   transceiver_state.Initialize();
 
-  RTCRtpTransceiver transceiver(peer_connection_.get(), track_map_,
-                                std::move(transceiver_state));
+  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
+                                    std::move(transceiver_state));
   EXPECT_TRUE(transceiver.Mid().IsNull());
   EXPECT_TRUE(transceiver.Sender());
   EXPECT_TRUE(transceiver.Receiver());
@@ -265,7 +265,7 @@
   EXPECT_FALSE(transceiver.FiredDirection());
 }
 
-TEST_F(RTCRtpTransceiverTest, ModifyTransceiver) {
+TEST_F(RTCRtpTransceiverImplTest, ModifyTransceiver) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
   auto webrtc_sender =
@@ -297,8 +297,8 @@
 
   // Modifying the webrtc transceiver after the initial state was created should
   // not have affected the transceiver state.
-  RTCRtpTransceiver transceiver(peer_connection_.get(), track_map_,
-                                std::move(initial_transceiver_state));
+  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
+                                    std::move(initial_transceiver_state));
   EXPECT_TRUE(transceiver.Mid().IsNull());
   EXPECT_TRUE(transceiver.Sender());
   EXPECT_TRUE(transceiver.Receiver());
@@ -322,7 +322,7 @@
   EXPECT_FALSE(transceiver.FiredDirection());
 }
 
-TEST_F(RTCRtpTransceiverTest, ShallowCopy) {
+TEST_F(RTCRtpTransceiverImplTest, ShallowCopy) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
   auto webrtc_sender =
@@ -333,7 +333,7 @@
       webrtc_sender, webrtc_receiver, base::nullopt, false /* stopped */,
       webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt);
 
-  std::unique_ptr<RTCRtpTransceiver> transceiver;
+  std::unique_ptr<RTCRtpTransceiverImpl> transceiver;
   // Create transceiver.
   {
     RtpTransceiverState transceiver_state =
@@ -341,13 +341,14 @@
                                remote_track_adapter->Copy());
     EXPECT_FALSE(transceiver_state.is_initialized());
     transceiver_state.Initialize();
-    transceiver.reset(new RTCRtpTransceiver(peer_connection_.get(), track_map_,
-                                            std::move(transceiver_state)));
+    transceiver.reset(new RTCRtpTransceiverImpl(
+        peer_connection_.get(), track_map_, std::move(transceiver_state)));
   }
   DCHECK(transceiver);
   EXPECT_FALSE(transceiver->Stopped());
 
-  std::unique_ptr<RTCRtpTransceiver> shallow_copy = transceiver->ShallowCopy();
+  std::unique_ptr<RTCRtpTransceiverImpl> shallow_copy =
+      transceiver->ShallowCopy();
   // Modifying the shallow copy should modify the original too since they have a
   // shared internal state.
   {
@@ -368,7 +369,7 @@
   EXPECT_TRUE(transceiver->Stopped());
 }
 
-TEST_F(RTCRtpTransceiverTest, TransceiverStateUpdateModeSetDescription) {
+TEST_F(RTCRtpTransceiverImplTest, TransceiverStateUpdateModeSetDescription) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
   auto webrtc_sender =
@@ -400,8 +401,8 @@
   modified_transceiver_state.Initialize();
 
   // Construct a transceiver from the initial state.
-  RTCRtpTransceiver transceiver(peer_connection_.get(), track_map_,
-                                std::move(initial_transceiver_state));
+  RTCRtpTransceiverImpl transceiver(peer_connection_.get(), track_map_,
+                                    std::move(initial_transceiver_state));
   // Setting the state with TransceiverStateUpdateMode::kSetDescription should
   // make the transceiver state up-to-date, except leaving
   // "transceiver.direction" and "transceiver.sender.track" unmodified.
@@ -422,4 +423,4 @@
             webrtc::RtpTransceiverDirection::kSendRecv);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.cc b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
similarity index 95%
rename from content/renderer/media/webrtc/transceiver_state_surfacer.cc
rename to third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
index 3f1fca6..3d996d8 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.cc
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
+#include "third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h"
 
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
 #include "third_party/webrtc/api/sctp_transport_interface.h"
 
-namespace content {
+namespace blink {
 
 TransceiverStateSurfacer::TransceiverStateSurfacer(
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
@@ -75,7 +75,7 @@
 
   for (auto& webrtc_transceiver : webrtc_transceivers) {
     // Create the sender state.
-    base::Optional<RtpSenderState> sender_state;
+    base::Optional<blink::RtpSenderState> sender_state;
     auto webrtc_sender = webrtc_transceiver->sender();
     if (webrtc_sender) {
       std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
@@ -91,7 +91,7 @@
             track_adapter_map->GetLocalTrackAdapter(webrtc_sender->track());
         CHECK(sender_track_ref);
       }
-      sender_state = RtpSenderState(
+      sender_state = blink::RtpSenderState(
           main_task_runner_, signaling_task_runner_, webrtc_sender.get(),
           std::move(sender_track_ref), webrtc_sender->stream_ids());
     }
@@ -113,7 +113,7 @@
           std::move(receiver_track_ref), std::move(receiver_stream_ids));
     }
     // Create the transceiver state.
-    transceiver_states_.push_back(RtpTransceiverState(
+    transceiver_states_.push_back(blink::RtpTransceiverState(
         main_task_runner_, signaling_task_runner_, webrtc_transceiver.get(),
         std::move(sender_state), std::move(receiver_state),
         blink::ToBaseOptional(webrtc_transceiver->mid()),
@@ -131,7 +131,8 @@
   return sctp_transport_snapshot_;
 }
 
-std::vector<RtpTransceiverState> TransceiverStateSurfacer::ObtainStates() {
+std::vector<blink::RtpTransceiverState>
+TransceiverStateSurfacer::ObtainStates() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   DCHECK(is_initialized_);
   for (auto& transceiver_state : transceiver_states_)
@@ -236,4 +237,4 @@
   NOTIMPLEMENTED();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
similarity index 97%
rename from content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
rename to third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
index 07fb7f4..ea08fe2 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
@@ -2,18 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
+#include "third_party/blink/public/web/modules/peerconnection/transceiver_state_surfacer.h"
 
 #include <memory>
 #include <tuple>
 
 #include "base/bind.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/test/task_environment.h"
-#include "content/child/child_process.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
@@ -28,7 +26,10 @@
 using testing::AnyNumber;
 using testing::Return;
 
-namespace content {
+namespace blink {
+
+// To avoid name collision on jumbo builds.
+namespace transceiver_state_surfacer_test {
 
 class MockSctpTransport : public webrtc::SctpTransportInterface {
  public:
@@ -271,8 +272,6 @@
     run_loop->Quit();
   }
 
-  base::test::TaskEnvironment task_environment_;
-
  protected:
   scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
   std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
@@ -385,4 +384,5 @@
   ObtainStatesAndExpectInitialized(webrtc_transceiver);
 }
 
-}  // namespace content
+}  // namespace transceiver_state_surfacer_test
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/DEPS b/third_party/blink/renderer/modules/xr/DEPS
index a66627d..ab86482 100644
--- a/third_party/blink/renderer/modules/xr/DEPS
+++ b/third_party/blink/renderer/modules/xr/DEPS
@@ -2,6 +2,7 @@
     "+mojo/public/cpp/bindings/binding.h",
     "+mojo/public/cpp/system/platform_handle.h",
     "+device/vr/public/mojom/vr_service.mojom-blink.h",
+    "+device/vr/public/mojom/vr_service.mojom-blink-forward.h",
     "+gpu/command_buffer/client/gles2_interface.h",
     "+gpu/command_buffer/common/mailbox_holder.h",
     "+services/metrics/public/cpp/ukm_builders.h",
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index 516b004..6d8c219 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/optional.h"
-#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_world_information.h b/third_party/blink/renderer/modules/xr/xr_world_information.h
index cbe0246..bd03a3a3 100644
--- a/third_party/blink/renderer/modules/xr/xr_world_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_world_information.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WORLD_INFORMATION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WORLD_INFORMATION_H_
 
-#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
 #include "third_party/blink/renderer/modules/xr/xr_plane.h"
 #include "third_party/blink/renderer/modules/xr/xr_plane_set.h"
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 158e3c8..7b626c23 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -401,6 +401,7 @@
     "bindings/callback_method_retriever.cc",
     "bindings/callback_method_retriever.h",
     "bindings/custom_wrappable.h",
+    "bindings/dom_data_store.cc",
     "bindings/dom_data_store.h",
     "bindings/dom_wrapper_world.cc",
     "bindings/dom_wrapper_world.h",
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
index 2c4517f..81e8266 100644
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/platform/audio/push_pull_fifo.h"
 
+#include <algorithm>
 #include <memory>
+
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
@@ -207,7 +209,8 @@
       : 0;
 }
 
-const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() const {
+const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() {
+  MutexLocker locker(lock_);
   return {length(),     NumberOfChannels(), frames_available_, index_read_,
           index_write_, overflow_count_,    underflow_count_};
 }
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo.h b/third_party/blink/renderer/platform/audio/push_pull_fifo.h
index a86ad7e..cf7b2c3 100644
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo.h
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo.h
@@ -67,15 +67,21 @@
   size_t Pull(AudioBus* output_bus, size_t frames_requested);
 
   size_t length() const { return fifo_length_; }
-  unsigned NumberOfChannels() const { return fifo_bus_->NumberOfChannels(); }
+  unsigned NumberOfChannels() const {
+    lock_.AssertAcquired();
+    return fifo_bus_->NumberOfChannels();
+  }
 
   // TODO(hongchan): For single thread unit test only. Consider refactoring.
-  AudioBus* GetFIFOBusForTest() const { return fifo_bus_.get(); }
+  AudioBus* GetFIFOBusForTest() {
+    MutexLocker locker(lock_);
+    return fifo_bus_.get();
+  }
 
   // For single thread unit test only. Get the current configuration that
   // consists of FIFO length, number of channels, read/write index position and
   // under/overflow count.
-  const PushPullFIFOStateForTest GetStateForTest() const;
+  const PushPullFIFOStateForTest GetStateForTest();
 
  private:
   // The size of the FIFO.
@@ -86,13 +92,12 @@
   unsigned overflow_count_ = 0;
   unsigned underflow_count_ = 0;
 
-  // This lock protects variables below.
   Mutex lock_;
   // The number of frames in the FIFO actually available for pulling.
-  size_t frames_available_ = 0;
-  size_t index_read_ = 0;
-  size_t index_write_ = 0;
-  scoped_refptr<AudioBus> fifo_bus_;
+  size_t frames_available_ GUARDED_BY(lock_) = 0;
+  size_t index_read_ GUARDED_BY(lock_) = 0;
+  size_t index_write_ GUARDED_BY(lock_) = 0;
+  scoped_refptr<AudioBus> fifo_bus_ GUARDED_BY(lock_);
 
   DISALLOW_COPY_AND_ASSIGN(PushPullFIFO);
 };
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
index abdc15b..1e5db219 100644
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
@@ -30,7 +30,8 @@
  public:
   FIFOClient(PushPullFIFO* fifo, size_t bus_length, size_t jitter_range_ms)
       : fifo_(fifo),
-        bus_(AudioBus::Create(fifo->NumberOfChannels(), bus_length)),
+        bus_(AudioBus::Create(fifo->GetStateForTest().number_of_channels,
+                              bus_length)),
         client_thread_(Platform::Current()->CreateThread(
             ThreadCreationParams(WebThreadType::kTestThread)
                 .SetThreadNameForTest("FIFOClientThread"))),
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.cc b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
new file mode 100644
index 0000000..5e10e70
--- /dev/null
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
@@ -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.
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+
+namespace blink {
+
+DOMDataStore::DOMDataStore(v8::Isolate* isolate, bool is_main_world)
+    : is_main_world_(is_main_world) {}
+
+void DOMDataStore::Dispose() {
+  for (const auto& it : wrapper_map_) {
+    // Explicitly reset references so that a following V8 GC will not find them
+    // and treat them as roots. There's optimizations (see
+    // EmbedderHeapTracer::IsRootForNonTracingGC) that would not treat them as
+    // roots and then Blink would not be able to find and remove them from a DOM
+    // world. Explicitly resetting on disposal avoids that problem
+    it.value->ref.Clear();
+  }
+}
+
+void DOMDataStore::WrappedReference::Trace(Visitor* visitor) {
+  visitor->Trace(ref);
+}
+
+void DOMDataStore::Trace(Visitor* visitor) {
+  visitor->Trace(wrapper_map_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h
index 166841fc..e582ad68 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.h
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -48,9 +48,7 @@
 // wrappers and provides an API to perform common operations with this map and
 // manage wrappers in a single world. Each world (DOMWrapperWorld) holds a
 // single map instance to hold wrappers only for that world.
-class DOMDataStore {
-  USING_FAST_MALLOC(DOMDataStore);
-
+class DOMDataStore final : public GarbageCollected<DOMDataStore> {
  public:
   static DOMDataStore& Current(v8::Isolate* isolate) {
     return DOMWrapperWorld::Current(isolate).DomDataStore();
@@ -110,20 +108,17 @@
     return Current(isolate).ContainsWrapper(object);
   }
 
-  DOMDataStore(v8::Isolate* isolate, bool is_main_world)
-      : is_main_world_(is_main_world) {
-    // We never use |wrapper_map_| when it's the main world.
-    if (!is_main_world) {
-      wrapper_map_.emplace();
-    }
-  }
+  DOMDataStore(v8::Isolate* isolate, bool is_main_world);
+
+  // Clears all references.
+  void Dispose();
 
   v8::Local<v8::Object> Get(ScriptWrappable* object, v8::Isolate* isolate) {
     if (is_main_world_)
       return object->MainWorldWrapper(isolate);
-    auto it = wrapper_map_->find(object);
-    if (it != wrapper_map_->end())
-      return it->value.NewLocal(isolate);
+    auto it = wrapper_map_.find(object);
+    if (it != wrapper_map_.end())
+      return it->value->ref.NewLocal(isolate);
     return v8::Local<v8::Object>();
   }
 
@@ -136,41 +131,40 @@
     if (is_main_world_)
       return object->SetWrapper(isolate, wrapper_type_info, wrapper);
 
-    auto result = wrapper_map_->insert(object, Value(isolate, wrapper));
+    auto result = wrapper_map_.insert(
+        object, MakeGarbageCollected<WrappedReference>(isolate, wrapper));
     if (LIKELY(result.is_new_entry)) {
-      wrapper_type_info->ConfigureWrapper(&result.stored_value->value.Get());
-      result.stored_value->value.Get().SetFinalizationCallback(
-          this, RemoveEntryFromMap);
+      wrapper_type_info->ConfigureWrapper(
+          &result.stored_value->value->ref.Get());
     } else {
-      DCHECK(!result.stored_value->value.IsEmpty());
-      wrapper = result.stored_value->value.NewLocal(isolate);
+      DCHECK(!result.stored_value->value->ref.IsEmpty());
+      wrapper = result.stored_value->value->ref.NewLocal(isolate);
     }
     return result.is_new_entry;
   }
 
-  void Trace(const ScriptWrappable* script_wrappable, Visitor* visitor) {
-    DCHECK(wrapper_map_);
-    auto it =
-        wrapper_map_->find(const_cast<ScriptWrappable*>(script_wrappable));
-    if (it != wrapper_map_->end()) {
-      visitor->Trace(
-          static_cast<TraceWrapperV8Reference<v8::Object>&>(it->value));
-    }
-  }
-
-  // Dissociates a wrapper, if any, from |script_wrappable|.
-  void UnsetWrapperIfAny(ScriptWrappable* script_wrappable) {
+  bool UnsetSpecificWrapperIfSet(
+      ScriptWrappable* object,
+      const v8::TracedReference<v8::Object>& handle) {
     DCHECK(!is_main_world_);
-    wrapper_map_->erase(script_wrappable);
+    const auto& it = wrapper_map_.find(object);
+    if (it != wrapper_map_.end()) {
+      if (it->value->ref.Get() == handle) {
+        it->value->ref.Clear();
+        wrapper_map_.erase(it);
+        return true;
+      }
+    }
+    return false;
   }
 
   bool SetReturnValueFrom(v8::ReturnValue<v8::Value> return_value,
                           ScriptWrappable* object) {
     if (is_main_world_)
       return object->SetReturnValue(return_value);
-    auto it = wrapper_map_->find(object);
-    if (it != wrapper_map_->end()) {
-      return_value.Set(it->value.Get());
+    auto it = wrapper_map_.find(object);
+    if (it != wrapper_map_.end()) {
+      return_value.Set(it->value->ref.Get());
       return true;
     }
     return false;
@@ -179,10 +173,12 @@
   bool ContainsWrapper(const ScriptWrappable* object) {
     if (is_main_world_)
       return object->ContainsWrapper();
-    return wrapper_map_->find(const_cast<ScriptWrappable*>(object)) !=
-           wrapper_map_->end();
+    return wrapper_map_.find(const_cast<ScriptWrappable*>(object)) !=
+           wrapper_map_.end();
   }
 
+  virtual void Trace(Visitor*);
+
  private:
   // We can use a wrapper stored in a ScriptWrappable when we're in the main
   // world.  This method does the fast check if we're in the main world. If this
@@ -203,58 +199,25 @@
     return wrappable->IsEqualTo(holder);
   }
 
-  static void RemoveEntryFromMap(const v8::WeakCallbackInfo<void>& data) {
-    DOMDataStore* store = reinterpret_cast<DOMDataStore*>(data.GetParameter());
-    ScriptWrappable* key = reinterpret_cast<ScriptWrappable*>(
-        data.GetInternalField(kV8DOMWrapperObjectIndex));
-    const auto& it = store->wrapper_map_->find(key);
-    DCHECK_NE(store->wrapper_map_->end(), it);
-    store->wrapper_map_->erase(it);
-    WrapperTypeInfo::WrapperDestroyed();
-  }
-
-  // Specialization of TraceWrapperV8Reference to avoid write barriers on move
-  // operations. This is correct as entries are never moved out of the storage
-  // but only moved for rehashing purposes.
-  //
-  // We need to avoid write barriers to allow resizing of the hashmap backing
-  // during V8 garbage collections. The resize is triggered when entries are
-  // removed which happens in a phase where V8 prohibits any API calls. To work
-  // around that we just don't emit write barriers for moving.
-  class DOMWorldWrapperReference : public TraceWrapperV8Reference<v8::Object> {
+  // Wrapper around TraceWrapperV8Reference to allow use in ephemeron hash map
+  // below.
+  class PLATFORM_EXPORT WrappedReference final
+      : public GarbageCollected<WrappedReference> {
    public:
-    DOMWorldWrapperReference() = default;
-    // Regular write barrier for constructor is emitted by
-    // TraceWrapperV8Reference.
-    DOMWorldWrapperReference(v8::Isolate* isolate, v8::Local<v8::Object> handle)
-        : TraceWrapperV8Reference(isolate, handle) {}
+    WrappedReference() = default;
+    WrappedReference(v8::Isolate* isolate, v8::Local<v8::Object> handle)
+        : ref(isolate, handle) {}
 
-    ~DOMWorldWrapperReference() {
-      // Destruction of a reference should clear it immediately.
-      Clear();
-    }
+    virtual void Trace(Visitor*);
 
-    // Move support without write barrier.
-    DOMWorldWrapperReference(DOMWorldWrapperReference&& other)
-        : TraceWrapperV8Reference() {
-      handle_ = std::move(other.handle_);
-    }
-    DOMWorldWrapperReference& operator=(DOMWorldWrapperReference&& rhs) {
-      handle_ = std::move(rhs.handle_);
-      return *this;
-    }
+    TraceWrapperV8Reference<v8::Object> ref;
   };
 
-  // UntracedMember is safe here as the map is not keeping ScriptWrappable alive
-  // but merely adding additional edges from Blink to V8.
-  using Key = UntracedMember<ScriptWrappable>;
-  using Value = DOMWorldWrapperReference;
-  using MapType = WTF::HashMap<Key, Value>;
-
   bool is_main_world_;
-  GC_PLUGIN_IGNORE(
-      "Avoid dispatch on Visitor by looking up value in DOMDataStore::Trace.")
-  base::Optional<MapType> wrapper_map_;
+  // Ephemeron map: WrappedReference will be kept alive as long as
+  // ScriptWrappable is alive.
+  HeapHashMap<WeakMember<ScriptWrappable>, Member<WrappedReference>>
+      wrapper_map_;
 
   DISALLOW_COPY_AND_ASSIGN(DOMDataStore);
 };
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
index 23bb6ef..a48be9f2 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
@@ -73,7 +73,8 @@
                                  int32_t world_id)
     : world_type_(world_type),
       world_id_(world_id),
-      dom_data_store_(std::make_unique<DOMDataStore>(isolate, IsMainWorld())) {
+      dom_data_store_(
+          MakeGarbageCollected<DOMDataStore>(isolate, IsMainWorld())) {
   switch (world_type_) {
     case WorldType::kMain:
       // The main world is managed separately from worldMap(). See worldMap().
@@ -109,16 +110,6 @@
     worlds.push_back(world);
 }
 
-void DOMWrapperWorld::Trace(const ScriptWrappable* script_wrappable,
-                            Visitor* visitor) {
-  // Marking for worlds other than the main world.
-  for (DOMWrapperWorld* world : GetWorldMap().Values()) {
-    DOMDataStore& data_store = world->DomDataStore();
-    if (data_store.ContainsWrapper(script_wrappable))
-      data_store.Trace(script_wrappable, visitor);
-  }
-}
-
 DOMWrapperWorld::~DOMWrapperWorld() {
   DCHECK(!IsMainWorld());
   if (IsMainThread())
@@ -131,7 +122,8 @@
 }
 
 void DOMWrapperWorld::Dispose() {
-  dom_data_store_.reset();
+  dom_data_store_->Dispose();
+  dom_data_store_.Clear();
   DCHECK(GetWorldMap().Contains(world_id_));
   GetWorldMap().erase(world_id_);
 }
@@ -238,17 +230,6 @@
   return kInvalidWorldId;
 }
 
-void DOMWrapperWorld::DissociateDOMWindowWrappersInAllWorlds(
-    ScriptWrappable* script_wrappable) {
-  DCHECK(script_wrappable);
-  DCHECK(IsMainThread());
-
-  script_wrappable->UnsetWrapperIfAny();
-
-  for (auto*& world : GetWorldMap().Values())
-    world->DomDataStore().UnsetWrapperIfAny(script_wrappable);
-}
-
 bool DOMWrapperWorld::HasWrapperInAnyWorldInMainThread(
     ScriptWrappable* script_wrappable) {
   DCHECK(IsMainThread());
@@ -263,4 +244,16 @@
   return false;
 }
 
+// static
+bool DOMWrapperWorld::UnsetNonMainWorldWrapperIfSet(
+    ScriptWrappable* object,
+    const v8::TracedReference<v8::Object>& handle) {
+  for (DOMWrapperWorld* world : GetWorldMap().Values()) {
+    DOMDataStore& data_store = world->DomDataStore();
+    if (data_store.UnsetSpecificWrapperIfSet(object, handle))
+      return true;
+  }
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
index 5b6ffba..75dde53 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -37,6 +37,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/web_isolated_world_ids.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
@@ -104,9 +105,6 @@
   static void AllWorldsInCurrentThread(
       Vector<scoped_refptr<DOMWrapperWorld>>& worlds);
 
-  // Traces wrappers corresponding to the ScriptWrappable in DOM data stores.
-  static void Trace(const ScriptWrappable*, Visitor*);
-
   static DOMWrapperWorld& World(v8::Local<v8::Context> context) {
     return ScriptState::From(context)->World();
   }
@@ -142,7 +140,16 @@
   int GetWorldId() const { return world_id_; }
   DOMDataStore& DomDataStore() const { return *dom_data_store_; }
 
+  // Clear the reference pointing from |object| to |handle| in any world.
+  static bool UnsetSpecificWrapperIfSet(
+      ScriptWrappable* object,
+      const v8::TracedReference<v8::Object>& handle);
+
  private:
+  static bool UnsetNonMainWorldWrapperIfSet(
+      ScriptWrappable* object,
+      const v8::TracedReference<v8::Object>& handle);
+
   DOMWrapperWorld(v8::Isolate*, WorldType, int32_t world_id);
 
   static unsigned number_of_non_main_worlds_in_main_thread_;
@@ -152,35 +159,23 @@
   // out of DOMWrapperWorld.
   static int GenerateWorldIdForType(WorldType);
 
-  // Dissociates all wrappers in all worlds associated with |script_wrappable|.
-  //
-  // Do not use this function except for DOMWindow.  Only DOMWindow needs to
-  // dissociate wrappers from the ScriptWrappable because of the following two
-  // reasons.
-  //
-  // Reason 1) Case of the main world
-  // A DOMWindow may be collected by Blink GC *before* V8 GC collects the
-  // wrapper because the wrapper object associated with a DOMWindow is a global
-  // proxy, which remains after navigations.  We don't want V8 GC to reset the
-  // weak persistent handle to a wrapper within the DOMWindow
-  // (ScriptWrappable::main_world_wrapper_) *after* Blink GC collects the
-  // DOMWindow because it's use-after-free.  Thus, we need to dissociate the
-  // wrapper in advance.
-  //
-  // Reason 2) Case of isolated worlds
-  // As same, a DOMWindow may be collected before the wrapper gets collected.
-  // A DOMWrapperMap supports mapping from ScriptWrappable* to v8::Global<T>,
-  // and we don't want to leave an entry of an already-dead DOMWindow* to the
-  // persistent handle for the global proxy object, especially considering that
-  // the address to the already-dead DOMWindow* may be re-used.
-  friend class DOMWindow;
-  static void DissociateDOMWindowWrappersInAllWorlds(ScriptWrappable*);
-
   const WorldType world_type_;
   const int32_t world_id_;
-  std::unique_ptr<DOMDataStore> dom_data_store_;
+  Persistent<DOMDataStore> dom_data_store_;
 };
 
+// static
+inline bool DOMWrapperWorld::UnsetSpecificWrapperIfSet(
+    ScriptWrappable* object,
+    const v8::TracedReference<v8::Object>& handle) {
+  // Fast path for main world.
+  if (object->UnsetMainWorldWrapperIfSet(handle))
+    return true;
+
+  // Slow path: |object| may point to |handle| in any non-main DOM world.
+  return DOMWrapperWorld::UnsetNonMainWorldWrapperIfSet(object, handle);
+}
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable.cc b/third_party/blink/renderer/platform/bindings/script_wrappable.cc
index 5d4d7bd..3f344d0 100644
--- a/third_party/blink/renderer/platform/bindings/script_wrappable.cc
+++ b/third_party/blink/renderer/platform/bindings/script_wrappable.cc
@@ -40,7 +40,6 @@
 
 void ScriptWrappable::Trace(Visitor* visitor) {
   visitor->Trace(main_world_wrapper_);
-  DOMWrapperWorld::Trace(this, visitor);
 }
 
 const char* ScriptWrappable::NameInHeapSnapshot() const {
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable.h b/third_party/blink/renderer/platform/bindings/script_wrappable.h
index 75c6845..25475561 100644
--- a/third_party/blink/renderer/platform/bindings/script_wrappable.h
+++ b/third_party/blink/renderer/platform/bindings/script_wrappable.h
@@ -128,14 +128,6 @@
     return true;
   }
 
-  // Dissociates the wrapper, if any, from this instance.
-  void UnsetWrapperIfAny() {
-    if (ContainsWrapper()) {
-      main_world_wrapper_.Get().Reset();
-      WrapperTypeInfo::WrapperDestroyed();
-    }
-  }
-
   bool IsEqualTo(const v8::Local<v8::Object>& other) const {
     return main_world_wrapper_.Get() == other;
   }
@@ -151,16 +143,14 @@
   ScriptWrappable() = default;
 
  private:
-  // These classes are exceptionally allowed to use MainWorldWrapper().
-  friend class DOMDataStore;
-  friend class HeapSnaphotWrapperVisitor;
-  friend class V8HiddenValue;
-  friend class V8PrivateProperty;
-
   v8::Local<v8::Object> MainWorldWrapper(v8::Isolate* isolate) const {
     return main_world_wrapper_.NewLocal(isolate);
   }
 
+  // Clear the main world wrapper if it is set to |handle|.
+  bool UnsetMainWorldWrapperIfSet(
+      const v8::TracedReference<v8::Object>& handle);
+
   static_assert(
       std::is_trivially_destructible<
           TraceWrapperV8Reference<v8::Object>>::value,
@@ -168,9 +158,26 @@
 
   TraceWrapperV8Reference<v8::Object> main_world_wrapper_;
 
+  // These classes are exceptionally allowed to directly interact with the main
+  // world wrapper.
+  friend class DOMDataStore;
+  friend class DOMWrapperWorld;
+  friend class HeapSnaphotWrapperVisitor;
+  friend class V8HiddenValue;
+  friend class V8PrivateProperty;
+
   DISALLOW_COPY_AND_ASSIGN(ScriptWrappable);
 };
 
+inline bool ScriptWrappable::UnsetMainWorldWrapperIfSet(
+    const v8::TracedReference<v8::Object>& handle) {
+  if (main_world_wrapper_.Get() == handle) {
+    main_world_wrapper_.Clear();
+    return true;
+  }
+  return false;
+}
+
 // Defines |GetWrapperTypeInfo| virtual method which returns the WrapperTypeInfo
 // of the instance. Also declares a static member of type WrapperTypeInfo, of
 // which the definition is given by the IDL code generator.
diff --git a/third_party/blink/renderer/platform/blob/blob_bytes_provider.h b/third_party/blink/renderer/platform/blob/blob_bytes_provider.h
index 5a65699..59b019a6 100644
--- a/third_party/blink/renderer/platform/blob/blob_bytes_provider.h
+++ b/third_party/blink/renderer/platform/blob/blob_bytes_provider.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_BYTES_PROVIDER_H_
 
 #include "base/sequenced_task_runner.h"
-#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 
diff --git a/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc b/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
index 308b2cf..9d1960e 100644
--- a/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
+++ b/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
@@ -65,7 +65,7 @@
       break;
 
     // See if the current size is "any".
-    if (sizes_string.FindIgnoringCase("any", i) == i &&
+    if (sizes_string.Substring(i, 3).StartsWithIgnoringCase("any") &&
         (i + 3 == length || IsWhitespace(sizes_string[i + 3]))) {
       icon_sizes.push_back(WebSize(0, 0));
       i = i + 3;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
index b18d26a5..9dcc0d50 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
@@ -34,7 +34,8 @@
         is_after_expansion_(false) {}
 
   const TextContainerType& Text() const { return text_; }
-  float LetterSpacing() const { return letter_spacing_; }
+  float LetterSpacing() const { return has_spacing_ ? letter_spacing_ : .0f; }
+  float WordSpacing() const { return has_spacing_ ? word_spacing_ : .0f; }
   bool HasSpacing() const { return has_spacing_; }
   bool HasExpansion() const { return expansion_opportunity_count_; }
 
diff --git a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h
index e50bc818..254eba7 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h
+++ b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h
@@ -35,7 +35,7 @@
 #include <unicode/uscript.h>
 
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink.h"
+#include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/graphics/image_decoding_store.cc b/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
index 30ec2ba..080f1e7a 100644
--- a/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
+++ b/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
@@ -240,6 +240,7 @@
 void ImageDecodingStore::InsertCacheInternal(std::unique_ptr<T> cache_entry,
                                              U* cache_map,
                                              V* identifier_map) {
+  mutex_.AssertAcquired();
   const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
   heap_memory_usage_in_bytes_ += cache_entry_bytes;
 
@@ -266,6 +267,7 @@
     U* cache_map,
     V* identifier_map,
     Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
+  mutex_.AssertAcquired();
   DCHECK_EQ(cache_entry->UseCount(), 0);
 
   const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
@@ -307,6 +309,7 @@
     V* identifier_map,
     const ImageFrameGenerator* generator,
     Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
+  mutex_.AssertAcquired();
   typename V::iterator iter = identifier_map->find(generator);
   if (iter == identifier_map->end())
     return;
@@ -327,6 +330,7 @@
 
 void ImageDecodingStore::RemoveFromCacheListInternal(
     const Vector<std::unique_ptr<CacheEntry>>& deletion_list) {
+  mutex_.AssertAcquired();
   for (size_t i = 0; i < deletion_list.size(); ++i)
     ordered_cache_list_.Remove(deletion_list[i].get());
 }
diff --git a/third_party/blink/renderer/platform/graphics/image_decoding_store.h b/third_party/blink/renderer/platform/graphics/image_decoding_store.h
index 8ef570c..99d17c1 100644
--- a/third_party/blink/renderer/platform/graphics/image_decoding_store.h
+++ b/third_party/blink/renderer/platform/graphics/image_decoding_store.h
@@ -322,35 +322,31 @@
   // This is used for eviction of old entries.
   // Head of this list is the least recently used cache entry.
   // Tail of this list is the most recently used cache entry.
-  DoublyLinkedList<CacheEntry> ordered_cache_list_;
+  DoublyLinkedList<CacheEntry> ordered_cache_list_ GUARDED_BY(mutex_);
 
   // A lookup table for all decoder cache objects. Owns all decoder cache
   // objects.
   typedef HashMap<DecoderCacheKey, std::unique_ptr<DecoderCacheEntry>>
       DecoderCacheMap;
-  DecoderCacheMap decoder_cache_map_;
+  DecoderCacheMap decoder_cache_map_ GUARDED_BY(mutex_);
 
   // A lookup table to map ImageFrameGenerator to all associated
   // decoder cache keys.
   typedef HashSet<DecoderCacheKey> DecoderCacheKeySet;
   typedef HashMap<const ImageFrameGenerator*, DecoderCacheKeySet>
       DecoderCacheKeyMap;
-  DecoderCacheKeyMap decoder_cache_key_map_;
+  DecoderCacheKeyMap decoder_cache_key_map_ GUARDED_BY(mutex_);
 
-  size_t heap_limit_in_bytes_;
-  size_t heap_memory_usage_in_bytes_;
+  size_t heap_limit_in_bytes_ GUARDED_BY(mutex_);
+  size_t heap_memory_usage_in_bytes_ GUARDED_BY(mutex_);
 
   // A listener to global memory pressure events.
   base::MemoryPressureListener memory_pressure_listener_;
 
-  // Protect concurrent access to these members:
-  //   m_orderedCacheList
-  //   m_decoderCacheMap and all CacheEntrys stored in it
-  //   m_decoderCacheKeyMap
-  //   m_heapLimitInBytes
-  //   m_heapMemoryUsageInBytes
-  // This mutex also protects calls to underlying skBitmap's
-  // lockPixels()/unlockPixels() as they are not threadsafe.
+  // Also protects:
+  // - the CacheEntry in |decoder_cache_map_|.
+  // - calls to underlying skBitmap's LockPixels()/UnlockPixels() as they are
+  //   not threadsafe.
   Mutex mutex_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDecodingStore);
@@ -358,4 +354,4 @@
 
 }  // namespace blink
 
-#endif
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DECODING_STORE_H_
diff --git a/third_party/blink/renderer/platform/graphics/image_frame_generator.h b/third_party/blink/renderer/platform/graphics/image_frame_generator.h
index 036bca1..59dfaac 100644
--- a/third_party/blink/renderer/platform/graphics/image_frame_generator.h
+++ b/third_party/blink/renderer/platform/graphics/image_frame_generator.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_FRAME_GENERATOR_H_
 
 #include <memory>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
@@ -150,13 +151,11 @@
   const bool is_multi_frame_;
   const Vector<SkISize> supported_sizes_;
 
-  // Prevents concurrent access to all variables below.
   mutable Mutex generator_mutex_;
-
-  bool decode_failed_ = false;
-  bool yuv_decoding_failed_ = false;
-  size_t frame_count_ = 0u;
-  Vector<bool> has_alpha_;
+  bool decode_failed_ GUARDED_BY(generator_mutex_) = false;
+  bool yuv_decoding_failed_ GUARDED_BY(generator_mutex_) = false;
+  size_t frame_count_ GUARDED_BY(generator_mutex_) = 0u;
+  Vector<bool> has_alpha_ GUARDED_BY(generator_mutex_);
 
   struct ClientMutex {
     int ref_count = 0;
@@ -170,7 +169,7 @@
           std::unique_ptr<ClientMutex>,
           WTF::IntHash<cc::PaintImage::GeneratorClientId>,
           WTF::UnsignedWithZeroKeyHashTraits<cc::PaintImage::GeneratorClientId>>
-      mutex_map_;
+      mutex_map_ GUARDED_BY(generator_mutex_);
 
   std::unique_ptr<ImageDecoderFactory> image_decoder_factory_;
 
@@ -179,4 +178,4 @@
 
 }  // namespace blink
 
-#endif
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_FRAME_GENERATOR_H_
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
index 4ddae51..220e763a2 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
@@ -14,7 +14,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
+#include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink-forward.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
index c4b5172..649b6f3 100644
--- a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
+++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/platform/heap/unified_heap_controller.h"
 
+#include "base/macros.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
@@ -143,9 +145,7 @@
   return is_tracing_done_;
 }
 
-namespace {
-
-bool IsRootForNonTracingGCInternal(
+bool UnifiedHeapController::IsRootForNonTracingGC(
     const v8::TracedReference<v8::Value>& handle) {
   const uint16_t class_id = handle.WrapperClassId();
   // Stand-alone reference or kCustomWrappableId. Keep as root as
@@ -168,8 +168,6 @@
   return false;
 }
 
-}  // namespace
-
 void UnifiedHeapController::ResetHandleInNonTracingGC(
     const v8::TracedReference<v8::Value>& handle) {
   const uint16_t class_id = handle.WrapperClassId();
@@ -180,12 +178,11 @@
     return;
 
   const v8::TracedReference<v8::Object>& traced = handle.As<v8::Object>();
-  ToScriptWrappable(traced)->UnsetWrapperIfAny();
-}
-
-bool UnifiedHeapController::IsRootForNonTracingGC(
-    const v8::TracedReference<v8::Value>& handle) {
-  return IsRootForNonTracingGCInternal(handle);
+  bool success = DOMWrapperWorld::UnsetSpecificWrapperIfSet(
+      ToScriptWrappable(traced), traced);
+  // Since V8 found a handle, Blink needs to find it as well when trying to
+  // remove it.
+  CHECK(success);
 }
 
 bool UnifiedHeapController::IsRootForNonTracingGC(
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_reader.cc b/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
index 5b457f0f..3d22cf6 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
+++ b/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
 
+#include <utility>
+
 #include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
@@ -20,7 +22,7 @@
 // Interface for ImageDecoder to read a SharedBuffer.
 class SharedBufferSegmentReader final : public SegmentReader {
  public:
-  SharedBufferSegmentReader(scoped_refptr<SharedBuffer>);
+  explicit SharedBufferSegmentReader(scoped_refptr<SharedBuffer>);
   size_t size() const override;
   size_t GetSomeData(const char*& data, size_t position) const override;
   sk_sp<SkData> GetAsSkData() const override;
@@ -66,7 +68,7 @@
 // Interface for ImageDecoder to read an SkData.
 class DataSegmentReader final : public SegmentReader {
  public:
-  DataSegmentReader(sk_sp<SkData>);
+  explicit DataSegmentReader(sk_sp<SkData>);
   size_t size() const override;
   size_t GetSomeData(const char*& data, size_t position) const override;
   sk_sp<SkData> GetAsSkData() const override;
@@ -101,7 +103,7 @@
 
 class ROBufferSegmentReader final : public SegmentReader {
  public:
-  ROBufferSegmentReader(sk_sp<SkROBuffer>);
+  explicit ROBufferSegmentReader(sk_sp<SkROBuffer>);
 
   size_t size() const override;
   size_t GetSomeData(const char*& data, size_t position) const override;
@@ -109,11 +111,10 @@
 
  private:
   sk_sp<SkROBuffer> ro_buffer_;
-  // Protects access to mutable fields.
   mutable Mutex read_mutex_;
   // Position of the first char in the current block of iter_.
-  mutable size_t position_of_block_;
-  mutable SkROBuffer::Iter iter_;
+  mutable size_t position_of_block_ GUARDED_BY(read_mutex_);
+  mutable SkROBuffer::Iter iter_ GUARDED_BY(read_mutex_);
 
   DISALLOW_COPY_AND_ASSIGN(ROBufferSegmentReader);
 };
diff --git a/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h b/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
index de9f8d5..9bab204 100644
--- a/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
+++ b/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h
@@ -69,7 +69,7 @@
   static bool is_low_end_device_;
 
   HeapHashSet<WeakMember<MemoryPressureListener>> clients_;
-  HashSet<Thread*> threads_;
+  HashSet<Thread*> threads_ GUARDED_BY(threads_mutex_);
   Mutex threads_mutex_;
 
   DISALLOW_COPY_AND_ASSIGN(MemoryPressureListenerRegistry);
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.cc b/third_party/blink/renderer/platform/loader/cors/cors.cc
index 361b791..e4ac62f 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -12,6 +12,7 @@
 #include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/cors/preflight_cache.h"
 #include "services/network/public/cpp/request_mode.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
diff --git a/third_party/blink/renderer/platform/loader/cors/cors.h b/third_party/blink/renderer/platform/loader/cors/cors.h
index 25ad5e2..84998f9 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors.h
+++ b/third_party/blink/renderer/platform/loader/cors/cors.h
@@ -7,9 +7,9 @@
 
 #include "base/optional.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
-#include "services/network/public/mojom/cors.mojom-blink.h"
-#include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "services/network/public/mojom/cors.mojom-blink-forward.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_http_header_set.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc b/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
index 8d40c95..d428a2f 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
@@ -6,6 +6,7 @@
 
 #include <initializer_list>
 
+#include "services/network/public/mojom/cors.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
diff --git a/third_party/blink/renderer/platform/loader/cors/cors_error_string.h b/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
index fa80650..73dfab95 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
+++ b/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "services/network/public/cpp/cors/cors_error_status.h"
-#include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/cors.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/platform/loader/cors/cors_test.cc b/third_party/blink/renderer/platform/loader/cors/cors_test.cc
index c081795..e68e93c 100644
--- a/third_party/blink/renderer/platform/loader/cors/cors_test.cc
+++ b/third_party/blink/renderer/platform/loader/cors/cors_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_source.cc b/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
index 354e0e0a..9e18929 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
@@ -189,7 +189,10 @@
 }
 
 void MediaStreamSource::Dispose() {
-  audio_consumers_.clear();
+  {
+    MutexLocker locker(audio_consumers_lock_);
+    audio_consumers_.clear();
+  }
   platform_source_.reset();
   constraints_.Reset();
 }
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_source.h b/third_party/blink/renderer/platform/mediastream/media_stream_source.h
index e17e94c..2e4edff 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_source.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_source.h
@@ -135,7 +135,8 @@
   bool requires_consumer_;
   HeapHashSet<WeakMember<Observer>> observers_;
   Mutex audio_consumers_lock_;
-  HashSet<AudioDestinationConsumer*> audio_consumers_;
+  HashSet<AudioDestinationConsumer*> audio_consumers_
+      GUARDED_BY(audio_consumers_lock_);
   std::unique_ptr<WebPlatformMediaStreamSource> platform_source_;
   WebMediaConstraints constraints_;
   WebMediaStreamSource::Capabilities capabilities_;
diff --git a/third_party/blink/renderer/platform/network/network_state_notifier.h b/third_party/blink/renderer/platform/network/network_state_notifier.h
index 30673285..4b686e1 100644
--- a/third_party/blink/renderer/platform/network/network_state_notifier.h
+++ b/third_party/blink/renderer/platform/network/network_state_notifier.h
@@ -362,9 +362,9 @@
   double GetRandomMultiplier(const String& host) const;
 
   mutable Mutex mutex_;
-  NetworkState state_;
-  bool has_override_;
-  NetworkState override_;
+  NetworkState state_ GUARDED_BY(mutex_);
+  bool has_override_ GUARDED_BY(mutex_);
+  NetworkState override_ GUARDED_BY(mutex_);
 
   ObserverListMap connection_observers_;
   ObserverListMap on_line_state_observers_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 33a28b7..dc032029 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1614,7 +1614,6 @@
     },
     {
       name: "TrustedDOMTypes",
-      origin_trial_feature_name: "TrustedDOMTypes",
       status: "experimental",
     },
     {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
index 43d0b66..ceabb45 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
@@ -43,6 +43,7 @@
     case Feature::kRequestedVideoCapturePermission:
     case Feature::kRequestedSensorsPermission:
     case Feature::kRequestedBackgroundWorkPermission:
+    case Feature::kWebLocks:
       return true;
   }
 }
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.h b/third_party/blink/renderer/platform/text/text_break_iterator.h
index 872e42a3..6192e29 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.h
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -216,6 +216,7 @@
     ReleaseIterator();
   }
 
+  const AtomicString& Locale() const { return locale_; }
   void SetLocale(const AtomicString& locale) {
     if (locale == locale_)
       return;
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 4cf58a8..6561ccc 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
@@ -250,6 +250,7 @@
             # cc::Layers.
             'cc::Layer',
             'cc::PictureLayer',
+            'cc::SurfaceLayer',
 
             # cc::Layer helper data structs.
             'cc::ElementId',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c3f818c..6018a197 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -349,8 +349,6 @@
 crbug.com/981719 [ Linux ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ]
 crbug.com/981719 [ Win ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ]
 
-crbug.com/1011453 [ Mac10.10 ] fast/text/trailing-white-space-2.html [ Failure ]
-
 # ====== Paint team owned tests to here ======
 
 crbug.com/922249 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure Pass ]
@@ -1012,8 +1010,11 @@
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-children-height-007.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-children-height-008.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-004.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-007.html [ Failure ]
+crbug.com/988015 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-002.html [ Failure ]
+crbug.com/988015 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-003.html [ Failure ]
+crbug.com/994172 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-007.html [ Failure Crash ]
 crbug.com/924142 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-010.html [ Crash Pass ]
+crbug.com/988015 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-013.html [ Failure ]
 crbug.com/874051 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Failure ]
 crbug.com/874051 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Failure ]
 crbug.com/874051 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-fieldset-003.html [ Failure ]
@@ -1050,7 +1051,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-float-with-margin-top-and-line-after-break.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-float-with-margin-top-and-line-before-break.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-overflow.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-underflow-1.html [ Crash ]
+crbug.com/829028 virtual/layout_ng_experimental/fast/multicol/balance-line-underflow-1.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-underflow-2.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/basic-rtl.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/break-before-first-line-in-first-child.html [ Failure ]
@@ -1108,8 +1109,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-and-insert-block-between-spanners.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-between-spanners.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-column-content-next-to-abspos-between-spanners.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-spanner-after-content.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-spanner-before-content.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-spanner-in-content.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/spanner-after-content-becomes-regular-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/spanner-ancestor-becomes-spanner.html [ Failure ]
@@ -1185,9 +1184,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-padding.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-single-empty-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-with-single-tall-line.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/avoid-column-break-inside.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/balance2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/balance3.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/break-before.html [ Failure ]
 crbug.com/874506 virtual/layout_ng_experimental/fast/multicol/newmulticol/breaks-2-columns-3.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/breaks-2-columns-3-no-balancing.html [ Failure ]
@@ -1240,7 +1236,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/fill-after-spanner-exact-fit.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/fill-after-spanner-extra-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/float.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/height-decrease.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/height-increase.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/in-nested-multicol-with-hard-breaks.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/in-nested-multicol-with-soft-breaks-inside.html [ Failure ]
@@ -2716,12 +2711,13 @@
 crbug.com/1012627 [ Win7 ] external/wpt/css/css-text/line-breaking/line-breaking-021.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
-crbug.com/626703 [ Linux ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
-crbug.com/626703 [ Mac10.10 ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
-crbug.com/626703 [ Retina ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
-crbug.com/626703 [ Win ] external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html [ Failure ]
+crbug.com/626703 external/wpt/webxr/events_referenceSpace_reset.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/clear-site-data/storage.https.html [ Timeout ]
@@ -5795,3 +5791,11 @@
 # Sheriff 2019-10-11
 crbug.com/1012599 external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.sharedworker.html [ Pass Timeout ]
 crbug.com/1012599 virtual/outofblink-cors/external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.sharedworker.html [ Pass Timeout ]
+crbug.com/1013391 http/tests/devtools/a11y-axe-core/basic-a11y-test.js [ Timeout ]
+crbug.com/1013391 http/tests/devtools/a11y-axe-core/performance/landing-page-a11y-test.js [ Timeout ]
+crbug.com/1013391 http/tests/devtools/a11y-axe-core/performance/performance-pane-a11y-test.js [ Timeout ]
+crbug.com/1013391 http/tests/devtools/a11y-axe-core/settings/shortcuts-a11y-test.js [ Timeout ]
+crbug.com/1013523 [ Release ] virtual/cross-origin-embedder-policy/external/wpt/html/cross-origin-embedder-policy/require-corp.https.html [ Failure Pass ]
+crbug.com/1013523 [ Release Linux Mac ] external/wpt/html/cross-origin-embedder-policy/require-corp.https.html [ Failure Pass ]
+crbug.com/1010472 [ Mac Debug ] virtual/disable-deferred-rendering/fast/canvas/color-space/canvas-drawImage-offscreenCanvas.html [ Failure Pass ]
+crbug.com/1010472 [ Mac Debug ] virtual/disable-deferred-rendering/fast/canvas/OffscreenCanvas-placeholder-createImageBitmap.html [ Failure Pass ]
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 d015a627..5fef2d24 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
@@ -31219,6 +31219,30 @@
      {}
     ]
    ],
+   "css/css-align/baseline-rules/grid-item-input-type-number.html": [
+    [
+     "css/css-align/baseline-rules/grid-item-input-type-number.html",
+     [
+      [
+       "/css/css-align/baseline-rules/grid-item-input-type-number-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-align/baseline-rules/grid-item-input-type-text.html": [
+    [
+     "css/css-align/baseline-rules/grid-item-input-type-text.html",
+     [
+      [
+       "/css/css-align/baseline-rules/grid-item-input-type-text-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-align/content-distribution/place-content-shorthand-007.html": [
     [
      "css/css-align/content-distribution/place-content-shorthand-007.html",
@@ -130450,6 +130474,12 @@
    "css/css-align/OWNERS": [
     []
    ],
+   "css/css-align/baseline-rules/grid-item-input-type-number-ref.html": [
+    []
+   ],
+   "css/css-align/baseline-rules/grid-item-input-type-text-ref.html": [
+    []
+   ],
    "css/css-align/content-distribution/place-content-shorthand-007-ref.html": [
     []
    ],
@@ -298448,6 +298478,18 @@
      {}
     ]
    ],
+   "svg/animations/scripted/SVGAnimationElement-exceptions.html": [
+    [
+     "svg/animations/scripted/SVGAnimationElement-exceptions.html",
+     {}
+    ]
+   ],
+   "svg/animations/scripted/SVGAnimationElement-getStartTime.html": [
+    [
+     "svg/animations/scripted/SVGAnimationElement-getStartTime.html",
+     {}
+    ]
+   ],
    "svg/animations/scripted/end-element-on-inactive-element.svg": [
     [
      "svg/animations/scripted/end-element-on-inactive-element.svg",
@@ -359454,6 +359496,22 @@
    "5511d6d68ad0b531cdd31da352e7a78e3a6b85b7",
    "reftest"
   ],
+  "css/css-align/baseline-rules/grid-item-input-type-number-ref.html": [
+   "dc91342cc81d9337ffe88720e430426b18cbb6d7",
+   "support"
+  ],
+  "css/css-align/baseline-rules/grid-item-input-type-number.html": [
+   "9b9735c7be7860f9f02e3df3ebd1b5dfffd7342e",
+   "reftest"
+  ],
+  "css/css-align/baseline-rules/grid-item-input-type-text-ref.html": [
+   "e6d2786c5ef471e56b76ae5042c07dc9d0e1c05f",
+   "support"
+  ],
+  "css/css-align/baseline-rules/grid-item-input-type-text.html": [
+   "526dcbae6a9c3e116557b50d9993f45b1edfbb5f",
+   "reftest"
+  ],
   "css/css-align/baseline-rules/synthesized-baseline-flexbox-001.html": [
    "af2dac4cc5f91e2464bbc65f218ca400b6252c74",
    "testharness"
@@ -461931,7 +461989,7 @@
    "support"
   ],
   "interfaces/encoding.idl": [
-   "d4fffa89334b1e79f4966769e1d0cf9fb17378aa",
+   "d00624fa1ed7edc5b6f740835147c62f27631359",
    "support"
   ],
   "interfaces/encrypted-media.idl": [
@@ -462295,7 +462353,7 @@
    "support"
   ],
   "interfaces/webrtc-identity.idl": [
-   "5a59a4887ac7eb0ad39cfd82fee052828d7af316",
+   "23f02c3f12b67e3fa7b9ec0f8270bc2565946370",
    "support"
   ],
   "interfaces/webrtc-stats.idl": [
@@ -491219,7 +491277,7 @@
    "support"
   ],
   "resources/readme.md": [
-   "97d2e6f92203d126ba585216c5605b581454af1e",
+   "09a62fbd7bfd7c58d3ded0115bd0ee1efb04f8c5",
    "support"
   ],
   "resources/sriharness.js": [
@@ -491455,7 +491513,7 @@
    "support"
   ],
   "scroll-to-text-fragment/scroll-to-text-fragment.html": [
-   "ab4f8df0d0fa7716322ec6cd6f8a931dbc6fcc2a",
+   "e05210f1ea06e3f7676cc86392c6eb5b3d1d8837",
    "testharness"
   ],
   "secure-contexts/META.yml": [
@@ -494883,7 +494941,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/test-helpers.sub.js": [
-   "af8dad3a5be8aa831900b183ad7ee9a10417ba1a",
+   "db7433dcdbdb25d5626725392828a7e0b76708da",
    "support"
   ],
   "service-workers/service-worker/resources/test-request-headers-worker.js": [
@@ -497806,6 +497864,14 @@
    "1715cd9d4711878d101e5b68963d2359521b4bfe",
    "testharness"
   ],
+  "svg/animations/scripted/SVGAnimationElement-exceptions.html": [
+   "0ad8ad3df50cf77cb12a7f7da550fac4dc4b8689",
+   "testharness"
+  ],
+  "svg/animations/scripted/SVGAnimationElement-getStartTime.html": [
+   "5ffe98c3b855dac879062ade9a6061f0b8bd2274",
+   "testharness"
+  ],
   "svg/animations/scripted/end-element-on-inactive-element.svg": [
    "34be9b9781f707d488e284c3285b82009557366b",
    "testharness"
@@ -504799,7 +504865,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/edgechromium.py": [
-   "63680b6774a27592491e671f53f140b65e17042b",
+   "e28e0f995d0724bca913b17b07714f96d411505f",
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/epiphany.py": [
@@ -504811,7 +504877,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/firefox_android.py": [
-   "30d0825a8ca373d4cd902bbe54966b32be66f22f",
+   "33b5d51e54e82adb6cdbe4612263bc30883dd108",
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/ie.py": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number-ref.html b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number-ref.html
new file mode 100644
index 0000000..dc91342
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number-ref.html
@@ -0,0 +1,62 @@
+<!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 baseline alignment of inline-grid with an INPUT type=number item</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <style>
+
+.grid {
+  display: inline-block;
+  line-height: 0;
+  border: 5px solid;
+  padding: 1px 0 3px 0;
+  margin: 11px 0;
+}
+
+input { -webkit-appearance:none; inline-size:4ch; }
+
+.big {
+ font-size: 4em;
+}
+
+.small {
+  font-size: 0.2em;
+}
+
+.vlr { writing-mode: vertical-lr; }
+  </style>
+</head>
+<body>
+
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div class="vlr">
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html
new file mode 100644
index 0000000..9b9735c7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset=utf-8>
+  <title>Test baseline alignment of inline-grid with an INPUT type=number item</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-align/">
+  <link rel="match" href="grid-item-input-type-number-ref.html">
+  <style>
+
+.grid {
+  display: inline-grid;
+  border: 5px solid;
+  padding: 1px 0 3px 0;
+  margin: 11px 0;
+}
+
+input { -webkit-appearance:none; inline-size:4ch; }
+
+.big {
+ font-size: 4em;
+}
+
+.small {
+  font-size: 0.2em;
+}
+
+.vlr { writing-mode: vertical-lr; }
+  </style>
+</head>
+<body>
+
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div class="vlr">
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" type="number" value="9"></div>
+  <span class="small">B</span>
+</div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text-ref.html b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text-ref.html
new file mode 100644
index 0000000..e6d2786
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text-ref.html
@@ -0,0 +1,62 @@
+<!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 baseline alignment of inline-grid with an INPUT type=text item</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <style>
+
+.grid {
+  display: inline-block;
+  line-height: 0;
+  border: 5px solid;
+  padding: 1px 0 3px 0;
+  margin: 11px 0;
+}
+
+input { -webkit-appearance:none; inline-size:4ch; }
+
+.big {
+ font-size: 4em;
+}
+
+.small {
+  font-size: 0.2em;
+}
+
+.vlr { writing-mode: vertical-lr; }
+  </style>
+</head>
+<body>
+
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div class="vlr">
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" value="9"></div>
+  <span class="small">B</span>
+</div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html
new file mode 100644
index 0000000..526dcbae
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/baseline-rules/grid-item-input-type-text.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset=utf-8>
+  <title>Test baseline alignment of inline-grid with an INPUT type=text item</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-align/">
+  <link rel="match" href="grid-item-input-type-text-ref.html">
+  <style>
+
+.grid {
+  display: inline-grid;
+  border: 5px solid;
+  padding: 1px 0 3px 0;
+  margin: 11px 0;
+}
+
+input { -webkit-appearance:none; inline-size:4ch; }
+
+.big {
+ font-size: 4em;
+}
+
+.small {
+  font-size: 0.2em;
+}
+
+.vlr { writing-mode: vertical-lr; }
+  </style>
+</head>
+<body>
+
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div class="vlr">
+<div>
+  <span class="big">B</span>
+  <div class="grid"><input value="9"></div>
+  <span class="small">B</span>
+</div>
+
+<div>
+  <span>B</span>
+  <div class="grid"><input class="big" value="9"></div>
+  <span class="small">B</span>
+</div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl b/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
index d4fffa89..d00624fa1 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
@@ -22,7 +22,7 @@
 interface TextDecoder {
   constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {});
 
-  USVString decode(optional BufferSource input, optional TextDecodeOptions options = {});
+  USVString decode(optional [AllowShared] BufferSource input, optional TextDecodeOptions options = {});
 };
 TextDecoder includes TextDecoderCommon;
 
@@ -40,7 +40,7 @@
   constructor();
 
   [NewObject] Uint8Array encode(optional USVString input = "");
-  TextEncoderEncodeIntoResult encodeInto(USVString source, Uint8Array destination);
+  TextEncoderEncodeIntoResult encodeInto(USVString source, [AllowShared] Uint8Array destination);
 };
 TextEncoder includes TextEncoderCommon;
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-identity.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-identity.idl
index 5a59a48..23f02c3f 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-identity.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-identity.idl
@@ -38,11 +38,11 @@
 };
 
 partial interface RTCPeerConnection {
-    void setIdentityProvider(DOMString provider, optional RTCIdentityProviderOptions options);
-    Promise<DOMString> getIdentityAssertion();
+    void               setIdentityProvider (DOMString provider, optional RTCIdentityProviderOptions options = {});
+    Promise<DOMString> getIdentityAssertion ();
     readonly        attribute Promise<RTCIdentityAssertion> peerIdentity;
-    readonly        attribute DOMString? idpLoginUrl;
-    readonly        attribute DOMString? idpErrorInfo;
+    readonly        attribute DOMString?                    idpLoginUrl;
+    readonly        attribute DOMString?                    idpErrorInfo;
 };
 
 dictionary RTCIdentityProviderOptions {
@@ -51,8 +51,9 @@
     DOMString peerIdentity;
 };
 
-[Constructor(DOMString idp, DOMString name), Exposed=Window]
+[Exposed=Window]
 interface RTCIdentityAssertion {
+    constructor(DOMString idp, DOMString name);
     attribute DOMString idp;
     attribute DOMString name;
 };
@@ -62,6 +63,6 @@
 };
 
 partial interface MediaStreamTrack {
-    readonly        attribute boolean isolated;
+    readonly        attribute boolean      isolated;
                     attribute EventHandler onisolationchange;
 };
diff --git a/third_party/blink/web_tests/external/wpt/resources/readme.md b/third_party/blink/web_tests/external/wpt/resources/readme.md
index 97d2e6f..09a62fb 100644
--- a/third_party/blink/web_tests/external/wpt/resources/readme.md
+++ b/third_party/blink/web_tests/external/wpt/resources/readme.md
@@ -1,5 +1,8 @@
 # Resources
 
+This directory contains utilities intended for use by tests and maintained as project infrastructure.
+It does not contain tests.
+
 ## `testharness.js`
 
 `testharness.js` is a framework for writing low-level tests of
@@ -7,20 +10,5 @@
 making assertions and is intended to work for both simple synchronous
 tests, and tests of asynchronous behaviour.
 
-### Getting started
-
-To use `testharness.js` you must include two scripts, in the order given:
-
-``` html
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-```
-
-### Full documentation
-
-For detailed API documentation please visit [https://web-platform-tests.org/writing-tests/testharness-api.html](https://web-platform-tests.org/writing-tests/testharness-api.html).
-
-### Tutorials
-
-You can also read a tutorial on
-[Using testharness.js](http://darobin.github.com/test-harness-tutorial/docs/using-testharness.html).
+Complete documentation is available in the `docs/` directory of this repository
+and on the web at https://web-platform-tests.org/writing-tests/.
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment.html
index ab4f8df..e05210f 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment.html
@@ -24,8 +24,8 @@
 ];
 
 test(t => {
-  assert_equals(typeof(window.location.selector), 'object', 'window.location.selector is defined');
-}, 'Scroll to text is feature detectable via window.location.selector');
+  assert_equals(typeof(window.location.fragmentDirective), 'object', 'window.location.fragmentDirective is defined');
+}, 'Scroll to text is feature detectable via window.location.fragmentDirective');
 
 for (const test_case of test_cases) {
   promise_test(t => new Promise(resolve => {
diff --git a/third_party/blink/web_tests/svg/dom/SVGAnimationElement-exceptions.html b/third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-exceptions.html
similarity index 83%
rename from third_party/blink/web_tests/svg/dom/SVGAnimationElement-exceptions.html
rename to third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-exceptions.html
index 532994e..0ad8ad3 100644
--- a/third_party/blink/web_tests/svg/dom/SVGAnimationElement-exceptions.html
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-exceptions.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <title>SVGAnimationElement exceptions</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>
 <svg height="0"><animate begin="foo.begin"/></svg>
 <script>
 setup(function() {
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-getStartTime.html b/third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-getStartTime.html
new file mode 100644
index 0000000..5ffe98c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/scripted/SVGAnimationElement-getStartTime.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>SVGAnimationElement.getStartTime() returns the start time of the current interval.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg height="0">
+  <animate attributeName="visibility" begin="1s; 3s" dur="1s"/>
+  <animate attributeName="visibility" begin="1s; 3s" dur="1s" fill="freeze"/>
+</svg>
+<script>
+setup(function() {
+  window.animationElements = document.querySelectorAll('animate');
+  window.timeContainer = document.querySelector('svg');
+});
+
+function checkStartTime(values, t) {
+  assert_equals(animationElements[0].getStartTime(), values[0],
+                'start time @ ' + t);
+  assert_equals(animationElements[1].getStartTime(), values[1],
+                'start time @ ' + t);
+}
+
+function checkHasNoCurrentInterval(t) {
+  assert_throws('InvalidStateError', () => {
+    animationElements[0].getStartTime()
+  }, 'no interval @ ' + t);
+  assert_throws('InvalidStateError', () => {
+    animationElements[1].getStartTime()
+  }, 'no interval @ ' + t);
+}
+
+async_test(t => {
+  timeContainer.pauseAnimations();
+  // Wait for the timeline to start.
+  onload = t.step_func(() => {
+    t.step_timeout(function() {
+      assert_equals(timeContainer.getCurrentTime(), 0);
+      checkStartTime([1, 1], 0);
+      timeContainer.setCurrentTime(1);
+      checkStartTime([1, 1], 1);
+      timeContainer.setCurrentTime(1.5);
+      checkStartTime([1, 1], 1.5);
+      timeContainer.setCurrentTime(2);
+      checkStartTime([3, 3], 2);
+      timeContainer.setCurrentTime(2.5);
+      checkStartTime([3, 3], 2.5);
+      timeContainer.setCurrentTime(3);
+      checkStartTime([3, 3], 3);
+      timeContainer.setCurrentTime(4);
+      checkHasNoCurrentInterval(4);
+      timeContainer.setCurrentTime(5);
+      checkHasNoCurrentInterval(5);
+      t.done();
+    }, 0);
+  });
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
index 30d0825..33b5d51e 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
@@ -10,7 +10,7 @@
 from ..executors.executormarionette import (MarionetteTestharnessExecutor,  # noqa: F401
                                             MarionetteRefTestExecutor)  # noqa: F401
 from .firefox import (get_timeout_multiplier,  # noqa: F401
-                      run_info_browser_version,
+                      run_info_extras as fx_run_info_extras,
                       update_properties,  # noqa: F401
                       executor_kwargs,  # noqa: F401
                       FirefoxBrowser)  # noqa: F401
@@ -65,10 +65,10 @@
 
 
 def run_info_extras(**kwargs):
+    rv = fx_run_info_extras(**kwargs)
     package = kwargs["package_name"]
-    rv = {"e10s": True if package is not None and "geckoview" in package else False,
-          "headless": False}
-    rv.update(run_info_browser_version(kwargs["binary"]))
+    rv.update({"e10s": True if package is not None and "geckoview" in package else False,
+               "headless": False})
     return rv
 
 
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/trusted-types-origin-trial.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/trusted-types-origin-trial.html
deleted file mode 100644
index ad08c22..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/trusted-types-origin-trial.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Trusted types - exposed by origin trial</title>
-<script src="../../../../resources/testharness.js"></script>
-<script src="../../../../resources/testharnessreport.js"></script>
-<script src="../../../../resources/origin-trials-helper.js"></script>
-
-<script>
-// Can only run this test if TrustedDOMTypes is not enabled via a Chrome flag.
-// That is only the case when running this in a virtual test suite (by default,
-// runtime enabled features are on for layout tests).
-// To run in virtual test suite:
-// tools/run_web_tests.py virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed
-if (!self.internals.runtimeFlags.trustedDOMTypesEnabled) {
-  test(t => {
-    assert_not_own_property(window, 'TrustedTypes', 'TrustedTypes is defined on the window');
-  }, 'trusted types in Origin-Trial disabled document.');
-}
-
-// generated with command
-// tools/origin_trials/generate_token.py http://127.0.0.1:8000 TrustedDOMTypes --expire-timestamp=2000000000
-const token = 'AqXgC692H4wuaTLMJ0jzBazq/pN6WCvcllT60HwwZpdNy/vrnklJOcAL7D6wcSDL+FjyR16xxhbcTtB8Mc1Q4wMAAABXeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiVHJ1c3RlZERPTVR5cGVzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9';
-OriginTrialsHelper.add_token(token);
-test(t => {
-  assert_own_property(window, 'TrustedTypes', 'TrustedTypes is not defined on the window');
-  assert_own_property(window.TrustedTypes, 'createPolicy', 'createPolicy is not defined on TrustedTypes');
-}, 'trusted types in Origin-Trial enabled document.');
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/cross-origin-createImageBitmap-useCounter.html b/third_party/blink/web_tests/http/tests/security/cross-origin-createImageBitmap-useCounter.html
new file mode 100644
index 0000000..c53a3e8a6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/cross-origin-createImageBitmap-useCounter.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const ORIGIN_CLEAN_URL = 'http://127.0.0.1:8000/security/resources/abe.png'
+const NON_ORIGIN_CLEAN_URL = 'http://localhost:8000/security/resources/abe.png'
+
+async function createImageBitmapFor(url) {
+  const img = document.createElement('img');
+  img.src = url;
+  try {
+    await new Promise((resolve, reject) => {
+      img.addEventListener('load', resolve, {once: true});
+      img.addEventListener('error', reject, {once: true});
+    });
+  } finally {
+    img.remove();
+  }
+  return createImageBitmap(img);
+}
+
+promise_test(async (t) => {
+  const FEATURE = 3056;
+
+  const bitmap = await createImageBitmapFor(ORIGIN_CLEAN_URL);
+
+  assert_false(internals.isUseCounted(document, FEATURE));
+
+  postMessage(bitmap, '*');
+
+  assert_true(internals.isUseCounted(document, FEATURE));
+}, 'origin-clean, serialization');
+
+promise_test(async (t) => {
+  const FEATURE = 3057;
+
+  const bitmap = await createImageBitmapFor(NON_ORIGIN_CLEAN_URL);
+
+  assert_false(internals.isUseCounted(document, FEATURE));
+
+  postMessage(bitmap, '*');
+
+  assert_true(internals.isUseCounted(document, FEATURE));
+}, 'non-origin-clean, serialization');
+
+promise_test(async (t) => {
+  const FEATURE = 3058;
+
+  const bitmap = await createImageBitmapFor(ORIGIN_CLEAN_URL);
+
+  assert_false(internals.isUseCounted(document, FEATURE));
+
+  postMessage(bitmap, '*', [bitmap]);
+
+  assert_true(internals.isUseCounted(document, FEATURE));
+}, 'origin-clean, transfer');
+
+promise_test(async (t) => {
+  const FEATURE = 3059;
+
+  const bitmap = await createImageBitmapFor(NON_ORIGIN_CLEAN_URL);
+
+  assert_false(internals.isUseCounted(document, FEATURE));
+
+  postMessage(bitmap, '*', [bitmap]);
+
+  assert_true(internals.isUseCounted(document, FEATURE));
+}, 'non-origin-clean, transfer');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/installedapp/getinstalledrelatedapps.html b/third_party/blink/web_tests/installedapp/getinstalledrelatedapps.html
index 626a338..a482bc8 100644
--- a/third_party/blink/web_tests/installedapp/getinstalledrelatedapps.html
+++ b/third_party/blink/web_tests/installedapp/getinstalledrelatedapps.html
@@ -12,8 +12,8 @@
   // list of related_applications from this page's manifest. The mock service
   // returns the empty list, implying that none are installed.
   mock.pushExpectedCall(
-      [{platform: 'play', url: null, id: 'com.test'},
-       {platform: 'itunes', url: 'https://itunes.apple.com/', id: null}],
+      [{platform: 'play', url: null, id: 'com.test', version: null},
+       {platform: 'itunes', url: 'https://itunes.apple.com/', id: null, version: null}],
       []);
   return navigator.getInstalledRelatedApps().then(result => {
     assert_array_relatedapplication_equals(result, []);
@@ -44,13 +44,13 @@
   mock.pushExpectedCall(
       [{platform: 'play', url: null, id: 'com.test'},
        {platform: 'itunes', url: 'https://itunes.apple.com/', id: null}],
-      [{platform: 'play', url: null, id: 'com.test'},
-       {platform: 'itunes', url: 'https://itunes.apple.com/', id: null}]);
+      [{platform: 'play', url: null, id: 'com.test', version: '1.4.2'},
+       {platform: 'itunes', url: 'https://itunes.apple.com/', id: null, version: null}]);
 
   return navigator.getInstalledRelatedApps().then(result => {
     assert_array_relatedapplication_equals(
         result,
-        [{platform: 'play', id: 'com.test'},
+        [{platform: 'play', id: 'com.test', version: '1.4.2'},
          {platform: 'itunes', url: 'https://itunes.apple.com/'}]);
   });
 }, 'getInstalledRelatedApps with multiple related and installed apps');
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
index 2d61265..229106c 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/trailing-white-space-2-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/trailing-white-space-2-expected.png
new file mode 100644
index 0000000..08e8e87
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/trailing-white-space-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
index 1f16d0d..f17d585e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/svg/animations/slider-switch-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/svg/animations/slider-switch-expected.txt
new file mode 100644
index 0000000..9317f59c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/svg/animations/slider-switch-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Check correct event bases for onclick assert_equals: expected "block" but got "none"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
index 4d94728..fcd2ceeb 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/svg/animations/resources/SVGAnimationTestCase.js b/third_party/blink/web_tests/svg/animations/resources/SVGAnimationTestCase.js
index 590f51eb..1deaf73 100644
--- a/third_party/blink/web_tests/svg/animations/resources/SVGAnimationTestCase.js
+++ b/third_party/blink/web_tests/svg/animations/resources/SVGAnimationTestCase.js
@@ -78,7 +78,7 @@
     try {
         newTime += animation.getStartTime();
     } catch(e) {
-        debug('Exception thrown: ' + e);
+        // No current interval.
     }
 
     // The sample time is relative to the start time of the animation, take that into account.
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 712dea85..b4599f52 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-1-68-g545a481a7
-Revision: 545a481a74a3c3b70af8928793a01a84f8b0ee9b
+Version: VER-2-10-1-69-g90a30f154
+Revision: 90a30f154a612693641e5366ea8d1d27ea2a4a99
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 60aa21e9..79c3e27 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -172,37 +172,34 @@
   "chrome/browser/resources/usb_internals/resources.grd": {
     "includes": [14030],
   },
-  "components/sync/driver/resources.grd": {
-    "includes": [14050],
-  },
   # END chrome/ WebUI resources section
 
   # START chrome/ miscellaneous section.
   "chrome/android/features/test_dummy/internal/resources/resources.grd": {
-    "includes": [14090],
+    "includes": [14070],
   },
   "chrome/common/common_resources.grd": {
-    "includes": [14180],
+    "includes": [14160],
   },
   "chrome/credential_provider/gaiacp/gaia_resources.grd": {
-    "includes": [14200],
-    "messages": [14210],
+    "includes": [14180],
+    "messages": [14190],
   },
   "chrome/renderer/resources/renderer_resources.grd": {
-    "includes": [14250],
-    "structures": [14360],
+    "includes": [14230],
+    "structures": [14340],
   },
   "chrome/test/data/webui_test_resources.grd": {
-    "includes": [14400],
+    "includes": [14380],
   },
   # END chrome/ miscellaneous section.
 
   # START chromeos/ section.
   "chromeos/chromeos_strings.grd": {
-    "messages": [14530],
+    "messages": [14510],
   },
   "chromeos/resources/chromeos_resources.grd": {
-    "includes": [14580],
+    "includes": [14560],
   },
   # END chromeos/ section.
 
@@ -211,27 +208,27 @@
   # We only use one file depending on whether we're building Chromium or
   # Google Chrome.
   "components/components_chromium_strings.grd": {
-    "messages": [15060],
+    "messages": [15040],
   },
   "components/components_google_chrome_strings.grd": {
-    "messages": [15060],
+    "messages": [15040],
   },
 
   "components/components_locale_settings.grd": {
-    "includes": [15080],
-    "messages": [15090],
+    "includes": [15060],
+    "messages": [15070],
   },
   "components/components_strings.grd": {
-    "messages": [15120],
+    "messages": [15100],
   },
   "components/omnibox/resources/omnibox_resources.grd": {
-    "includes": [17210],
+    "includes": [17190],
   },
   "components/policy/resources/policy_templates.grd": {
-    "structures": [17220],
+    "structures": [17200],
   },
   "components/resources/components_resources.grd": {
-    "includes": [17230],
+    "includes": [17210],
   },
   "components/resources/components_scaled_resources.grd": {
     "structures": [17400],
diff --git a/tools/idl_parser/idl_parser.py b/tools/idl_parser/idl_parser.py
index cd22a2a..ceedb91 100755
--- a/tools/idl_parser/idl_parser.py
+++ b/tools/idl_parser/idl_parser.py
@@ -670,8 +670,7 @@
   def p_AsyncIterable(self, p):
     """AsyncIterable : ASYNC ITERABLE '<' TypeWithExtendedAttributes ',' TypeWithExtendedAttributes '>' ';'"""
     childlist = ListFromConcat(p[4], p[6])
-    p[0] = self.BuildProduction('Iterable', p, 2, childlist)
-    p[0].AddChildren(self.BuildTrue('ASYNC'))
+    p[0] = self.BuildProduction('AsyncIterable', p, 2, childlist)
 
   def p_ReadWriteMaplike(self, p):
     """ReadWriteMaplike : MaplikeRest"""
diff --git a/tools/idl_parser/test_parser/interface_web.idl b/tools/idl_parser/test_parser/interface_web.idl
index d6660b63..10ac458 100644
--- a/tools/idl_parser/test_parser/interface_web.idl
+++ b/tools/idl_parser/test_parser/interface_web.idl
@@ -436,8 +436,7 @@
  *      PrimitiveType(long long)
  *      ExtAttributes()
  *        ExtAttribute(EnforceRange)
- *  Iterable()
- *    ASYNC: True
+ *  AsyncIterable()
  *    Type()
  *      PrimitiveType(double)
  *    Type()
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index f7af7e6..af60a1b6 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -650,7 +650,6 @@
     },
 
     'tryserver.chromium.android': {
-      'android-opus-kitkat-arm-rel': 'android_release_trybot',
       # TODO(crbug/597596): Switch this back to debug_trybot when cronet's
       # shared library loading is fixed.
       'android-cronet-arm-dbg': 'android_cronet_debug_static_bot_arm_no_neon',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a5533fd..fd88c0e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -24920,6 +24920,10 @@
   <int value="3053" label="UserTimingL3"/>
   <int value="3054" label="GetGamepadsFromCrossOriginSubframe"/>
   <int value="3055" label="GetGamepadsFromInsecureContext"/>
+  <int value="3056" label="OriginCleanImageBitmapSerialization"/>
+  <int value="3057" label="NonOriginCleanImageBitmapSerialization"/>
+  <int value="3058" label="OriginCleanImageBitmapTransfer"/>
+  <int value="3059" label="NonOriginCleanImageBitmapTransfer"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -59367,6 +59371,7 @@
   <int value="10"
       label="[Deprecated in M79] Sharing notification dismiss button"/>
   <int value="11" label="Sharing error notification try again button"/>
+  <int value="12" label="Settings button for notifications."/>
 </enum>
 
 <enum name="SystemNotificationType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a8396ca..a76bc737 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4957,7 +4957,7 @@
 
 <histogram
     name="Apps.AppList.DriveQuickAccessProvider.TimeFromFetchToZeroStateStart"
-    units="ms">
+    units="ms" expires_after="M82">
   <owner>wrong@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -5070,7 +5070,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.ZeroStateResultsList.FileImpressions"
-    enum="AppListSearchResult">
+    enum="AppListSearchResult" expires_after="M82">
   <owner>wrong@chromium.org</owner>
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
@@ -10010,7 +10010,7 @@
   </summary>
 </histogram>
 
-<histogram name="AuthPolicy.TimeToRunNetAdsJoin" units="ms">
+<histogram name="AuthPolicy.TimeToRunNetAdsJoin" units="ms" expires_after="M82">
   <owner>fsandrade@chromium.org</owner>
   <owner>ljusten@chromium.org</owner>
   <owner>tomdobro@chromium.org</owner>
@@ -16048,7 +16048,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Sms.Receive.TimeCancel" units="ms">
+<histogram name="Blink.Sms.Receive.TimeCancel" units="ms" expires_after="M82">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>ayui@chromium.org</owner>
@@ -22244,7 +22244,8 @@
   </summary>
 </histogram>
 
-<histogram name="Compositing.Renderer.LayersUpdateTime" units="microseconds">
+<histogram name="Compositing.Renderer.LayersUpdateTime" units="microseconds"
+    expires_after="M82">
   <owner>animations-dev@chromium.org</owner>
   <summary>
     Time spent updating layers, in microseconds. Recorded when layers are
@@ -36435,7 +36436,7 @@
 </histogram>
 
 <histogram name="Enterprise.ActiveDirectoryJoin"
-    enum="ActiveDirectoryDomainJoinType">
+    enum="ActiveDirectoryDomainJoinType" expires_after="M82">
   <owner>rsorokin@chromium.org</owner>
   <summary>
     The way device is joined to the Active Directory domain. This will be
@@ -36519,7 +36520,8 @@
   <summary>URL fetcher status for auto-enrollment requests.</summary>
 </histogram>
 
-<histogram name="Enterprise.CloudReportingBasicRequestSize" units="KB">
+<histogram name="Enterprise.CloudReportingBasicRequestSize" units="KB"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>
     The proto size of the basic Chrome browser cloud management reporting
@@ -36528,7 +36530,8 @@
   </summary>
 </histogram>
 
-<histogram name="Enterprise.CloudReportingRequestCount" units="requests">
+<histogram name="Enterprise.CloudReportingRequestCount" units="requests"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>
     The number of request for one Chrome browser cloud management report. A
@@ -36536,7 +36539,8 @@
   </summary>
 </histogram>
 
-<histogram name="Enterprise.CloudReportingRequestSize" units="KB">
+<histogram name="Enterprise.CloudReportingRequestSize" units="KB"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>
     The proto size of each Chrome browser cloud management reporting request. It
@@ -36982,7 +36986,7 @@
 
 <histogram
     name="Enterprise.MachineLevelUserCloudPolicyEnrollment.RequestFailureTime"
-    units="ms">
+    units="ms" expires_after="M82">
   <owner>rogerta@chromium.org</owner>
   <summary>
     Time since the enrollment request was made until an error is returned from
@@ -37009,7 +37013,8 @@
 
 <histogram
     name="Enterprise.MachineLevelUserCloudPolicyEnrollment.StartupDialog"
-    enum="MachineLevelUserCloudPolicyEnrollmentStartupDialog">
+    enum="MachineLevelUserCloudPolicyEnrollmentStartupDialog"
+    expires_after="M82">
   <owner>rogerta@chromium.org</owner>
   <summary>
     Records whether the machine level user cloud policy enrollment dialog is
@@ -50793,7 +50798,7 @@
 </histogram>
 
 <histogram name="GPU.DirectComposition.FramesSinceColorSpaceChange"
-    units="frames">
+    units="frames" expires_after="M82">
   <owner>sunnyps@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -51321,7 +51326,7 @@
   </summary>
 </histogram>
 
-<histogram name="GPU.InitializeOneOffMediumTime" units="ms">
+<histogram name="GPU.InitializeOneOffMediumTime" units="ms" expires_after="M82">
   <owner>jmadill@chromium.org</owner>
   <summary>
     The time that the GPU process spends in initializing the GL surface, and
@@ -52241,7 +52246,8 @@
   </summary>
 </histogram>
 
-<histogram name="Hardware.Serial.NewMinusOldDeviceListSize" units="units">
+<histogram name="Hardware.Serial.NewMinusOldDeviceListSize" units="units"
+    expires_after="M82">
   <owner>charliea@chromium.org</owner>
   <summary>
     On Windows and Mac, we're implementing new methods to enumerate serial
@@ -59654,14 +59660,16 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audible.ConcurrentTabsWhenStarting" units="units">
+<histogram name="Media.Audible.ConcurrentTabsWhenStarting" units="units"
+    expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <summary>
     Records how many tabs were audible when a new tab started to be audible.
   </summary>
 </histogram>
 
-<histogram name="Media.Audible.MaxConcurrentTabsInSession" units="units">
+<histogram name="Media.Audible.MaxConcurrentTabsInSession" units="units"
+    expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <summary>
     Records how many tabs are audible at the same time during the session. It is
@@ -60042,7 +60050,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.Capture.Win.InitError" enum="Hresult">
+<histogram name="Media.Audio.Capture.Win.InitError" enum="Hresult"
+    expires_after="M82">
   <owner>tommi@chromium.org</owner>
   <summary>
     Error codes from IAudioClient::Initialize() in
@@ -61017,7 +61026,7 @@
 
 <histogram
     name="Media.AudioOutputStreamProxy.GetPreferredOutputStreamParametersWin.CreateDeviceEnumeratorResult"
-    enum="Hresult">
+    enum="Hresult" expires_after="M82">
   <owner>marinaciocea@chromium.org</owner>
   <summary>
     This histogram is the 1st potential failure step in
@@ -61030,7 +61039,7 @@
 
 <histogram
     name="Media.AudioOutputStreamProxy.GetPreferredOutputStreamParametersWin.CreateDeviceResult"
-    enum="Hresult">
+    enum="Hresult" expires_after="M82">
   <owner>marinaciocea@chromium.org</owner>
   <summary>
     This histogram is the 2nd potential failure step in
@@ -61043,7 +61052,7 @@
 
 <histogram
     name="Media.AudioOutputStreamProxy.GetPreferredOutputStreamParametersWin.GetDevicePeriodResult"
-    enum="Hresult">
+    enum="Hresult" expires_after="M82">
   <owner>marinaciocea@chromium.org</owner>
   <summary>
     This histogram is the last potential failure step in
@@ -61445,7 +61454,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.BytesReadFromCache" units="KB">
+<histogram name="Media.BytesReadFromCache" units="KB" expires_after="M82">
   <owner>hubbe@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Kb read by media demuxer from MultiBuffer cache.</summary>
@@ -61891,7 +61900,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.DXVAVDA.ErrorLine" units="units">
+<histogram name="Media.DXVAVDA.ErrorLine" units="units" expires_after="M82">
   <owner>liberato@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -62016,14 +62025,15 @@
 </histogram>
 
 <histogram name="Media.EME.CdmHostVerificationStatus"
-    enum="CdmHostVerificationStatus">
+    enum="CdmHostVerificationStatus" expires_after="M82">
   <owner>media-dev@chromium.org</owner>
   <summary>
     The status of CDM host verification. This is reported per CDM load.
   </summary>
 </histogram>
 
-<histogram name="Media.EME.CdmInterfaceVersion" units="units">
+<histogram name="Media.EME.CdmInterfaceVersion" units="units"
+    expires_after="M82">
   <owner>xhwang@chromium.org</owner>
   <summary>
     Chromium could support multiple versions of CDM interface. This metric
@@ -62076,7 +62086,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.EME.EncryptedMediaEnabled" enum="BooleanEnabled">
+<histogram name="Media.EME.EncryptedMediaEnabled" enum="BooleanEnabled"
+    expires_after="M82">
   <owner>xhwang@chromium.org</owner>
   <summary>
     Whether encrypted media is enabled when requestMediaKeySystemAccess() is
@@ -62241,7 +62252,8 @@
   <summary>EME NeedKey event count.</summary>
 </histogram>
 
-<histogram name="Media.EME.OutputProtection" enum="MediaOutputProtectionStatus">
+<histogram name="Media.EME.OutputProtection" enum="MediaOutputProtectionStatus"
+    expires_after="M82">
   <owner>xhwang@chromium.org</owner>
   <summary>
     Output protection query status and result. One query and one positive (no
@@ -62535,7 +62547,7 @@
 </histogram>
 
 <histogram name="Media.FallbackToHighLatencyAudioPath"
-    enum="BooleanDidFallBack">
+    enum="BooleanDidFallBack" expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Whether Chrome had to fallback to the high latency audio path or not.
@@ -62632,7 +62644,7 @@
 </histogram>
 
 <histogram name="Media.GpuArcVideoDecodeAccelerator.InitializeResult"
-    enum="ArcVideoDecodeAcceleratorResult">
+    enum="ArcVideoDecodeAcceleratorResult" expires_after="M82">
   <owner>hiroh@chromium.org</owner>
   <summary>
     Counts of status values returned from calls to
@@ -62641,7 +62653,7 @@
 </histogram>
 
 <histogram name="Media.GpuMemoryBufferVideoFramePool.UnsupportedFormat"
-    enum="VideoPixelFormatUnion">
+    enum="VideoPixelFormatUnion" expires_after="M82">
   <owner>dcastagna@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
   <summary>
@@ -62767,7 +62779,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.InputErrorMac" units="OSStatus">
+<histogram name="Media.InputErrorMac" units="OSStatus" expires_after="M82">
   <owner>tommi@chromium.org</owner>
   <summary>
     Error codes that we encounter while setting up an AUAudioInputStream on Mac.
@@ -62930,7 +62942,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.LoadType" enum="MediaLoadType">
+<histogram name="Media.LoadType" enum="MediaLoadType" expires_after="M82">
   <owner>xhwang@chromium.org</owner>
   <summary>
     Load type of HTML5 media, such as URL, MediaSource and MediaStream.
@@ -63029,7 +63041,7 @@
 </histogram>
 
 <histogram name="Media.MediaElement.PlayPromiseReject"
-    enum="PlayPromiseRejectReason">
+    enum="PlayPromiseRejectReason" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -63039,7 +63051,7 @@
 </histogram>
 
 <histogram name="Media.MediaKeysListener.RegisterHotKeyResult"
-    enum="BooleanSuccess">
+    enum="BooleanSuccess" expires_after="M82">
   <owner>steimel@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -63597,7 +63609,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Pepper.PlayedSound" enum="Boolean">
+<histogram name="Media.Pepper.PlayedSound" enum="Boolean" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -63689,7 +63701,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.PreloadMetadataSuspendWasIdeal" enum="Boolean">
+<histogram name="Media.PreloadMetadataSuspendWasIdeal" enum="Boolean"
+    expires_after="M82">
   <owner>media-dev@chromium.org</owner>
   <summary>
     Indicates if a suspend initiated for preload=metadata was ideal. I.e. we did
@@ -63698,7 +63711,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.RebuffersCount" units="rebuffers">
+<histogram name="Media.RebuffersCount" units="rebuffers" expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Indicates the number of rebuffers a given watch time session had.
@@ -63793,7 +63806,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Remoting.SessionDuration" units="ms">
+<histogram name="Media.Remoting.SessionDuration" units="ms" expires_after="M82">
   <owner>miu@chromium.org</owner>
   <summary>Measures the duration of each remoting session.</summary>
 </histogram>
@@ -63897,7 +63910,8 @@
   <summary>Video width while remoting content.</summary>
 </histogram>
 
-<histogram name="Media.RTCVideoDecoderError" enum="VideoDecodeAcceleratorError">
+<histogram name="Media.RTCVideoDecoderError" enum="VideoDecodeAcceleratorError"
+    expires_after="M82">
   <owner>posciak@chromium.org</owner>
   <summary>Counts of video decode errors reported to RTCVideoDecoder.</summary>
 </histogram>
@@ -63911,7 +63925,8 @@
   <summary>Results of attempts to RTCVideoDecoder::InitDecode().</summary>
 </histogram>
 
-<histogram name="Media.RTCVideoDecoderInitDecodeSuccess" enum="BooleanSuccess">
+<histogram name="Media.RTCVideoDecoderInitDecodeSuccess" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>posciak@chromium.org</owner>
   <summary>
     Indicates whether we were successful in initializing hardware video decoder
@@ -63919,7 +63934,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.RTCVideoEncoderInitEncodeSuccess" enum="BooleanSuccess">
+<histogram name="Media.RTCVideoEncoderInitEncodeSuccess" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>posciak@chromium.org</owner>
   <summary>
     Indicates whether we were successful in initializing hardware video encoder
@@ -64112,7 +64128,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Media.Timeline.DragGestureDuration" units="ms">
+<histogram base="true" name="Media.Timeline.DragGestureDuration" units="ms"
+    expires_after="M82">
 <!-- Name completed by histogram_suffixes name="MediaTimelineWidths" -->
 
   <owner>mlamouri@google.com</owner>
@@ -64125,7 +64142,7 @@
 </histogram>
 
 <histogram base="true" name="Media.Timeline.DragPercent"
-    enum="MediaTimelinePercent">
+    enum="MediaTimelinePercent" expires_after="M82">
 <!-- Name completed by histogram_suffixes name="MediaTimelineWidths" -->
 
   <owner>mlamouri@google.com</owner>
@@ -64139,7 +64156,7 @@
 </histogram>
 
 <histogram base="true" name="Media.Timeline.DragSumAbsTimeDelta"
-    enum="MediaTimelineAbsTimeDelta">
+    enum="MediaTimelineAbsTimeDelta" expires_after="M82">
 <!-- Name completed by histogram_suffixes name="MediaTimelineWidths" -->
 
   <owner>mlamouri@google.com</owner>
@@ -64157,7 +64174,7 @@
 </histogram>
 
 <histogram base="true" name="Media.Timeline.DragTimeDelta"
-    enum="MediaTimelineTimeDelta">
+    enum="MediaTimelineTimeDelta" expires_after="M82">
 <!-- Name completed by histogram_suffixes name="MediaTimelineWidths" -->
 
   <owner>mlamouri@google.com</owner>
@@ -64171,7 +64188,7 @@
 </histogram>
 
 <histogram base="true" name="Media.Timeline.SeekType"
-    enum="MediaTimelineSeekType">
+    enum="MediaTimelineSeekType" expires_after="M82">
 <!-- Name completed by histogram_suffixes name="MediaTimelineWidths" -->
 
   <owner>mlamouri@google.com</owner>
@@ -64473,7 +64490,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Video.Autoplay" enum="AutoplaySource">
+<histogram name="Media.Video.Autoplay" enum="AutoplaySource"
+    expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64495,7 +64513,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Video.Autoplay.Muted" enum="AutoplaySource">
+<histogram name="Media.Video.Autoplay.Muted" enum="AutoplaySource"
+    expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Records the autoplay source of muted videos.</summary>
@@ -64516,7 +64535,7 @@
 </histogram>
 
 <histogram name="Media.Video.Autoplay.Muted.Blocked"
-    enum="AutoplayBlockedReason">
+    enum="AutoplayBlockedReason" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64525,7 +64544,7 @@
 </histogram>
 
 <histogram name="Media.Video.Autoplay.Muted.PlayMethod.BecomesVisible"
-    enum="Boolean">
+    enum="Boolean" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64538,7 +64557,7 @@
 </histogram>
 
 <histogram name="Media.Video.Autoplay.Muted.PlayMethod.OffscreenDuration"
-    units="ms">
+    units="ms" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64547,7 +64566,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Video.Autoplay.Muted.UnmuteAction" enum="BooleanSuccess">
+<histogram name="Media.Video.Autoplay.Muted.UnmuteAction" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64635,7 +64655,7 @@
 </histogram>
 
 <histogram name="Media.Video.FullscreenOrientationLock.AutoRotateEnabled"
-    enum="BooleanEnabled">
+    enum="BooleanEnabled" expires_after="M82">
   <owner>mlamouri@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -64728,7 +64748,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.AspectRatio" units="%">
+<histogram name="Media.VideoCapture.AspectRatio" units="%" expires_after="M82">
   <owner>mcasas@chromium.org</owner>
   <summary>
     Video Capture Device captured aspect ratio, as a rounded integer multiplied
@@ -64738,7 +64758,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.BlacklistedDevice"
-    enum="BlacklistedVideoCaptureDeviceNames">
+    enum="BlacklistedVideoCaptureDeviceNames" expires_after="M82">
   <owner>mcasas@chromium.org</owner>
   <summary>
     Counts appearances of Blacklisted Video Capture devices during enumeration.
@@ -64747,7 +64767,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.DelayUntilFirstFrame" units="ms">
+<histogram name="Media.VideoCapture.DelayUntilFirstFrame" units="ms"
+    expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <summary>
     Time it takes from the moment that a VideoCaptureController is requested to
@@ -64756,7 +64777,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.Error" enum="VideoCaptureError">
+<histogram name="Media.VideoCapture.Error" enum="VideoCaptureError"
+    expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <summary>
     Breaks down the events counted in Media.VideoCaptureManager.Event bucket
@@ -64782,7 +64804,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.FrameRate" units="fps">
+<histogram name="Media.VideoCapture.FrameRate" units="fps" expires_after="M82">
   <owner>mcasas@chromium.org</owner>
   <summary>
     Video Capture Device frame rate requested by VideoCaptureManager on
@@ -64826,7 +64848,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.MacBook.HardwareVersionWhenNoCamera"
-    enum="MacBookVersions">
+    enum="MacBookVersions" expires_after="M82">
   <owner>perkj@chromium.org</owner>
   <summary>
     MacBook hardware version used when Chrome cannot enumerate a video device.
@@ -64834,7 +64856,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.MacBook.NumberOfDevices" units="devices">
+<histogram name="Media.VideoCapture.MacBook.NumberOfDevices" units="devices"
+    expires_after="M82">
   <owner>perkj@chromium.org</owner>
   <summary>
     Number of video capture devices detected by Chrome during device
@@ -64880,7 +64903,7 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoCapture.Width" units="pixels">
+<histogram name="Media.VideoCapture.Width" units="pixels" expires_after="M82">
   <owner>mcasas@chromium.org</owner>
   <summary>
     Video Capture Device captured frame width in pixels. The collection is made
@@ -64918,7 +64941,7 @@
 
 <histogram
     name="Media.VideoCapture.Windows.NumberOfRetriesNeededForMFGetDeviceStreamCategory"
-    units="retries">
+    units="retries" expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <summary>
     Counts how many retries are needed for calls to MediaFoundation function
@@ -64929,7 +64952,7 @@
 
 <histogram
     name="Media.VideoCapture.Windows.NumberOfRetriesNeededForMFGetDeviceStreamCount"
-    units="retries">
+    units="retries" expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <summary>
     Counts how many retries are needed for calls to MediaFoundation function
@@ -64964,7 +64987,8 @@
   <summary>Measures the time taken for VideoCaptureManager::</summary>
 </histogram>
 
-<histogram name="Media.VideoCaptureManager.Event" enum="VideoCaptureEvent">
+<histogram name="Media.VideoCaptureManager.Event" enum="VideoCaptureEvent"
+    expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
   <summary>
@@ -65056,7 +65080,7 @@
 </histogram>
 
 <histogram name="Media.VideoCaptureService.Event"
-    enum="VideoCaptureServiceEvent">
+    enum="VideoCaptureServiceEvent" expires_after="M82">
   <owner>chfremer@chromium.org</owner>
   <summary>
     Counts video capture service events, such as startup, shutdown, and
@@ -65071,7 +65095,8 @@
   <summary>Video codec used in HTML5 media.</summary>
 </histogram>
 
-<histogram name="Media.VideoCodecProfile" enum="VideoCodecProfile">
+<histogram name="Media.VideoCodecProfile" enum="VideoCodecProfile"
+    expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>Video codec profile used in HTML5 media.</summary>
 </histogram>
@@ -65094,14 +65119,16 @@
   <summary>Coded width of HTML5 video.</summary>
 </histogram>
 
-<histogram name="Media.VideoColorRange" enum="FFmpegColorRanges">
+<histogram name="Media.VideoColorRange" enum="FFmpegColorRanges"
+    expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Pixel format color range of HTML5 video. Emitted on video load.
   </summary>
 </histogram>
 
-<histogram name="Media.VideoDecoderFallback" enum="BooleanDidFallBack">
+<histogram name="Media.VideoDecoderFallback" enum="BooleanDidFallBack"
+    expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Whether Chrome had to fall back to a secondary video decoder after the
@@ -65109,7 +65136,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoDecodeStatsDB.OpSuccess" enum="BooleanSuccess">
+<histogram name="Media.VideoDecodeStatsDB.OpSuccess" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>chcunningham@chromium.org</owner>
   <summary>
     Indicates whether we were successful performing some database operation. See
@@ -65126,7 +65154,8 @@
   <summary>Pixel format used in HTML5 video. Emitted on video load.</summary>
 </histogram>
 
-<histogram name="Media.VideoFrame.ColorSpace" enum="VideoFrameColorSpaceUMA">
+<histogram name="Media.VideoFrame.ColorSpace" enum="VideoFrameColorSpaceUMA"
+    expires_after="M82">
   <owner>hubbe@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Video frame color space. Emitted for each video frame.</summary>
@@ -65163,7 +65192,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Media.VideoHeight.Initial" units="pixels">
+<histogram base="true" name="Media.VideoHeight.Initial" units="pixels"
+    expires_after="M82">
   <owner>media-dev@chromium.org</owner>
   <summary>
     The height of the first video frame in an HTML5 video. Reported when the
@@ -65234,7 +65264,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoRenderer.CadenceChanges" units="changes">
+<histogram name="Media.VideoRenderer.CadenceChanges" units="changes"
+    expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Indicates how many cadence changes have occurred during playback, a zero
@@ -65243,7 +65274,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.VideoRenderer.LowDelay" enum="Boolean">
+<histogram name="Media.VideoRenderer.LowDelay" enum="Boolean"
+    expires_after="M82">
   <owner>xhwang@chromium.org</owner>
   <summary>
     Indicates whether video is rendering in low delay mode. It's recorded when a
@@ -65274,7 +65306,7 @@
   <summary>Visible aspect ratio of HTML5 video.</summary>
 </histogram>
 
-<histogram name="Media.VideoVisibleWidth" units="units">
+<histogram name="Media.VideoVisibleWidth" units="units" expires_after="M82">
   <owner>dalecurtis@chromium.org</owner>
   <summary>Visible width of HTML5 video.</summary>
 </histogram>
@@ -65339,7 +65371,7 @@
 </histogram>
 
 <histogram name="Media.VTVDA.SessionFailureReason"
-    enum="VTVDASessionFailureType">
+    enum="VTVDASessionFailureType" expires_after="M82">
   <owner>sandersd@chromium.org</owner>
   <summary>
     Count of VTVDA session failure reasons. Successful initializations are
@@ -66563,7 +66595,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.Debug.FailedProcessDumpsPerGlobalDump"
-    units="failures">
+    units="failures" expires_after="M82">
   <owner>ssid@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <summary>
@@ -66574,7 +66606,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Experimental.Debug.GlobalDumpDuration" units="ms">
+<histogram name="Memory.Experimental.Debug.GlobalDumpDuration" units="ms"
+    expires_after="M82">
   <owner>ssid@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <summary>
@@ -66624,7 +66657,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Memory.Experimental.Extension2" units="MB">
+<histogram base="true" name="Memory.Experimental.Extension2" units="MB"
+    expires_after="M82">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
@@ -66635,7 +66669,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Memory.Experimental.Extension2.Small" units="KB">
+<histogram base="true" name="Memory.Experimental.Extension2.Small" units="KB"
+    expires_after="M82">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
@@ -66771,7 +66806,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.OomIntervention.NearOomDetectionEndReason"
-    enum="NearOomDetectionEndReason">
+    enum="NearOomDetectionEndReason" expires_after="M82">
   <owner>bashi@chromium.org</owner>
   <summary>
     Records the reason for stopping near-OOM detection. This isn't recorded when
@@ -67048,7 +67083,7 @@
 
 <histogram
     name="Memory.Experimental.OomIntervention.RendererTimeSinceLastNavigationAtOOM"
-    units="seconds">
+    units="seconds" expires_after="M82">
   <owner>ssid@chromium.org</owner>
   <owner>haraken@chromium.org</owner>
   <summary>
@@ -67462,7 +67497,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.ExtensionProcessCount" units="processes">
+<histogram name="Memory.ExtensionProcessCount" units="processes"
+    expires_after="M82">
   <owner>creis@chromium.org</owner>
   <owner>nasko@chromium.org</owner>
   <summary>
@@ -67552,7 +67588,7 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Graphics" units="MB">
+<histogram name="Memory.Graphics" units="MB" expires_after="M82">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
@@ -67742,7 +67778,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Contents.MemAllocatedMB" units="MB">
+<histogram name="Memory.OOMKill.Contents.MemAllocatedMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     System-wide memory allocation right after a renderer was killed by
@@ -67751,7 +67788,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Contents.MemAvailableMB" units="MB">
+<histogram name="Memory.OOMKill.Contents.MemAvailableMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     System-wide file-backed memory plus free memory right after a renderer was
@@ -67771,7 +67809,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Contents.MemShmemMB" units="MB">
+<histogram name="Memory.OOMKill.Contents.MemShmemMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     System-wide shared memory right after a renderer was killed by oom-killer.
@@ -67780,7 +67819,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Extensions.MemAllocatedMB" units="MB">
+<histogram name="Memory.OOMKill.Extensions.MemAllocatedMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     System-wide memory allocation right after a renderer was killed by
@@ -67800,7 +67840,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Extensions.MemGraphicsMB" units="MB">
+<histogram name="Memory.OOMKill.Extensions.MemGraphicsMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     Graphics driver (GEM object) memory right after a renderer was killed by
@@ -67808,7 +67849,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.OOMKill.Extensions.MemShmemMB" units="MB">
+<histogram name="Memory.OOMKill.Extensions.MemShmemMB" units="MB"
+    expires_after="M82">
   <owner>oshima@chromium.org</owner>
   <summary>
     System-wide shared memory right after a renderer was killed by oom-killer.
@@ -67862,12 +67904,13 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Compression.Latency"
-    units="microseconds">
+    units="microseconds" expires_after="M82">
   <owner>lizeb@chromium.org</owner>
   <summary>Time to compress a parkable string, in ms.</summary>
 </histogram>
 
-<histogram name="Memory.ParkableString.Compression.SizeKb" units="KB">
+<histogram name="Memory.ParkableString.Compression.SizeKb" units="KB"
+    expires_after="M82">
   <owner>lizeb@chromium.org</owner>
   <summary>
     Size of a compressed parkable string, recorded at compression time.
@@ -67904,12 +67947,13 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Decompression.Latency"
-    units="microseconds">
+    units="microseconds" expires_after="M82">
   <owner>lizeb@chromium.org</owner>
   <summary>Time to decompress a parkable string, in ms.</summary>
 </histogram>
 
-<histogram name="Memory.ParkableString.Decompression.SizeKb" units="KB">
+<histogram name="Memory.ParkableString.Decompression.SizeKb" units="KB"
+    expires_after="M82">
   <owner>lizeb@chromium.org</owner>
   <summary>
     Original size of a compressed parkable string, recorded at decompression
@@ -67918,7 +67962,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Decompression.ThroughputMBps"
-    units="MBps">
+    units="MBps" expires_after="M82">
   <owner>lizeb@chromium.org</owner>
   <summary>
     Original size of a compressed parkable string, recorded at decompression
@@ -68952,7 +68996,8 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Utility.PrivateSwapFootprint" units="MB">
+<histogram name="Memory.Utility.PrivateSwapFootprint" units="MB"
+    expires_after="M82">
   <owner>jam@chromium.org</owner>
   <summary>
     An amount of private memory of the utility process placed in swap (VmSwap).
@@ -69281,7 +69326,7 @@
 </histogram>
 
 <histogram name="MixedAutoupgrade.Websocket.Status"
-    enum="MixedContentAutoupgradeStatus">
+    enum="MixedContentAutoupgradeStatus" expires_after="M82">
   <owner>carlosil@chromium.org</owner>
   <summary>
     The status of a mixed content websocket that was autoupgraded to WSS.
@@ -69573,7 +69618,7 @@
   </summary>
 </histogram>
 
-<histogram name="MobileDownload.BytesDownloaded" units="KB">
+<histogram name="MobileDownload.BytesDownloaded" units="KB" expires_after="M82">
   <owner>qinmin@chromium.org</owner>
   <summary>
     Android: Records the total data downloaded by completion status.
@@ -69730,7 +69775,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.Result"
-    enum="DownloadLocationDialogResult">
+    enum="DownloadLocationDialogResult" expires_after="M82">
   <owner>xingliu@chromium.org</owner>
   <summary>
     Records whether the user accepted or dismissed the dialog to select a
@@ -69739,7 +69784,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.Type"
-    enum="DownloadLocationDialogType">
+    enum="DownloadLocationDialogType" expires_after="M82">
   <owner>xingliu@chromium.org</owner>
   <summary>
     Records the download location dialog type when the dialog is shown to the
@@ -69748,7 +69793,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.DirectoryType"
-    enum="DownloadLocationDirectoryType">
+    enum="DownloadLocationDirectoryType" expires_after="M82">
   <owner>dtrainor@chromium.org</owner>
   <owner>qinmin@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
@@ -69760,13 +69805,13 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Download.DirectoryType"
-    enum="DownloadLocationDirectoryType">
+    enum="DownloadLocationDirectoryType" expires_after="M82">
   <owner>xingliu@chromium.org</owner>
   <summary>Records the directory type when download is completed.</summary>
 </histogram>
 
 <histogram name="MobileDownload.Location.Setting.DirectoryType"
-    enum="DownloadLocationDirectoryType">
+    enum="DownloadLocationDirectoryType" expires_after="M82">
   <owner>xingliu@chromium.org</owner>
   <summary>
     Records the directory type when the user selects the download directory
@@ -69805,7 +69850,7 @@
 </histogram>
 
 <histogram name="MobileDownload.StoragePermission"
-    enum="MobileDownloadStoragePermission">
+    enum="MobileDownloadStoragePermission" expires_after="M82">
   <owner>qinmin@chromium.org</owner>
   <summary>
     Android: Records various counts when requesting the storage permission.
@@ -70026,7 +70071,8 @@
   </summary>
 </histogram>
 
-<histogram name="MobileStartup.DailyLaunchCount" units="units">
+<histogram name="MobileStartup.DailyLaunchCount" units="units"
+    expires_after="M82">
   <owner>tedchoc@chromium.org</owner>
   <summary>The count of launching Chrome mobile app within a day.</summary>
 </histogram>
@@ -70160,7 +70206,7 @@
 </histogram>
 
 <histogram base="true" name="MobileStartup.ToolbarFirstFocusStartupState"
-    enum="MobileStartupToolbarFirstFocusStartupState">
+    enum="MobileStartupToolbarFirstFocusStartupState" expires_after="M82">
   <owner>mheikal@chromium.org</owner>
   <summary>
     Android: The state of chrome startup at the first time the user focuses the
@@ -70183,7 +70229,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="MobileStartup.ToolbarFirstFocusTime2" units="ms">
+<histogram base="true" name="MobileStartup.ToolbarFirstFocusTime2" units="ms"
+    expires_after="M82">
   <owner>pasko@chromium.org</owner>
   <summary>
     Android: The time it takes from launch to the first time the user focuses
@@ -70192,7 +70239,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="MobileStartup.ToolbarInflationTime" units="ms">
+<histogram base="true" name="MobileStartup.ToolbarInflationTime" units="ms"
+    expires_after="M82">
   <owner>pasko@chromium.org</owner>
   <summary>
     Android: The time spent performing toolbar layout inflation.
@@ -70220,7 +70268,8 @@
   </summary>
 </histogram>
 
-<histogram name="ModuleBlacklistCache.DeleteResult" enum="BooleanSuccess">
+<histogram name="ModuleBlacklistCache.DeleteResult" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>pmonette@chromium.org</owner>
   <summary>
     The result of deleting the module blacklist cache when third-party module
@@ -70413,7 +70462,8 @@
   <summary>Methods where leveldb's Mojo environment has IO errors.</summary>
 </histogram>
 
-<histogram name="MojoLevelDBEnv.IOError.BFE" enum="PlatformFileError">
+<histogram name="MojoLevelDBEnv.IOError.BFE" enum="PlatformFileError"
+    expires_after="M82">
   <owner>mek@chromium.org</owner>
   <summary>
     Errors (base::File::Error) encountered by a single leveldb method in
@@ -70422,7 +70472,7 @@
 </histogram>
 
 <histogram name="MojoLevelDBEnv.RetryRecoveredFromErrorIn"
-    enum="PlatformFileError">
+    enum="PlatformFileError" expires_after="M82">
   <owner>mek@chromium.org</owner>
   <summary>
     When Mojo LevelDBEnv successfully retries an operation that had failed,
@@ -70430,7 +70480,8 @@
   </summary>
 </histogram>
 
-<histogram name="MojoLevelDBEnv.TimeUntilSuccessFor" units="ms">
+<histogram name="MojoLevelDBEnv.TimeUntilSuccessFor" units="ms"
+    expires_after="M82">
   <owner>mek@chromium.org</owner>
   <summary>
     Time Mojo LevelDBEnv slept before successfully completing this operation. 0
@@ -104919,6 +104970,17 @@
   </summary>
 </histogram>
 
+<histogram name="Permissions.Prompt.Infobar.DetailsExpanded" enum="Boolean"
+    expires_after="M80">
+  <owner>andypaicu@chromium.org</owner>
+  <owner>engedy@chromium.org</owner>
+  <owner>hkamila@chromium.org</owner>
+  <summary>
+    Tracks whether the user has expanded the infobar notification permission
+    request.
+  </summary>
+</histogram>
+
 <histogram name="Permissions.Prompt.MergedBubbleAccepted"
     enum="PermissionRequestType" expires_after="2017-11-08">
   <obsolete>
@@ -111760,7 +111822,8 @@
   </summary>
 </histogram>
 
-<histogram name="Prerender.PrerendersPerSessionCount" units="units">
+<histogram name="Prerender.PrerendersPerSessionCount" units="units"
+    expires_after="M82">
   <owner>pasko@chromium.org</owner>
   <summary>
     The number of sessions that have at least X successful prerenders. Recorded
@@ -128469,7 +128532,8 @@
   </summary>
 </histogram>
 
-<histogram name="Security.SafetyTips.OpenTime.DismissWithClose" units="ms">
+<histogram name="Security.SafetyTips.OpenTime.DismissWithClose" units="ms"
+    expires_after="M82">
   <owner>jdeblasio@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <owner>livvielin@chromium.org</owner>
@@ -132575,7 +132639,7 @@
 </histogram>
 
 <histogram name="SettingsResetPrompt.ConfigError"
-    enum="SettingsResetPromptConfigError">
+    enum="SettingsResetPromptConfigError" expires_after="M82">
   <owner>alito@chromium.org</owner>
   <summary>
     Indicates if an error was detected in the settings reset prompt config data
@@ -132634,7 +132698,8 @@
   </summary>
 </histogram>
 
-<histogram name="SettingsResetPrompt.PromptRequired" enum="BooleanRequired">
+<histogram name="SettingsResetPrompt.PromptRequired" enum="BooleanRequired"
+    expires_after="M82">
   <owner>alito@chromium.org</owner>
   <summary>
     Indicates whether the settings reset prompt should be shown to the user
@@ -132643,7 +132708,7 @@
 </histogram>
 
 <histogram name="SettingsResetPrompt.ResetState"
-    enum="SettingsResetPromptResetState">
+    enum="SettingsResetPromptResetState" expires_after="M82">
   <owner>alito@chromium.org</owner>
   <summary>
     Indicates whether the settings reset prompt is enabled for the user's
@@ -132708,7 +132773,8 @@
   </summary>
 </histogram>
 
-<histogram name="Setup.Install.ApplyArchivePatchTime" units="ms">
+<histogram name="Setup.Install.ApplyArchivePatchTime" units="ms"
+    expires_after="M82">
   <obsolete>
     Deprecated 06/2019.
   </obsolete>
@@ -132765,7 +132831,8 @@
   </summary>
 </histogram>
 
-<histogram name="Setup.Install.HasArchivePatch" enum="Boolean">
+<histogram name="Setup.Install.HasArchivePatch" enum="Boolean"
+    expires_after="M82">
   <obsolete>
     Deprecated 06/2019.
   </obsolete>
@@ -132777,7 +132844,8 @@
   </summary>
 </histogram>
 
-<histogram name="Setup.Install.LzmaUnPackNTSTATUS" enum="NTSTATUS">
+<histogram name="Setup.Install.LzmaUnPackNTSTATUS" enum="NTSTATUS"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>
     Record the NTSTATUS code of unpacking the contents of a 7z file.
@@ -132785,14 +132853,15 @@
 </histogram>
 
 <histogram base="true" name="Setup.Install.LzmaUnPackResult"
-    enum="WinGetLastError">
+    enum="WinGetLastError" expires_after="M82">
   <owner>etiennep@chromium.org</owner>
   <summary>
     Record the return value of unpacking the contents of a 7z file.
   </summary>
 </histogram>
 
-<histogram name="Setup.Install.LzmaUnPackStatus" enum="UnPackStatus">
+<histogram name="Setup.Install.LzmaUnPackStatus" enum="UnPackStatus"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>Record the status of unpacking the contents of a 7z file.</summary>
 </histogram>
@@ -132988,7 +133057,8 @@
   </summary>
 </histogram>
 
-<histogram name="SharedMemory.CreateWinError" enum="WinGetLastError">
+<histogram name="SharedMemory.CreateWinError" enum="WinGetLastError"
+    expires_after="M82">
   <owner>bcwhite@chromium.org</owner>
   <summary>
     A histogram entry is emitted each time a shared memory object could not be
@@ -133984,7 +134054,8 @@
   </summary>
 </histogram>
 
-<histogram name="Signin.ForceSigninVerificationTime" units="ms">
+<histogram name="Signin.ForceSigninVerificationTime" units="ms"
+    expires_after="M82">
   <owner>zmin@chromium.org</owner>
   <summary>
     Elapsed time of the force-sign-in verfication if it's finished. It includes
@@ -137689,7 +137760,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.Cleaner.HasCompleted" enum="SRTCompleted">
+<histogram name="SoftwareReporter.Cleaner.HasCompleted" enum="SRTCompleted"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The state of software reporter cleaner tool runs. A value of &quot;Not
@@ -137764,7 +137836,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.Cleaner.TimeToCompleteDownload" units="ms">
+<histogram name="SoftwareReporter.Cleaner.TimeToCompleteDownload" units="ms"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The time between the first attempt to download the Chrome Cleanup tool and a
@@ -137773,7 +137846,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.Cleaner.Version" units="units">
+<histogram name="SoftwareReporter.Cleaner.Version" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>The build version of the software reporter cleaner tool.</summary>
 </histogram>
@@ -137814,7 +137888,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.FoundUwSReadError" enum="BooleanError">
+<histogram name="SoftwareReporter.FoundUwSReadError" enum="BooleanError"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     Whether there was an error reading the registry key containing UwS found by
@@ -137836,7 +137911,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.LastUploadResult" enum="BooleanSuccess">
+<histogram name="SoftwareReporter.LastUploadResult" enum="BooleanSuccess"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>The result of the most recent SRT log upload.</summary>
 </histogram>
@@ -137869,7 +137945,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.MajorVersion" units="units">
+<histogram name="SoftwareReporter.MajorVersion" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     As SoftwareReporter.MinorVersion, but a double word combination of the other
@@ -137879,7 +137956,7 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.MemoryUsed" units="KB">
+<histogram name="SoftwareReporter.MemoryUsed" units="KB" expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The memory used by the software reporter tool as reported by the tool itself
@@ -137887,7 +137964,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.MinorVersion" units="units">
+<histogram name="SoftwareReporter.MinorVersion" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The last component of the version of the software reporter that was executed
@@ -137940,7 +138018,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.PromptDialog.TimeUntilDone" units="ms">
+<histogram name="SoftwareReporter.PromptDialog.TimeUntilDone" units="ms"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The time between the Chrome Cleaner dialog being shown and the dialog being
@@ -137990,7 +138069,7 @@
 </histogram>
 
 <histogram name="SoftwareReporter.ReporterSequenceResult"
-    enum="SoftwareReporterSequenceResult">
+    enum="SoftwareReporterSequenceResult" expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>Indicates the result of a reporter sequence once it ends.</summary>
 </histogram>
@@ -138004,7 +138083,7 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.RunningTime" units="ms">
+<histogram name="SoftwareReporter.RunningTime" units="ms" expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The amount of time it took to run the software reporter tool as reported by
@@ -138013,7 +138092,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.RunningTimeAccordingToChrome" units="ms">
+<histogram name="SoftwareReporter.RunningTimeAccordingToChrome" units="ms"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The amount of time it took for the software reporter to run as measured by
@@ -138022,7 +138102,7 @@
 </histogram>
 
 <histogram name="SoftwareReporter.RunningTimeRegistryError"
-    enum="SwReporterRunningTimeRegistryError">
+    enum="SwReporterRunningTimeRegistryError" expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     Error encountered when reading the software reporter tool's start and end
@@ -138040,7 +138120,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.Step" enum="SwReporterStep">
+<histogram name="SoftwareReporter.Step" enum="SwReporterStep"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The registration and execution steps for the software reporter.
@@ -138056,7 +138137,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.UploadFailureCount" units="units">
+<histogram name="SoftwareReporter.UploadFailureCount" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The total count of SRT log upload failures experienced by this machine for
@@ -138064,7 +138146,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.UploadLongestFailureRun" units="units">
+<histogram name="SoftwareReporter.UploadLongestFailureRun" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The longest run of upload failures logged by SRT runs. This value is
@@ -138072,7 +138155,8 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.UploadSuccessCount" units="units">
+<histogram name="SoftwareReporter.UploadSuccessCount" units="units"
+    expires_after="M82">
   <owner>joenotcharles@google.com</owner>
   <summary>
     The total count of successful SRT log uploads experienced by this machine
@@ -138806,7 +138890,7 @@
 </histogram>
 
 <histogram name="Stability.BadMessageTerminated.Extensions"
-    enum="BadMessageReasonExtensions">
+    enum="BadMessageReasonExtensions" expires_after="M82">
   <owner>jamescook@chromium.org</owner>
   <summary>
     Count of extension processes killed because they sent an IPC that couldn't
@@ -138844,7 +138928,8 @@
   </summary>
 </histogram>
 
-<histogram name="Stability.BrowserExitCodes" enum="WindowsExitCode">
+<histogram name="Stability.BrowserExitCodes" enum="WindowsExitCode"
+    expires_after="M82">
   <owner>siggi@chromium.org</owner>
   <summary>
     Records the exit code of the browser process (on Windows) from the previous
@@ -153504,7 +153589,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCScavenger" units="ms">
+<histogram name="V8.GCScavenger" units="ms" expires_after="M82">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent in scavenging phase of GC.</summary>
 </histogram>
@@ -162914,7 +162999,8 @@
   </summary>
 </histogram>
 
-<histogram name="WindowManager.AppWindowCountPerLoad" units="units">
+<histogram name="WindowManager.AppWindowCountPerLoad" units="units"
+    expires_after="M82">
   <owner>kuscher@chromium.org</owner>
   <summary>
     The number of app windows open when a load completes. This includes windows
@@ -162936,7 +163022,8 @@
   </summary>
 </histogram>
 
-<histogram name="WindowManager.PopUpWindowCountPerLoad" units="units">
+<histogram name="WindowManager.PopUpWindowCountPerLoad" units="units"
+    expires_after="M82">
   <owner>kuscher@chromium.org</owner>
   <summary>
     The number of popup windows open when a load completes. Popup windows only
@@ -162945,7 +163032,8 @@
   </summary>
 </histogram>
 
-<histogram name="WindowManager.TabbedWindowCountPerLoad" units="units">
+<histogram name="WindowManager.TabbedWindowCountPerLoad" units="units"
+    expires_after="M82">
   <owner>kuscher@chromium.org</owner>
   <summary>
     The number of tabbed windows open when a load completes. A tabbed window is
@@ -162986,7 +163074,8 @@
   </summary>
 </histogram>
 
-<histogram name="Windows.GetVersionExVersion" enum="WindowsVersion">
+<histogram name="Windows.GetVersionExVersion" enum="WindowsVersion"
+    expires_after="M82">
   <owner>wfh@chromium.org</owner>
   <owner>brucedawson@chromium.org</owner>
   <summary>
@@ -163110,7 +163199,8 @@
   </summary>
 </histogram>
 
-<histogram name="Windows.OOPSelectFileDialog.ProcessError" enum="BooleanError">
+<histogram name="Windows.OOPSelectFileDialog.ProcessError" enum="BooleanError"
+    expires_after="M82">
   <owner>pmonette@chromium.org</owner>
   <summary>
     Indicates whether a connection error occured between the browser and the
@@ -163129,7 +163219,8 @@
   </summary>
 </histogram>
 
-<histogram name="Windows.PatchLevel" enum="WindowsPatchLevel">
+<histogram name="Windows.PatchLevel" enum="WindowsPatchLevel"
+    expires_after="M82">
   <owner>wfh@chromium.org</owner>
   <owner>brucedawson@chromium.org</owner>
   <summary>
diff --git a/tools/perf/measurements/dual_metric_measurement.py b/tools/perf/measurements/dual_metric_measurement.py
index 3db68dd..c0ce1cf6 100644
--- a/tools/perf/measurements/dual_metric_measurement.py
+++ b/tools/perf/measurements/dual_metric_measurement.py
@@ -7,12 +7,10 @@
 
 
 class DualMetricMeasurement(story_test.StoryTest):
-  """Test class for a benchmark that aggregates all metrics.
+  """Test class supporting both ad hoc measurements and trace based metrics.
 
-    Assumes both javascript as well as tracing metrics might be defined.
-
-    All pages associated with this measurement must implement
-    GetJavascriptMetricValues().
+    Currently only works with PressStory pages, which implement
+    GetMeasurements().
   """
   def __init__(self, tbm_options):
     super(DualMetricMeasurement, self).__init__()
@@ -43,8 +41,8 @@
       for histogram in results.current_page.GetJavascriptMetricHistograms():
         results.AddHistogram(histogram)
     else:
-      for value in results.current_page.GetJavascriptMetricValues():
-        results.AddValue(value)
+      for value in results.current_page.GetMeasurements():
+        results.AddMeasurement(**value)
       # This call is necessary to convert the current ScalarValues to
       # histograms before more histograms are added.  If we don't,
       # when histograms get added by TBM2 page_test_results will see those and
diff --git a/tools/perf/page_sets/jetstream2_pages.py b/tools/perf/page_sets/jetstream2_pages.py
index 44a9c5e..fe1c7aa4 100644
--- a/tools/perf/page_sets/jetstream2_pages.py
+++ b/tools/perf/page_sets/jetstream2_pages.py
@@ -4,7 +4,7 @@
 
 from page_sets import press_story
 from telemetry import story
-from telemetry.value import scalar
+
 
 class Jetstream2Story(press_story.PressStory):
   URL = 'http://browserbench.org/JetStream/'
@@ -57,25 +57,19 @@
         })();"""
     )
 
-    self.AddJavascriptMetricValue(
-      scalar.ScalarValue(self, 'Score', 'score', score))
-
+    self.AddMeasurement('Score', 'score', score)
     for k, v in result.iteritems():
       # Replace '.' in the benchmark name, because '.' is interpreted
       # as a sub-category of the metric
       benchmark = str(k).replace('.', '_')
-      self.AddJavascriptMetricValue(scalar.ScalarValue(
-          self, benchmark, 'score', v['Score'],
-          important=False,
-          description='Geometric mean of the iterations'))
-      self.AddJavascriptMetricValue(scalar.ScalarValue(
-          self, benchmark+'.Iterations', 'number', v['Iterations'],
-          important=False,
-          description='Total number of iterations'))
+      self.AddMeasurement(
+          benchmark, 'score', v['Score'],
+          description='Geometric mean of the iterations')
+      self.AddMeasurement(
+          '%s.Iterations' % benchmark, 'number', v['Iterations'],
+          description='Total number of iterations')
       for sub_k, sub_v in v['SubResults'].iteritems():
-        self.AddJavascriptMetricValue(scalar.ScalarValue(
-            self, benchmark+'.'+str(sub_k), 'score', sub_v,
-            important=False))
+        self.AddMeasurement('%s.%s' % (benchmark, sub_k), 'score', sub_v)
 
 
 class Jetstream2StorySet(story.StorySet):
diff --git a/tools/perf/page_sets/jetstream_pages.py b/tools/perf/page_sets/jetstream_pages.py
index b77c505..1ef0aae 100644
--- a/tools/perf/page_sets/jetstream_pages.py
+++ b/tools/perf/page_sets/jetstream_pages.py
@@ -6,7 +6,6 @@
 from page_sets import press_story
 from telemetry import story
 from telemetry.util import statistics
-from telemetry.value import list_of_scalar_values
 
 
 class JetstreamStory(press_story.PressStory):
@@ -41,9 +40,7 @@
 
     all_score_lists = []
     for k, v in result.iteritems():
-      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
-          self, k.replace('.', '_'), 'score', v['result'],
-          important=False))
+      self.AddMeasurement(k.replace('.', '_'), 'score', v['result'])
       # Collect all test scores to compute geometric mean.
       for i, score in enumerate(v['result']):
         if len(all_score_lists) <= i:
@@ -52,9 +49,7 @@
     all_scores = []
     for score_list in all_score_lists:
       all_scores.append(statistics.GeometricMean(score_list))
-    self.AddJavascriptMetricValue(
-        list_of_scalar_values.ListOfScalarValues(
-        self, 'Score', 'score', all_scores))
+    self.AddMeasurement('Score', 'score', all_scores)
 
 
 class JetstreamStorySet(story.StorySet):
diff --git a/tools/perf/page_sets/kraken_pages.py b/tools/perf/page_sets/kraken_pages.py
index b62adf79..7db46ab 100644
--- a/tools/perf/page_sets/kraken_pages.py
+++ b/tools/perf/page_sets/kraken_pages.py
@@ -6,9 +6,6 @@
 from page_sets import press_story
 from telemetry import story
 
-from telemetry.value import list_of_scalar_values
-from telemetry.value import scalar
-
 
 DESCRIPTIONS = {
     'ai-astar':
@@ -78,18 +75,16 @@
     for key in result_dict:
       if key == 'v':
         continue
-      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
-          self, key, 'ms', result_dict[key], important=False,
-          description=DESCRIPTIONS.get(key)))
+      self.AddMeasurement(key, 'ms', result_dict[key],
+                          description=DESCRIPTIONS.get(key))
       total += _Mean(result_dict[key])
 
     # TODO(tonyg/nednguyen): This measurement shouldn't calculate Total. The
     # results system should do that for us.
-    self.AddJavascriptMetricValue(scalar.ScalarValue(
-        self, 'Total', 'ms', total,
-        description='Total of the means of the results for each type '
-                    'of benchmark in [Mozilla\'s Kraken JavaScript benchmark]'
-                    '(http://krakenbenchmark.mozilla.org/)'))
+    self.AddMeasurement(
+        'Total', 'ms', total,
+        description='Sum of the mean runtime for each type of benchmark in '
+                    "Mozilla's Kraken JavaScript benchmark")
 
 
 class KrakenStorySet(story.StorySet):
diff --git a/tools/perf/page_sets/octane_pages.py b/tools/perf/page_sets/octane_pages.py
index 35457ef..033046d 100644
--- a/tools/perf/page_sets/octane_pages.py
+++ b/tools/perf/page_sets/octane_pages.py
@@ -2,12 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 from telemetry import story
-
 from telemetry.util import statistics
-from telemetry.value import scalar
 
 from page_sets import press_story
 
+
 _GB = 1024 * 1024 * 1024
 
 DESCRIPTIONS = {
@@ -91,18 +90,15 @@
       if 'Skipped' not in score_and_name[1]:
         name = score_and_name[0]
         score = float(score_and_name[1])
-        self.AddJavascriptMetricValue(scalar.ScalarValue(
-            self, name, 'score', score, important=False,
-            description=DESCRIPTIONS.get(name)))
+        self.AddMeasurement(name, 'score', score,
+                            description=DESCRIPTIONS.get(name))
 
         # Collect all test scores to compute geometric mean.
         all_scores.append(score)
     total = statistics.GeometricMean(all_scores)
-    self.AddJavascriptMetricValue(
-        scalar.ScalarValue(self, 'Total.Score', 'score', total,
-                           description='Geometric mean of the scores of each '
-                           'individual benchmark in the Octane '
-                           'benchmark collection.'))
+    self.AddMeasurement('Total.Score', 'score', total,
+                        description='Geometric mean of the scores of each '
+                        'individual benchmark in the Octane collection.')
 
 
 class OctaneStorySet(story.StorySet):
diff --git a/tools/perf/page_sets/press_story.py b/tools/perf/page_sets/press_story.py
index 11df1fd..1eedc27 100644
--- a/tools/perf/page_sets/press_story.py
+++ b/tools/perf/page_sets/press_story.py
@@ -14,14 +14,16 @@
     Example Implementation:
 
     class FooPressStory:
-      URL = 'http://foo'
+      URL = 'http://example.com/foo_story'
+      NAME = 'FooStory'
 
       def ExecuteTest(self, action_runner):
-        //Execute some javascript
+        // Execute some javascript
 
       def ParseTestResults(self, action_runner):
-        some_value = action_runner.EvaluateJavascript("some javascript")
-        self.AddJavascriptMetricValue(some_value)
+        js_code = 'some_js_expression;'
+        self.AddJavaScriptMeasurement(name, unit, js_code)
+
   """
   URL = None
   DETERMINISTIC_JS = False
@@ -33,19 +35,59 @@
         base_dir=ps.base_dir,
         make_javascript_deterministic=self.DETERMINISTIC_JS,
         name=self.NAME if self.NAME else self.URL)
-    self._values = []
+    self._measurements = []
     self._histogram_values = []
+    self._action_runner = None
 
-  def GetJavascriptMetricValues(self):
-    return self._values
+  def AddMeasurement(self, name, unit, samples, description=None):
+    """Record an ad-hoc measurement.
 
-  def AddJavascriptMetricValue(self, value):
-    self._values.append(value)
+    Args:
+      name: A string with the name of the measurement (e.g. 'score', 'runtime',
+        etc).
+      unit: A string specifying the unit used for measurements (e.g. 'ms',
+        'count', etc).
+      samples: Either a single numeric value or a list of numeric values to
+        record as part of this measurement.
+      description: An optional string with a short human readable description
+        of the measurement.
+    """
+    # TODO(crbug.com/999484): Ideally, these should be recorded directly into
+    # the results object, rather than held on this temporary list. That needs,
+    # however, another slight refactor to make the results object available at
+    # this point.
+    self._measurements.append({'name': name, 'unit': unit, 'samples': samples,
+                               'description': description})
+
+  def AddJavaScriptMeasurement(self, name, unit, code, **kwargs):
+    """Run some JavaScript to obtain and record an ad-hoc measurements.
+
+    Args:
+      name: A string with the name of the measurement (e.g. 'score', 'runtime',
+        etc).
+      unit: A string specifying the unit used for measurements (e.g. 'ms',
+        'count', etc).
+      code: A piece of JavaScript code to run on the current tab, it must
+        return either a single or a list of numeric values. These are the
+        values for the measurement to be recorded.
+      description: An optional string with a short human readable description
+        of the measurement.
+      Other keyword arguments provide values to be interpolated within
+          the JavaScript code. See telemetry.util.js_template for details.
+    """
+    description = kwargs.pop('description', None)
+    samples = self._action_runner.EvaluateJavaScript(code, **kwargs)
+    self.AddMeasurement(name, unit, samples, description)
+
+  def GetMeasurements(self):
+    return self._measurements
 
   def GetJavascriptMetricHistograms(self):
+    """DEPRECATED: Use measurements instead."""
     return self._histogram_values
 
   def AddJavascriptMetricHistogram(self, value):
+    """DEPRECATED: Use measurements instead."""
     self._histogram_values.append(value)
 
   def ExecuteTest(self, action_runner):
@@ -55,5 +97,9 @@
     pass
 
   def RunPageInteractions(self, action_runner):
-    self.ExecuteTest(action_runner)
-    self.ParseTestResults(action_runner)
+    self._action_runner = action_runner
+    try:
+      self.ExecuteTest(action_runner)
+      self.ParseTestResults(action_runner)
+    finally:
+      self._action_runner = None
diff --git a/tools/perf/page_sets/speedometer_pages.py b/tools/perf/page_sets/speedometer_pages.py
index 564d1bc..ed5f7e22 100644
--- a/tools/perf/page_sets/speedometer_pages.py
+++ b/tools/perf/page_sets/speedometer_pages.py
@@ -3,10 +3,9 @@
 # found in the LICENSE file.
 from telemetry import story
 
-from telemetry.value import list_of_scalar_values
-
 from page_sets import press_story
 
+
 class SpeedometerStory(press_story.PressStory):
   URL='http://browserbench.org/Speedometer/'
 
@@ -44,30 +43,24 @@
         timeout=600)
 
   def ParseTestResults(self, action_runner):
-    self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
-        self, 'Total', 'ms',
-        action_runner.EvaluateJavaScript('benchmarkClient._timeValues'),
-        important=True))
-    self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
-        self, 'RunsPerMinute', 'score',
-        action_runner.EvaluateJavaScript(
-            '[parseFloat(document.getElementById("result-number").innerText)];'
-        ),
-        important=True))
+    self.AddJavaScriptMeasurement('Total', 'ms', 'benchmarkClient._timeValues')
+    self.AddJavaScriptMeasurement(
+        'RunsPerMinute', 'score',
+        '[parseFloat(document.getElementById("result-number").innerText)];')
 
     # Extract the timings for each suite
     for suite_name in self.enabled_suites:
-      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
-          self, suite_name, 'ms',
-          action_runner.EvaluateJavaScript("""
-              var suite_times = [];
-              for(var i = 0; i < benchmarkClient.iterationCount; i++) {
-                suite_times.push(
-                    benchmarkClient._measuredValues[i].tests[{{ key }}].total);
-              };
-              suite_times;
-              """,
-              key=suite_name), important=False))
+      self.AddJavaScriptMeasurement(
+          suite_name, 'ms',
+          """
+          var suite_times = [];
+          for(var i = 0; i < benchmarkClient.iterationCount; i++) {
+            suite_times.push(
+                benchmarkClient._measuredValues[i].tests[{{ key }}].total);
+          };
+          suite_times;
+          """,
+          key=suite_name)
 
 
 class SpeedometerStorySet(story.StorySet):
diff --git a/tools/run-swarmed.py b/tools/run-swarmed.py
index dfae81b..0c87ff6 100755
--- a/tools/run-swarmed.py
+++ b/tools/run-swarmed.py
@@ -46,7 +46,7 @@
   """
   index, args, isolated_hash = args
   json_file = os.path.join(args.results, '%d.json' % index)
-  trigger_args = [
+  trigger_args = [sys.executable,
       'tools/swarming_client/swarming.py', 'trigger',
       '-S', 'https://chromium-swarm.appspot.com',
       '-I', 'https://isolateserver.appspot.com',
@@ -62,7 +62,9 @@
       '-d', 'gpu', 'none',
       '-d', 'cpu', args.arch,
     ]
-  elif args.target_os == 'android':
+  elif args.target_os == 'android' or args.target_os == 'win':
+    if args.target_os == 'android':
+      trigger_args += ['-d', 'device_os', args.device_os]
     # The canonical version numbers are stored in the infra repository here:
     # build/scripts/slave/recipe_modules/swarming/api.py
     cpython_version = 'version:2.7.15.chromium14'
@@ -77,7 +79,6 @@
         '.swarming_module:infra/tools/luci/vpython/${platform}:' +
         vpython_version)
     trigger_args += [
-        '-d', 'device_os', args.device_os,
         '--cipd-package', cpython_pkg,
         '--cipd-package', vpython_native_pkg,
         '--cipd-package', vpython_pkg,
@@ -104,7 +105,7 @@
 
 def _Collect(spawn_result):
   index, json_file, args = spawn_result
-  p = subprocess.Popen([
+  p = subprocess.Popen([sys.executable,
     'tools/swarming_client/swarming.py', 'collect',
     '-S', 'https://chromium-swarm.appspot.com',
     '--json', json_file,
@@ -177,8 +178,8 @@
     else:
       args.arch = 'x86-64'
 
-  subprocess.check_call(
-      ['tools/mb/mb.py', 'isolate', '//' + args.out_dir, args.target_name])
+  subprocess.check_call([sys.executable, 'tools/mb/mb.py',
+      'isolate', '//' + args.out_dir, args.target_name])
 
   print('If you get authentication errors, follow:')
   print(
@@ -187,7 +188,7 @@
 
   print('Uploading to isolate server, this can take a while...')
   archive_output = subprocess.check_output(
-      ['tools/swarming_client/isolate.py', 'archive',
+      [sys.executable,'tools/swarming_client/isolate.py', 'archive',
        '-I', 'https://isolateserver.appspot.com',
        '-i', os.path.join(args.out_dir, args.target_name + '.isolate'),
        '-s', os.path.join(args.out_dir, args.target_name + '.isolated')])
diff --git a/ui/base/ui_base_types.h b/ui/base/ui_base_types.h
index efd565f..f79b03a 100644
--- a/ui/base/ui_base_types.h
+++ b/ui/base/ui_base_types.h
@@ -25,9 +25,10 @@
 
 // Dialog button identifiers used to specify which buttons to show the user.
 enum DialogButton {
-  DIALOG_BUTTON_NONE   = 0,
-  DIALOG_BUTTON_OK     = 1,
+  DIALOG_BUTTON_NONE = 0,
+  DIALOG_BUTTON_OK = 1,
   DIALOG_BUTTON_CANCEL = 2,
+  DIALOG_BUTTON_LAST = DIALOG_BUTTON_CANCEL,
 };
 
 // Specifies the type of modality applied to a window. Different modal
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index f939395..0fe9705 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -679,9 +679,10 @@
   const int frame_width = std::max(title_bar_width, client_area_width);
   DialogDelegate* dialog_delegate =
       GetWidget()->widget_delegate()->AsDialogDelegate();
-  return dialog_delegate && dialog_delegate->ShouldSnapFrameWidth()
-             ? LayoutProvider::Get()->GetSnappedDialogWidth(frame_width)
-             : frame_width;
+  bool snapping = dialog_delegate &&
+                  dialog_delegate->GetDialogButtons() != ui::DIALOG_BUTTON_NONE;
+  return snapping ? LayoutProvider::Get()->GetSnappedDialogWidth(frame_width)
+                  : frame_width;
 }
 
 gfx::Size BubbleFrameView::GetFrameSizeForClientSize(
diff --git a/ui/views/bubble/bubble_frame_view_unittest.cc b/ui/views/bubble/bubble_frame_view_unittest.cc
index 53db9bd..c5622c6 100644
--- a/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -990,7 +990,10 @@
   ~TestWidthSnapDelegate() override { GetWidget()->CloseNow(); }
 
   // TestBubbleDialogDelegateView:
-  bool ShouldSnapFrameWidth() const override { return should_snap_; }
+  int GetDialogButtons() const override {
+    // Only dialogs with buttons get snapped by BubbleFrameView.
+    return should_snap_ ? ui::DIALOG_BUTTON_OK : ui::DIALOG_BUTTON_NONE;
+  }
 
  private:
   bool should_snap_;
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 61d4e59..d0832c3 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -121,6 +121,9 @@
 
 base::string16 DialogDelegate::GetDialogButtonLabel(
     ui::DialogButton button) const {
+  if (!GetParams().button_labels[button].empty())
+    return GetParams().button_labels[button];
+
   if (button == ui::DIALOG_BUTTON_OK)
     return l10n_util::GetStringUTF16(IDS_APP_OK);
   if (button == ui::DIALOG_BUTTON_CANCEL) {
@@ -176,10 +179,6 @@
   button->SetIsDefault(is_default);
 }
 
-bool DialogDelegate::ShouldSnapFrameWidth() const {
-  return GetDialogButtons() != ui::DIALOG_BUTTON_NONE;
-}
-
 View* DialogDelegate::GetInitiallyFocusedView() {
   // Focus the default button if any.
   const DialogClientView* dcv = GetDialogClientView();
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 5431cf1b..48d436f 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -47,6 +47,12 @@
     // should use the platform-native frame, and all other dialogs should use
     // the Views-styled one.
     bool custom_frame = true;
+
+    // Text labels for the buttons on this dialog. Any button without a label
+    // here will get the default text for its type from GetDialogButtonLabel.
+    // Prefer to use this field (via set_button_label) rather than override
+    // GetDialogButtonLabel - see https://crbug.com/1011446
+    base::string16 button_labels[ui::DIALOG_BUTTON_LAST + 1];
   };
 
   DialogDelegate();
@@ -125,10 +131,6 @@
   // the typical.
   virtual void UpdateButton(LabelButton* button, ui::DialogButton type);
 
-  // Returns true if this dialog should snap the frame width based on the
-  // LayoutProvider's snapping.
-  virtual bool ShouldSnapFrameWidth() const;
-
   // Overridden from WidgetDelegate:
   View* GetInitiallyFocusedView() override;
   DialogDelegate* AsDialogDelegate() override;
@@ -159,6 +161,10 @@
   void set_use_custom_frame(bool use) { params_.custom_frame = use; }
   bool use_custom_frame() const { return params_.custom_frame; }
 
+  void set_button_label(ui::DialogButton button, base::string16 label) {
+    params_.button_labels[button] = label;
+  }
+
  protected:
   ~DialogDelegate() override;
 
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 95754f97..7dc2afb 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -437,11 +437,4 @@
   dialog_widget->CloseNow();
 }
 
-TEST_F(DialogTest, DontSnapWithoutButtons) {
-  TestDialog dialog;
-  EXPECT_TRUE(dialog.ShouldSnapFrameWidth());
-  dialog.set_dialog_buttons(ui::DIALOG_BUTTON_NONE);
-  EXPECT_FALSE(dialog.ShouldSnapFrameWidth());
-}
-
 }  // namespace views
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
index 9c30c9b..2141c6c 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
@@ -51,6 +51,18 @@
     }
 
     @Override
+    public boolean canGoBack() {
+        return NavigationControllerImplJni.get().canGoBack(
+                mNativeNavigationController, NavigationControllerImpl.this);
+    }
+
+    @Override
+    public boolean canGoForward() {
+        return NavigationControllerImplJni.get().canGoForward(
+                mNativeNavigationController, NavigationControllerImpl.this);
+    }
+
+    @Override
     public void reload() {
         NavigationControllerImplJni.get().reload(
                 mNativeNavigationController, NavigationControllerImpl.this);
@@ -139,6 +151,8 @@
                 long nativeNavigationControllerImpl, NavigationControllerImpl caller, String uri);
         void goBack(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
         void goForward(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+        boolean canGoBack(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+        boolean canGoForward(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
         void reload(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
         void stop(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
         int getNavigationListSize(
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl
index fe744bf..bb9cf32 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl
@@ -20,4 +20,8 @@
   int getNavigationListCurrentIndex() = 6;
 
   String getNavigationEntryDisplayUri(in int index) = 7;
+
+  boolean canGoBack() = 8;
+
+  boolean canGoForward() = 9;
 }
diff --git a/weblayer/browser/navigation_controller_impl.cc b/weblayer/browser/navigation_controller_impl.cc
index 22a380f..4f31793 100644
--- a/weblayer/browser/navigation_controller_impl.cc
+++ b/weblayer/browser/navigation_controller_impl.cc
@@ -82,6 +82,14 @@
   browser_controller_->web_contents()->GetController().GoForward();
 }
 
+bool NavigationControllerImpl::CanGoBack() {
+  return browser_controller_->web_contents()->GetController().CanGoBack();
+}
+
+bool NavigationControllerImpl::CanGoForward() {
+  return browser_controller_->web_contents()->GetController().CanGoForward();
+}
+
 void NavigationControllerImpl::Reload() {
   browser_controller_->web_contents()->GetController().Reload(
       content::ReloadType::NORMAL, false);
diff --git a/weblayer/browser/navigation_controller_impl.h b/weblayer/browser/navigation_controller_impl.h
index 339ced37..77fba9fc 100644
--- a/weblayer/browser/navigation_controller_impl.h
+++ b/weblayer/browser/navigation_controller_impl.h
@@ -40,6 +40,12 @@
   void GoForward(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
     GoForward();
   }
+  bool CanGoBack(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
+    return CanGoBack();
+  }
+  bool CanGoForward(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
+    return CanGoForward();
+  }
   void Reload(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
     Reload();
   }
@@ -68,6 +74,8 @@
   void Navigate(const GURL& url) override;
   void GoBack() override;
   void GoForward() override;
+  bool CanGoBack() override;
+  bool CanGoForward() override;
   void Reload() override;
   void Stop() override;
   int GetNavigationListSize() override;
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
index 6d905013..5adcb37 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigationController.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
@@ -61,6 +61,22 @@
         }
     }
 
+    public boolean canGoBack() {
+        try {
+            return mNavigationController.canGoBack();
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    public boolean canGoForward() {
+        try {
+            return mNavigationController.canGoForward();
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
     public void reload() {
         try {
             mNavigationController.reload();
diff --git a/weblayer/public/navigation_controller.h b/weblayer/public/navigation_controller.h
index 1b455a9..25b7c111 100644
--- a/weblayer/public/navigation_controller.h
+++ b/weblayer/public/navigation_controller.h
@@ -26,6 +26,10 @@
 
   virtual void GoForward() = 0;
 
+  virtual bool CanGoBack() = 0;
+
+  virtual bool CanGoForward() = 0;
+
   virtual void Reload() = 0;
 
   virtual void Stop() = 0;
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
index 5347e671..dfbbe82 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -4,18 +4,24 @@
 
 package org.chromium.weblayer.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
+
 import android.net.Uri;
 import android.support.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.Navigation;
+import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.NavigationObserver;
 import org.chromium.weblayer.shell.WebLayerShellActivity;
 
@@ -29,8 +35,10 @@
     @Rule
     public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
 
-    // URL used for base tests.
-    private static final String URL = "data:text";
+    // URLs used for base tests.
+    private static final String URL1 = "data:text,foo";
+    private static final String URL2 = "data:text,bar";
+    private static final String URL3 = "data:text,baz";
 
     private static class Observer extends NavigationObserver {
         public static class NavigationCallbackHelper extends CallbackHelper {
@@ -43,7 +51,7 @@
 
             public void assertCalledWith(int currentCallCount, String uri) throws TimeoutException {
                 waitForCallback(currentCallCount);
-                Assert.assertEquals(mUri.toString(), uri);
+                assertEquals(mUri.toString(), uri);
             }
         }
 
@@ -67,36 +75,88 @@
         }
     }
 
+    private final Observer mObserver = new Observer();
+
     @Test
     @SmallTest
     public void testBaseStartup() {
-        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL);
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL1);
 
-        Assert.assertNotNull(activity);
+        assertNotNull(activity);
 
-        mActivityTestRule.waitForNavigation(URL);
+        mActivityTestRule.waitForNavigation(URL1);
     }
 
     @Test
     @SmallTest
     public void testNavigationEvents() throws Exception {
-        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL);
-        mActivityTestRule.waitForNavigation(URL);
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL1);
+        mActivityTestRule.waitForNavigation(URL1);
 
-        Observer observer = new Observer();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            activity.getBrowserController().getNavigationController().addObserver(observer);
+        setNavigationObserver(activity);
+        int curStartedCount = mObserver.onStartedCallback.getCallCount();
+        int curCommittedCount = mObserver.onCommittedCallback.getCallCount();
+        int curCompletedCount = mObserver.onCompletedCallback.getCallCount();
+
+        mActivityTestRule.loadUrl(URL2);
+        mActivityTestRule.waitForNavigation(URL2);
+
+        mObserver.onStartedCallback.assertCalledWith(curStartedCount, URL2);
+        mObserver.onCommittedCallback.assertCalledWith(curCommittedCount, URL2);
+        mObserver.onCompletedCallback.assertCalledWith(curCompletedCount, URL2);
+    }
+
+    @Test
+    @SmallTest
+    public void testGoBackAndForward() throws Exception {
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL1);
+        mActivityTestRule.waitForNavigation(URL1);
+        setNavigationObserver(activity);
+
+        mActivityTestRule.loadUrl(URL2);
+        mActivityTestRule.waitForNavigation(URL2);
+        mActivityTestRule.loadUrl(URL3);
+        mActivityTestRule.waitForNavigation(URL3);
+
+        NavigationController navigationController =
+                    activity.getBrowserController().getNavigationController();
+
+        navigateAndWaitForCompletion(URL2, () -> {
+            assertTrue(navigationController.canGoBack());
+            navigationController.goBack();
         });
-        int curStartedCount = observer.onStartedCallback.getCallCount();
-        int curCommittedCount = observer.onCommittedCallback.getCallCount();
-        int curCompletedCount = observer.onCompletedCallback.getCallCount();
 
-        String url = "data:text,foo";
-        mActivityTestRule.loadUrl(url);
-        mActivityTestRule.waitForNavigation(url);
+        navigateAndWaitForCompletion(URL1, () -> {
+            assertTrue(navigationController.canGoBack());
+            navigationController.goBack();
+        });
 
-        observer.onStartedCallback.assertCalledWith(curStartedCount, url);
-        observer.onCommittedCallback.assertCalledWith(curCommittedCount, url);
-        observer.onCompletedCallback.assertCalledWith(curCompletedCount, url);
+        navigateAndWaitForCompletion(URL2, () -> {
+            assertFalse(navigationController.canGoBack());
+            assertTrue(navigationController.canGoForward());
+            navigationController.goForward();
+        });
+
+        navigateAndWaitForCompletion(URL3, () -> {
+            assertTrue(navigationController.canGoForward());
+            navigationController.goForward();
+        });
+
+        runOnUiThreadBlocking(() -> {
+            assertFalse(navigationController.canGoForward());
+        });
+    }
+
+    private void setNavigationObserver(WebLayerShellActivity activity) {
+        runOnUiThreadBlocking(() ->
+            activity.getBrowserController().getNavigationController().addObserver(mObserver)
+        );
+    }
+
+    private void navigateAndWaitForCompletion(String expectedUrl, Runnable navigateRunnable)
+            throws Exception {
+        int currentCallCount = mObserver.onCompletedCallback.getCallCount();
+        runOnUiThreadBlocking(navigateRunnable);
+        mObserver.onCompletedCallback.assertCalledWith(currentCallCount, expectedUrl);
     }
 }
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 4e46a2b..6bc733a 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -29,6 +29,7 @@
 import org.chromium.weblayer.BrowserFragment;
 import org.chromium.weblayer.BrowserFragmentController;
 import org.chromium.weblayer.BrowserObserver;
+import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.UnsupportedVersionException;
 import org.chromium.weblayer.WebLayer;
@@ -213,4 +214,17 @@
         // ids as before.
         outState.putInt(KEY_MAIN_VIEW_ID, mMainViewId);
     }
+
+    @Override
+    public void onBackPressed() {
+        if (mBrowserFragmentController != null) {
+            NavigationController controller = mBrowserFragmentController.getBrowserController()
+                    .getNavigationController();
+            if (controller.canGoBack()) {
+                controller.goBack();
+                return;
+            }
+        }
+        super.onBackPressed();
+    }
 }