diff --git a/.gn b/.gn
index e3ff7d4b..22e155c1 100644
--- a/.gn
+++ b/.gn
@@ -250,10 +250,8 @@
   "//third_party/blink/renderer/core/workers:*",  # 289 errors
   "//third_party/blink/renderer/core/xmlhttprequest:*",  # 49 errors
   "//third_party/blink/renderer/core:*",  # 823 errors
-  "//third_party/blink/renderer/modules/canvas:*",  # 1 error
   "//third_party/blink/renderer/modules/font_access:*",  # 3 errors
   "//third_party/blink/renderer/modules/peerconnection:*",  # 43 errors
-  "//third_party/blink/renderer/platform:*",  # 10 errors
 
   "//third_party/breakpad:*",  # 34 errors
   "//third_party/ced/*",
diff --git a/DEPS b/DEPS
index 85d11de..e88c938 100644
--- a/DEPS
+++ b/DEPS
@@ -171,7 +171,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:18502e6a01811b0c2dd2a0250e81bd82e47d2c2d',
+  'luci_go': 'git_revision:b022173f8069cf8001d4cf2a87ce7c5f0eae220f',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -195,7 +195,7 @@
   # 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': '081bc32703b7eb62728e6b5478e24e55c8f84463',
+  'skia_revision': '2bc4077c9e4216fba03607d61d4f1bda43a60eb8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -203,11 +203,11 @@
   # 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.
-  'swarming_revision': '4c095d04179dc725a300085ae21fe3b79900d072',
+  'swarming_revision': '44c13d73156581ea09b9389001e58c23a4b8d70a',
   # 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': '75d257686e00d26633d0ae4648b1c9939fffda22',
+  'angle_revision': '2296e7afd74851bd287f46f06864b904365b05ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '02fad13e19503b82c73778c2cf54bc25cb2dd652',
+  'devtools_frontend_revision': 'd1b7c5ca690cf6874aa2f69713048a688ed1f1b0',
   # 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.
@@ -322,7 +322,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.
-  'quiche_revision': '5c52ad843f8a33546cc17f4e7f05e6ab0df92c43',
+  'quiche_revision': '093f8e4f0014522e32896cfa4d1d8e5510b41788',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -545,7 +545,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6543af21be74965abb609e792d744f8f76fe4c63',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'dc8add9b14b393940bd3430c5dffa51f016424e5',
       'condition': 'checkout_ios',
   },
 
@@ -896,7 +896,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a1e15498cd2dd4b2cc8826e65dbee2c2b0b63aa9',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0547fb291708a09df5e9873561aee0eed6889133',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -947,7 +947,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'a9449e612bc251b4271bbe1e3a0d12e9809bf74c',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'f8a5602c55606f8e97f5576c85cbc2a58026a999',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'ac2f01f4bd89762c084003003a15c2be0939877d',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1249,7 +1249,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'bef393a5a340a4d6303b263cba7adee98372ab7f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cfda0b2aaf01f3fc41a9885114baeab0c368477e',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1428,7 +1428,7 @@
   },
 
   'src/third_party/usrsctp/usrsctplib':
-    Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + 'a8c51df76caae94254b1e59999405f739467490e',
+    Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '995c0b84414466d77d52011e5b572cbe213b770a',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '6c656df63da5995a932aafd45b32af1974e497d9',
@@ -1471,7 +1471,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '8452ea86c5cbc8f3626c00ddd38ac43db57f1c4d',
+    Var('webrtc_git') + '/src.git' + '@' + '7703f23b60c493744aba84977aec459cbc33f016',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1543,7 +1543,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@44c9ca6a2c0e352909ae9cf9206b6155380446f3',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a0ee89a93f645c9eb91e107360503fc96cb6c61e',
     'condition': 'checkout_src_internal',
   },
 
@@ -1551,7 +1551,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '_QUWBlAB3zSpGuENS-ygkl0rPTkolBKtlvjdPANqPVEC',
+        'version': 'Efy7gVoSk1mOpec4spFNgvpGxdfwB3frPpVKkwksne8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1562,7 +1562,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'MEjLKGkVlDNdHj0W1rgJY5UBkUoBb16icAog6f-mvaEC',
+        'version': 'tbdCdSmxCSExSm3_J4ZD3I0NKirjX4F7MA1Lr38MjDcC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1972,17 +1972,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/android_deps/libs/androidx_legacy_legacy_support_v13': {
-      'packages': [
-          {
-              'package': 'chromium/third_party/android_deps/libs/androidx_legacy_legacy_support_v13',
-              'version': 'version:1.0.0-cr0',
-          },
-      ],
-      'condition': 'checkout_android',
-      'dep_type': 'cipd',
-  },
-
   'src/third_party/android_deps/libs/androidx_legacy_legacy_support_v4': {
       'packages': [
           {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index fbbf6fd..543843a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1200,7 +1200,7 @@
       (
         'mojo::MakeRequest is deprecated.',
         'Use mojo::AssociatedRemote::'
-        'BindNewEndpointAndPassDedicatedReceiverForTesting() instead.',
+        'BindNewEndpointAndPassDedicatedReceiver() instead.',
       ),
     ),
     (
diff --git a/android_webview/browser/aw_browser_policy_connector.cc b/android_webview/browser/aw_browser_policy_connector.cc
index 881a34e..369724c 100644
--- a/android_webview/browser/aw_browser_policy_connector.cc
+++ b/android_webview/browser/aw_browser_policy_connector.cc
@@ -8,9 +8,9 @@
 
 #include "android_webview/browser/aw_browser_process.h"
 #include "base/bind.h"
-#include "components/policy/core/browser/android/android_combined_policy_provider.h"
 #include "components/policy/core/browser/configuration_policy_handler_list.h"
 #include "components/policy/core/browser/url_blocklist_policy_handler.h"
+#include "components/policy/core/common/android/android_combined_policy_provider.h"
 #include "components/policy/core/common/policy_details.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/policy_constants.h"
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index 72d1fcd..4d9db1d1 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -45,10 +45,10 @@
 import org.chromium.base.task.TaskRunner;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.components.minidump_uploader.CrashFileManager;
+import org.chromium.components.policy.CombinedPolicyProvider;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.ChildProcessCreationParams;
 import org.chromium.content_public.browser.ChildProcessLauncherHelper;
-import org.chromium.policy.CombinedPolicyProvider;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 03a5140..c27c16ec 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -61,6 +61,9 @@
                             + "crash reporting is typically compiled but disabled."),
             Flag.commandLine(GpuSwitches.DISABLE_GPU_RASTERIZATION,
                     "Disables GPU rasterization, i.e. rasterizes on the CPU only."),
+            Flag.commandLine(GpuSwitches.IGNORE_GPU_BLOCKLIST,
+                    "Overrides the built-in software rendering list and enables "
+                            + "GPU acceleration on unsupported device configurations."),
             Flag.baseFeature("OutOfBlinkCors",
                     "Moves CORS logic into the Network Service (rather than inside the blink "
                             + "rendering engine)."),
diff --git a/android_webview/java/src/org/chromium/android_webview/common/crash/CrashUploadUtil.java b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashUploadUtil.java
index 7c45cef..312826c 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/crash/CrashUploadUtil.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashUploadUtil.java
@@ -11,6 +11,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.android_webview.common.services.ServiceNames;
 import org.chromium.base.Log;
@@ -28,12 +29,43 @@
     private static final String TAG = "CrashUploadUtil";
 
     /**
+     * Delegate interface to mock network status check and scheduling upload jobs for testing.
+     */
+    @VisibleForTesting
+    public static interface CrashUploadDelegate {
+        /**
+         * Schedule a MinidumpUploadJobService to attempt uploading all ready crash minidumps.
+         */
+        void scheduleNewJob(@NonNull Context context);
+
+        /**
+         * Check if network is unmetered or not.
+         */
+        boolean isNetworkUnmetered(@NonNull Context context);
+    }
+
+    private static CrashUploadDelegate sDelegate = new CrashUploadDelegate() {
+        @Override
+        public void scheduleNewJob(@NonNull Context context) {
+            JobInfo.Builder builder = new JobInfo.Builder(TaskIds.WEBVIEW_MINIDUMP_UPLOADING_JOB_ID,
+                    new ComponentName(context, ServiceNames.AW_MINIDUMP_UPLOAD_JOB_SERVICE));
+            MinidumpUploadJobService.scheduleUpload(builder);
+        }
+
+        @Override
+        public boolean isNetworkUnmetered(@NonNull Context context) {
+            ConnectivityManager connectivityManager =
+                    (ConnectivityManager) context.getApplicationContext().getSystemService(
+                            Context.CONNECTIVITY_SERVICE);
+            return NetworkPermissionUtil.isNetworkUnmetered(connectivityManager);
+        }
+    };
+
+    /**
      * Schedule a MinidumpUploadJobService to attempt uploading all ready crash minidumps.
      */
     public static void scheduleNewJob(@NonNull Context context) {
-        JobInfo.Builder builder = new JobInfo.Builder(TaskIds.WEBVIEW_MINIDUMP_UPLOADING_JOB_ID,
-                new ComponentName(context, ServiceNames.AW_MINIDUMP_UPLOAD_JOB_SERVICE));
-        MinidumpUploadJobService.scheduleUpload(builder);
+        sDelegate.scheduleNewJob(context);
     }
 
     /**
@@ -66,10 +98,12 @@
     }
 
     public static boolean isNetworkUnmetered(@NonNull Context context) {
-        ConnectivityManager connectivityManager =
-                (ConnectivityManager) context.getApplicationContext().getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-        return NetworkPermissionUtil.isNetworkUnmetered(connectivityManager);
+        return sDelegate.isNetworkUnmetered(context);
+    }
+
+    @VisibleForTesting
+    public static void setCrashUploadDelegateForTesting(CrashUploadDelegate delegate) {
+        sDelegate = delegate;
     }
 
     // Do not instantiate this class.
diff --git a/android_webview/java/src/org/chromium/android_webview/policy/AwPolicyProvider.java b/android_webview/java/src/org/chromium/android_webview/policy/AwPolicyProvider.java
index 12250fb..68020092 100644
--- a/android_webview/java/src/org/chromium/android_webview/policy/AwPolicyProvider.java
+++ b/android_webview/java/src/org/chromium/android_webview/policy/AwPolicyProvider.java
@@ -9,7 +9,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.policy.AppRestrictionsProvider;
+import org.chromium.components.policy.AppRestrictionsProvider;
 
 /**
  * Does the plumbing between the policies collected via Android's App Restriction system and the
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
index 7eb07265..c4426e3 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -449,13 +449,8 @@
     }
 
     @Test
-    /**
-     * @SmallTest
-     * @Feature({"AndroidWebView"})
-     * BUG=813837
-     */
-    // Originally flaked only in multi-process mode (http://crbug.com/616505)
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testTouchScrollCanBeAlteredByUi() throws Throwable {
         final TestAwContentsClient contentsClient = new TestAwContentsClient();
         final ScrollTestContainerView testContainerView =
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
index 9a286448..08e1ece 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
@@ -23,13 +23,13 @@
 import org.chromium.android_webview.WebviewErrorCode;
 import org.chromium.android_webview.policy.AwPolicyProvider;
 import org.chromium.base.test.util.Feature;
+import org.chromium.components.policy.AbstractAppRestrictionsProvider;
+import org.chromium.components.policy.CombinedPolicyProvider;
+import org.chromium.components.policy.test.PolicyData;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.util.TestWebServer;
-import org.chromium.policy.AbstractAppRestrictionsProvider;
-import org.chromium.policy.CombinedPolicyProvider;
-import org.chromium.policy.test.PolicyData;
-import org.chromium.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
index 0b9e4cc2..deee8e9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
@@ -7,19 +7,30 @@
 import static androidx.test.espresso.Espresso.onData;
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.intent.Intents.assertNoUnverifiedIntents;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.Intents.intending;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.anything;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 
 import static org.chromium.android_webview.test.common.crash.CrashInfoTest.createCrashInfo;
+import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.getClipBoardTextOnUiThread;
+import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.setClipBoardTextOnUiThread;
 import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.withCount;
 
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -29,12 +40,14 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
 import android.view.View;
 import android.widget.ImageView;
 
 import androidx.annotation.IdRes;
 import androidx.test.espresso.DataInteraction;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.espresso.intent.matcher.IntentMatchers;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
 import androidx.test.filters.LargeTest;
 
 import org.hamcrest.Description;
@@ -48,10 +61,13 @@
 
 import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.android_webview.common.crash.CrashInfo.UploadState;
+import org.chromium.android_webview.common.crash.CrashUploadUtil;
+import org.chromium.android_webview.common.crash.CrashUploadUtil.CrashUploadDelegate;
 import org.chromium.android_webview.common.crash.SystemWideCrashDirectories;
 import org.chromium.android_webview.devui.CrashesListFragment;
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
+import org.chromium.android_webview.devui.util.CrashBugUrlFactory;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.base.FileUtils;
 import org.chromium.base.test.util.CallbackHelper;
@@ -77,13 +93,15 @@
     private static final String CRASH_UPLOAD_BUTTON_TEXT = "Upload this crash report";
 
     @Rule
-    public ActivityTestRule mRule =
-            new ActivityTestRule<MainActivity>(MainActivity.class, false, false);
+    public IntentsTestRule mRule =
+            new IntentsTestRule<MainActivity>(MainActivity.class, false, false);
 
     @After
     public void tearDown() {
         FileUtils.recursivelyDeleteFile(SystemWideCrashDirectories.getWebViewCrashDir(), null);
         FileUtils.recursivelyDeleteFile(SystemWideCrashDirectories.getWebViewCrashLogDir(), null);
+        // Tests are responsible for verifying every Intent they trigger.
+        assertNoUnverifiedIntents();
     }
 
     private void launchCrashesFragment() {
@@ -91,6 +109,11 @@
         intent.putExtra(MainActivity.FRAGMENT_ID_INTENT_EXTRA, MainActivity.FRAGMENT_ID_CRASHES);
         mRule.launchActivity(intent);
         onView(withId(R.id.fragment_crashes_list)).check(matches(isDisplayed()));
+
+        // Stub all external intents, to avoid launching other apps (ex. system browser), has to be
+        // done after launching the activity.
+        intending(not(IntentMatchers.isInternal()))
+                .respondWith(new ActivityResult(Activity.RESULT_OK, null));
     }
 
     private static File createMinidumpFile(CrashInfo crashInfo) throws IOException {
@@ -309,6 +332,55 @@
 
     @Test
     @Feature({"AndroidWebView"})
+    public void testOpenBugReportCrash() throws Throwable {
+        final long systemTime = System.currentTimeMillis();
+        CrashInfo crashInfo = createCrashInfo("123456", systemTime, "0abcde123456",
+                systemTime + 1000, FAKE_APP_PACKAGE_NAME, UploadState.UPLOADED);
+
+        assertThat("temp json log file should exist", writeJsonLogFile(crashInfo).exists());
+        assertThat("upload log file should exist", appendUploadedEntryToLog(crashInfo).exists());
+
+        CallbackHelper helper = getCrashListLoadedListener();
+        int crashListLoadInitCount = helper.getCallCount();
+        launchCrashesFragment();
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
+        // Click the header to expand the list item.
+        onData(anything()).atPosition(0).perform(click());
+        // The body is considered item#2 in the list view after expansion.
+        onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
+        onData(anything())
+                .atPosition(1)
+                .onChildView(withId(R.id.crash_report_button))
+                .perform(click());
+        onView(withText(CrashesListFragment.CRASH_BUG_DIALOG_MESSAGE))
+                .check(matches(isDisplayed()));
+        // button2 is the AlertDialog negative button id.
+        onView(withId(android.R.id.button2)).check(matches(withText("Dismiss"))).perform(click());
+        onView(withText(CrashesListFragment.CRASH_BUG_DIALOG_MESSAGE)).check(doesNotExist());
+        // Verify that no intents are sent out.
+        Intents.times(0);
+
+        onData(anything())
+                .atPosition(1)
+                .onChildView(withId(R.id.crash_report_button))
+                .perform(click());
+        // button1 is the AlertDialog positive button id.
+        onView(withId(android.R.id.button1))
+                .check(matches(withText("Provide more info")))
+                .perform(click());
+        onView(withText(CrashesListFragment.CRASH_BUG_DIALOG_MESSAGE)).check(doesNotExist());
+        Intent expectedIntent = new CrashBugUrlFactory(crashInfo).getReportIntent();
+        // TODO(hazems): use IntentMatchers.filterEquals() after pulling the new version of
+        // espresso-intents
+        intended(allOf(IntentMatchers.hasAction(expectedIntent.getAction()),
+                IntentMatchers.hasData(expectedIntent.getData())));
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
     public void testShowingSingleCrashReport_pending() throws Throwable {
         final long systemTime = System.currentTimeMillis();
         CrashInfo crashInfo = createCrashInfo(
@@ -425,6 +497,124 @@
 
     @Test
     @Feature({"AndroidWebView"})
+    public void testForceUploadSkippedCrashReport_noWifi() throws Throwable {
+        final long systemTime = System.currentTimeMillis();
+        CrashInfo crashInfo = createCrashInfo(
+                "123456", systemTime, null, -1, FAKE_APP_PACKAGE_NAME, UploadState.SKIPPED);
+
+        File minidumpFile = createMinidumpFile(crashInfo);
+        assertThat("temp minidump file should exist", minidumpFile.exists());
+        assertThat("temp json log file should exist", writeJsonLogFile(crashInfo).exists());
+
+        CrashUploadUtil.setCrashUploadDelegateForTesting(new CrashUploadDelegate() {
+            @Override
+            public void scheduleNewJob(Context context) {}
+
+            @Override
+            public boolean isNetworkUnmetered(Context context) {
+                return false;
+            }
+        });
+
+        CallbackHelper helper = getCrashListLoadedListener();
+        int crashListLoadInitCount = helper.getCallCount();
+        launchCrashesFragment();
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
+
+        // click on the crash item header to expand
+        onData(anything()).atPosition(0).perform(click());
+        // The body is considered item#2 in the list view after expansion
+        onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
+        DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
+        checkCrashItemUploadStatus(bodyDataInteraction, crashInfo);
+
+        // Firstly test clicking the upload button, and dismissing the dialog
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button)).perform(click());
+        onView(withText(CrashesListFragment.NO_WIFI_DIALOG_MESSAGE)).check(matches(isDisplayed()));
+        // button2 is the AlertDialog negative button id.
+        onView(withId(android.R.id.button2)).check(matches(withText("Cancel"))).perform(click());
+        // Check no changes in the view after dismissing the dialog
+        checkCrashItemUploadStatus(bodyDataInteraction, crashInfo);
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button))
+                .check(matches(isDisplayed()));
+
+        // Secondly test clicking the upload button, and proceeding with upload.
+        crashListLoadInitCount = helper.getCallCount();
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button)).perform(click());
+        onView(withText(CrashesListFragment.NO_WIFI_DIALOG_MESSAGE)).check(matches(isDisplayed()));
+        // button1 is the AlertDialog positive button id.
+        onView(withId(android.R.id.button1)).check(matches(withText("Upload"))).perform(click());
+        helper.waitForCallback(crashListLoadInitCount, 1);
+        // upload button is now hidden
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button))
+                .check(matches(not(isDisplayed())));
+        crashInfo.uploadState = UploadState.PENDING_USER_REQUESTED;
+        checkCrashItemUploadStatus(bodyDataInteraction, crashInfo);
+
+        // Check that minidump file suffix is changed to ".forced"
+        File renamedMinidumpFile =
+                new File(minidumpFile.getAbsolutePath().replace("skipped", "forced"));
+        assertThat("skipped minidump file shouldn't exist", not(minidumpFile.exists()));
+        assertThat("renamed forced minidump file should exist", renamedMinidumpFile.exists());
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    public void testForceUploadSkippedCrashReport_withWifi() throws Throwable {
+        final long systemTime = System.currentTimeMillis();
+        CrashInfo crashInfo = createCrashInfo(
+                "123456", systemTime, null, -1, FAKE_APP_PACKAGE_NAME, UploadState.SKIPPED);
+
+        File minidumpFile = createMinidumpFile(crashInfo);
+        assertThat("temp minidump file should exist", minidumpFile.exists());
+        assertThat("temp json log file should exist", writeJsonLogFile(crashInfo).exists());
+
+        CrashUploadUtil.setCrashUploadDelegateForTesting(new CrashUploadDelegate() {
+            @Override
+            public void scheduleNewJob(Context context) {}
+
+            @Override
+            public boolean isNetworkUnmetered(Context context) {
+                return true;
+            }
+        });
+
+        CallbackHelper helper = getCrashListLoadedListener();
+        int crashListLoadInitCount = helper.getCallCount();
+        launchCrashesFragment();
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
+
+        // click on the crash item header to expand
+        onData(anything()).atPosition(0).perform(click());
+        // The body is considered item#2 in the list view after expansion
+        onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
+        DataInteraction bodyDataInteraction = onData(anything()).atPosition(1);
+        checkCrashItemUploadStatus(bodyDataInteraction, crashInfo);
+
+        crashListLoadInitCount = helper.getCallCount();
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button)).perform(click());
+        helper.waitForCallback(crashListLoadInitCount, 1);
+        // upload button is now hidden
+        bodyDataInteraction.onChildView(withId(R.id.crash_upload_button))
+                .check(matches(not(isDisplayed())));
+        crashInfo.uploadState = UploadState.PENDING_USER_REQUESTED;
+        checkCrashItemUploadStatus(bodyDataInteraction, crashInfo);
+
+        // Check that minidump file suffix is changed to ".forced"
+        File renamedMinidumpFile =
+                new File(minidumpFile.getAbsolutePath().replace("skipped", "forced"));
+        assertThat("skipped minidump file shouldn't exist", not(minidumpFile.exists()));
+        assertThat("renamed forced minidump file should exist", renamedMinidumpFile.exists());
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
     // Test when a crash has a known package name that can be found using PackageManager
     public void testInstalledPackageInfo() throws Throwable {
         Context context = InstrumentationRegistry.getTargetContext();
@@ -593,4 +783,80 @@
 
         onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
     }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    public void testRefreshMenuOption() throws Throwable {
+        CallbackHelper helper = getCrashListLoadedListener();
+        int crashListLoadInitCount = helper.getCallCount();
+        launchCrashesFragment();
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(0)));
+
+        final long systemTime = System.currentTimeMillis();
+        CrashInfo crashInfo = createCrashInfo(
+                "123456", systemTime, null, -1, FAKE_APP_PACKAGE_NAME, UploadState.PENDING);
+
+        assertThat("temp minidump file should exist", createMinidumpFile(crashInfo).exists());
+        assertThat("temp json log file should exist", writeJsonLogFile(crashInfo).exists());
+
+        crashListLoadInitCount = helper.getCallCount();
+        onView(withText("Refresh")).check(matches(isDisplayed()));
+        onView(withText("Refresh")).perform(click());
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(1)));
+        checkUnknownPackageCrashItemHeader(onData(anything()).atPosition(0), crashInfo);
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    public void testLongPressCopy() throws Throwable {
+        Context context = InstrumentationRegistry.getTargetContext();
+        final long systemTime = System.currentTimeMillis();
+        CrashInfo uploadedCrashInfo = createCrashInfo("123456", systemTime - 1000, "0abcde123456",
+                systemTime, FAKE_APP_PACKAGE_NAME, UploadState.UPLOADED);
+        CrashInfo pendingCrashInfo = createCrashInfo(
+                "78910", systemTime - 2000, null, -1, FAKE_APP_PACKAGE_NAME, UploadState.PENDING);
+
+        assertThat("temp json log file for uploaded crash should exist",
+                writeJsonLogFile(uploadedCrashInfo).exists());
+        assertThat("upload log file should exist",
+                appendUploadedEntryToLog(uploadedCrashInfo).exists());
+
+        assertThat("temp minidump file for pending crash should exist",
+                createMinidumpFile(pendingCrashInfo).exists());
+        assertThat("temp json log file for pending crash should exist",
+                writeJsonLogFile(pendingCrashInfo).exists());
+
+        CallbackHelper helper = getCrashListLoadedListener();
+        int crashListLoadInitCount = helper.getCallCount();
+        launchCrashesFragment();
+        helper.waitForCallback(crashListLoadInitCount, 1);
+
+        onView(withId(R.id.crashes_list)).check(matches(withCount(2)));
+
+        // click on the first crash item header to expand
+        onData(anything()).atPosition(0).perform(click());
+        // long click on the crash item body to copy
+        onData(anything()).atPosition(1).perform(longClick());
+        String expectedUploadInfo = new Date(uploadedCrashInfo.uploadTime).toString()
+                + "\nID: " + uploadedCrashInfo.uploadId;
+        assertThat(getClipBoardTextOnUiThread(context), is(expectedUploadInfo));
+
+        // click on the first crash item header to collapse
+        onData(anything()).atPosition(0).perform(click());
+        // click on the second crash item header to expand
+        onData(anything()).atPosition(1).perform(click());
+        // Clear clipboard content
+        setClipBoardTextOnUiThread(context, "", "");
+        // Crash body is now the second item in the list view, long click on the crash item body to
+        // copy.
+        onData(anything()).atPosition(2).perform(longClick());
+        // This a pending upload, nothing should be copied
+        assertThat(getClipBoardTextOnUiThread(context), is(""));
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTestUtils.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTestUtils.java
index 613747848..411aaab 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTestUtils.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTestUtils.java
@@ -6,6 +6,7 @@
 
 import static org.hamcrest.Matchers.is;
 
+import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.view.View;
@@ -63,6 +64,17 @@
         });
     }
 
+    public static void setClipBoardTextOnUiThread(Context context, String key, String value)
+            throws ExecutionException {
+        // ClipManager service has to be called on the UI main thread.
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ClipboardManager clipboardManager =
+                    (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+            ClipData clip = ClipData.newPlainText(key, value);
+            clipboardManager.setPrimaryClip(clip);
+        });
+    }
+
     // Don't instantiate this class.
     private DeveloperUiTestUtils() {}
 }
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/CrashesListFragment.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/CrashesListFragment.java
index da2409f..06a2c5d 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/CrashesListFragment.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/CrashesListFragment.java
@@ -62,6 +62,13 @@
 public class CrashesListFragment extends DevUiBaseFragment {
     private static final String TAG = "WebViewDevTools";
 
+    public static final String CRASH_BUG_DIALOG_MESSAGE =
+            "This crash has already been reported to our crash system. "
+            + "Do you want to share more information, such as steps to reproduce the crash?";
+    public static final String NO_WIFI_DIALOG_MESSAGE =
+            "You are connected to a metered network or cellular data."
+            + " Do you want to proceed?";
+
     // Max number of crashes to show in the crashes list.
     public static final int MAX_CRASHES_NUMBER = 20;
 
@@ -273,9 +280,7 @@
                     if (!CrashUploadUtil.isNetworkUnmetered(mContext)) {
                         new AlertDialog.Builder(mContext)
                                 .setTitle("Network Warning")
-                                .setMessage(
-                                        "You are connected to a metered network or cellular data."
-                                        + " Do you want to proceed?")
+                                .setMessage(NO_WIFI_DIALOG_MESSAGE)
                                 .setPositiveButton("Upload",
                                         (dialog, id) -> {
                                             logCrashInteraction(
@@ -503,9 +508,7 @@
 
     private AlertDialog buildCrashBugDialog(CrashInfo crashInfo) {
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
-        dialogBuilder.setMessage(
-                "This crash has already been reported to our crash system. Do you want to share "
-                + "more information, such as steps to reproduce the crash?");
+        dialogBuilder.setMessage(CRASH_BUG_DIALOG_MESSAGE);
         dialogBuilder.setPositiveButton("Provide more info", (dialog, id) -> {
             logCrashInteraction(CrashInteraction.FILE_BUG_REPORT_DIALOG_PROCEED);
             startActivity(new CrashBugUrlFactory(crashInfo).getReportIntent());
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index e9f4a27..dad91d6a 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -53,6 +53,9 @@
     } else {
       if (_is_bundle_module && weblayer_in_split) {
         deps += [
+          # TODO(crbug.com/1105096): WebLayer resources are added to the base
+          # module for now because of bugs with shared resources in splits.
+          ":${target_name}__all_weblayer_resources",
           "//android_webview:android_webview_no_weblayer_java",
           "//weblayer/browser/java:base_module_java",
         ]
diff --git a/android_webview/system_webview_bundle.gni b/android_webview/system_webview_bundle.gni
index 284ad06..3a852ae 100644
--- a/android_webview/system_webview_bundle.gni
+++ b/android_webview/system_webview_bundle.gni
@@ -27,6 +27,18 @@
       name = "weblayer"
       module_target = ":${_weblayer_module_target}"
     }
+
+    # TODO(crbug.com/1105096): This target is needed to add all WebLayer
+    # resources to the base module because of bugs with shared resources in
+    # splits.
+    android_resources("${_base_target_name}__all_weblayer_resources") {
+      recursive_resource_deps = true
+      deps = [ "//weblayer/browser/java" ]
+      if (defined(invoker.weblayer_deps)) {
+        deps += invoker.weblayer_deps
+      }
+    }
+
     android_app_bundle_module(_weblayer_module_target) {
       forward_variables_from(invoker,
                              [
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java
index 1c8d1a0d7..4840a38 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java
@@ -13,7 +13,7 @@
 import org.chromium.android_webview.test.OnlyRunIn.ProcessMode;
 import org.chromium.base.CommandLine;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.policy.test.annotations.Policies;
+import org.chromium.components.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ee1ec24..eb0f710e 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -925,8 +925,12 @@
     "system/holding_space/holding_space_item_chip_view.h",
     "system/holding_space/holding_space_item_chips_container.cc",
     "system/holding_space/holding_space_item_chips_container.h",
-    "system/holding_space/holding_space_screenshot_view.cc",
-    "system/holding_space/holding_space_screenshot_view.h",
+    "system/holding_space/holding_space_item_context_menu.cc",
+    "system/holding_space/holding_space_item_context_menu.h",
+    "system/holding_space/holding_space_item_screenshot_view.cc",
+    "system/holding_space/holding_space_item_screenshot_view.h",
+    "system/holding_space/holding_space_item_view.cc",
+    "system/holding_space/holding_space_item_view.h",
     "system/holding_space/holding_space_tray.cc",
     "system/holding_space/holding_space_tray.h",
     "system/holding_space/pinned_files_container.cc",
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index 8f1d2df..12d0453 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -44,6 +44,7 @@
 #include "ui/base/ui_base_types.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
+#include "ui/wm/core/cursor_manager.h"
 #include "ui/wm/core/visibility_controller.h"
 #include "ui/wm/core/window_animations.h"
 
@@ -549,6 +550,9 @@
 
   widget->Show();
 
+  // Hide cursor.
+  Shell::Get()->cursor_manager()->HideCursor();
+
   // Requests keyboard focus for |container_view_| to receive keyboard events.
   container_view_->RequestFocus();
 }
diff --git a/ash/ambient/ambient_controller_unittest.cc b/ash/ambient/ambient_controller_unittest.cc
index f10b0ab..bedd5fc2 100644
--- a/ash/ambient/ambient_controller_unittest.cc
+++ b/ash/ambient/ambient_controller_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/ambient/test/ambient_ash_test_base.h"
 #include "ash/ambient/ui/ambient_container_view.h"
 #include "ash/public/cpp/ambient/ambient_ui_model.h"
+#include "ash/shell.h"
 #include "ash/system/power/power_status.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
@@ -474,4 +475,25 @@
   EXPECT_TRUE(ambient_controller()->IsShown());
 }
 
+TEST_F(AmbientControllerTest, HideCursor) {
+  auto* cursor_manager = Shell::Get()->cursor_manager();
+  LockScreen();
+
+  cursor_manager->ShowCursor();
+  EXPECT_TRUE(cursor_manager->IsCursorVisible());
+
+  FastForwardToInactivity();
+  FastForwardToNextImage();
+
+  EXPECT_TRUE(container_view());
+  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
+            AmbientUiVisibility::kShown);
+  EXPECT_TRUE(ambient_controller()->IsShown());
+  EXPECT_FALSE(cursor_manager->IsCursorVisible());
+
+  // Clean up.
+  UnlockScreen();
+  EXPECT_FALSE(ambient_controller()->IsShown());
+}
+
 }  // namespace ash
diff --git a/ash/ambient/ui/ambient_background_image_view.cc b/ash/ambient/ui/ambient_background_image_view.cc
index 4f3aadf..b4d28189 100644
--- a/ash/ambient/ui/ambient_background_image_view.cc
+++ b/ash/ambient/ui/ambient_background_image_view.cc
@@ -6,8 +6,10 @@
 
 #include <memory>
 
+#include "ash/ambient/ui/glanceable_info_view.h"
 #include "ash/ambient/util/ambient_util.h"
 #include "ash/assistant/ui/assistant_view_ids.h"
+#include "base/rand_util.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/controls/image_view.h"
@@ -21,14 +23,22 @@
 namespace {
 
 // Appearance.
-constexpr int kHorizontalMarginDip = 16;
-constexpr int kVerticalMarginDip = 43;
+constexpr int kMarginDip = 16;
+constexpr int kSpacingDip = 8;
 
 // Typography.
 constexpr SkColor kTextColor = SK_ColorWHITE;
 constexpr int kDefaultFontSizeDip = 64;
 constexpr int kDetailsFontSizeDip = 13;
 
+// The dicretion to translate glanceable info views in the x/y coordinates.  `1`
+// means positive translate, `-1` negative.
+int translate_x_direction = 1;
+int translate_y_direction = -1;
+// The current x/y translation of glanceable info views in Dip.
+int current_x_translation = 0;
+int current_y_translation = 0;
+
 }  // namespace
 
 AmbientBackgroundImageView::AmbientBackgroundImageView(
@@ -57,6 +67,8 @@
 
 void AmbientBackgroundImageView::UpdateImage(const gfx::ImageSkia& img) {
   image_view_->SetImage(img);
+
+  UpdateGlanceableInfoPosition();
 }
 
 void AmbientBackgroundImageView::UpdateImageDetails(
@@ -78,16 +90,29 @@
   // Inits the image view. This view should have the same size of the screen.
   image_view_ = AddChildView(std::make_unique<views::ImageView>());
 
+  gfx::Insets shadow_insets =
+      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues());
+
   // Inits the attribution view. It also has a full-screen size and is
   // responsible for layout the details label at its bottom left corner.
   views::View* attribution_view = AddChildView(std::make_unique<views::View>());
   views::BoxLayout* attribution_layout =
       attribution_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal));
+          views::BoxLayout::Orientation::kVertical));
   attribution_layout->set_main_axis_alignment(
-      views::BoxLayout::MainAxisAlignment::kStart);
+      views::BoxLayout::MainAxisAlignment::kEnd);
+  attribution_layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
   attribution_layout->set_inside_border_insets(
-      gfx::Insets(0, kHorizontalMarginDip, kVerticalMarginDip, 0));
+      gfx::Insets(0, kMarginDip + shadow_insets.left(),
+                  kMarginDip + shadow_insets.bottom(), 0));
+
+  attribution_layout->set_between_child_spacing(
+      kSpacingDip + shadow_insets.top() + shadow_insets.bottom());
+
+  glanceable_info_view_ = attribution_view->AddChildView(
+      std::make_unique<GlanceableInfoView>(delegate_));
+  glanceable_info_view_->SetPaintToLayer();
 
   // Inits the details label.
   details_label_ =
@@ -98,11 +123,45 @@
       ambient::util::GetDefaultFontlist().DeriveWithSizeDelta(
           kDetailsFontSizeDip - kDefaultFontSizeDip));
   details_label_->SetShadows(ambient::util::GetTextShadowValues());
-  details_label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
-  details_label_->SetVerticalAlignment(gfx::VerticalAlignment::ALIGN_BOTTOM);
+  details_label_->SetPaintToLayer();
+  details_label_->layer()->SetFillsBoundsOpaquely(false);
 }
 
-BEGIN_METADATA(AmbientBackgroundImageView, views::ImageView)
+void AmbientBackgroundImageView::UpdateGlanceableInfoPosition() {
+  constexpr int kStepDP = 5;
+  constexpr int kMaxTranslationDip = 20;
+
+  // Move the translation point randomly one step on each x/y direction.
+  int x_increment = kStepDP * base::RandInt(0, 1);
+  int y_increment = x_increment == 0 ? kStepDP : kStepDP * base::RandInt(0, 1);
+  current_x_translation += translate_x_direction * x_increment;
+  current_y_translation += translate_y_direction * y_increment;
+
+  // If the translation point is out of bounds, reset it within bounds and
+  // reverse the direction.
+  if (current_x_translation < 0) {
+    translate_x_direction = 1;
+    current_x_translation = 0;
+  } else if (current_x_translation > kMaxTranslationDip) {
+    translate_x_direction = -1;
+    current_x_translation = kMaxTranslationDip;
+  }
+
+  if (current_y_translation > 0) {
+    translate_y_direction = -1;
+    current_y_translation = 0;
+  } else if (current_y_translation < -kMaxTranslationDip) {
+    translate_y_direction = 1;
+    current_y_translation = -kMaxTranslationDip;
+  }
+
+  gfx::Transform transform;
+  transform.Translate(current_x_translation, current_y_translation);
+  glanceable_info_view_->layer()->SetTransform(transform);
+  details_label_->layer()->SetTransform(transform);
+}
+
+BEGIN_METADATA(AmbientBackgroundImageView, views::View)
 END_METADATA
 
 }  // namespace ash
diff --git a/ash/ambient/ui/ambient_background_image_view.h b/ash/ambient/ui/ambient_background_image_view.h
index 7607694..fbbb046e 100644
--- a/ash/ambient/ui/ambient_background_image_view.h
+++ b/ash/ambient/ui/ambient_background_image_view.h
@@ -19,6 +19,8 @@
 
 namespace ash {
 
+class GlanceableInfoView;
+
 // AmbientBackgroundImageView--------------------------------------------------
 // A custom ImageView to display photo image and details information on ambient.
 // It also handles specific mouse/gesture events to dismiss ambient when user
@@ -49,12 +51,16 @@
  private:
   void InitLayout();
 
+  void UpdateGlanceableInfoPosition();
+
   // Owned by |AmbientController| and should always outlive |this|.
   AmbientViewDelegate* delegate_ = nullptr;
 
   // View to display the current image on ambient. Owned by the view hierarchy.
   views::ImageView* image_view_ = nullptr;
 
+  GlanceableInfoView* glanceable_info_view_ = nullptr;
+
   // Label to show details text, i.e. attribution, to be displayed for the
   // current image. Owned by the view hierarchy.
   views::Label* details_label_ = nullptr;
diff --git a/ash/ambient/ui/ambient_container_view.cc b/ash/ambient/ui/ambient_container_view.cc
index 24803e8..4f7d01d 100644
--- a/ash/ambient/ui/ambient_container_view.cc
+++ b/ash/ambient/ui/ambient_container_view.cc
@@ -9,7 +9,6 @@
 
 #include "ash/ambient/ui/ambient_assistant_container_view.h"
 #include "ash/ambient/ui/ambient_view_delegate.h"
-#include "ash/ambient/ui/glanceable_info_view.h"
 #include "ash/ambient/ui/media_string_view.h"
 #include "ash/ambient/ui/photo_view.h"
 #include "ash/ambient/util/ambient_util.h"
@@ -39,7 +38,6 @@
 
 // Appearance.
 constexpr int kHorizontalMarginDip = 16;
-constexpr int kVerticalMarginDip = 64;
 constexpr int kAssistantPreferredHeightDip = 128;
 constexpr int kMediaStringTopMarginDip = 25;
 
@@ -60,7 +58,8 @@
     DCHECK(container_);
     event_monitor_ = views::EventMonitor::CreateWindowMonitor(
         this, container_->GetWidget()->GetNativeWindow()->GetRootWindow(),
-        {ui::ET_KEY_PRESSED, ui::ET_MOUSE_ENTERED, ui::ET_MOUSE_MOVED});
+        {ui::ET_KEY_PRESSED, ui::ET_MOUSE_ENTERED, ui::ET_MOUSE_MOVED,
+         ui::ET_TOUCH_PRESSED, ui::ET_TOUCH_MOVED});
   }
 
   ~HostWidgetEventObserver() override = default;
@@ -75,6 +74,10 @@
         DCHECK(event.IsKeyEvent());
         container_->HandleEvent();
         break;
+      case ui::ET_TOUCH_PRESSED:
+      case ui::ET_TOUCH_MOVED:
+        container_->HandleEvent();
+        break;
       case ui::ET_MOUSE_ENTERED:
         DCHECK(event.IsMouseEvent());
         // Updates the mouse enter location.
@@ -133,7 +136,6 @@
 void AmbientContainerView::Layout() {
   // Layout child views first to have proper bounds set for children.
   LayoutPhotoView();
-  LayoutGlanceableInfoView();
   LayoutMediaStringView();
   // The assistant view may not exist if |kAmbientAssistant| feature is
   // disabled.
@@ -155,9 +157,6 @@
 
   photo_view_ = AddChildView(std::make_unique<PhotoView>(delegate_));
 
-  glanceable_info_view_ =
-      AddChildView(std::make_unique<GlanceableInfoView>(delegate_));
-
   media_string_view_ = AddChildView(std::make_unique<MediaStringView>());
   media_string_view_->SetVisible(false);
 
@@ -173,19 +172,6 @@
   photo_view_->SetBoundsRect(GetLocalBounds());
 }
 
-void AmbientContainerView::LayoutGlanceableInfoView() {
-  const gfx::Size container_size = GetLocalBounds().size();
-  const gfx::Size preferred_size = glanceable_info_view_->GetPreferredSize();
-
-  // The clock and weather view is positioned on the left-bottom corner of the
-  // container.
-  int x = kHorizontalMarginDip;
-  int y =
-      container_size.height() - kVerticalMarginDip - preferred_size.height();
-  glanceable_info_view_->SetBoundsRect(
-      gfx::Rect(x, y, preferred_size.width(), preferred_size.height()));
-}
-
 void AmbientContainerView::LayoutAssistantView() {
   int preferred_width = GetPreferredSize().width();
   int preferred_height = kAssistantPreferredHeightDip;
diff --git a/ash/ambient/ui/ambient_container_view.h b/ash/ambient/ui/ambient_container_view.h
index 6e5dbce..2cd56ea6 100644
--- a/ash/ambient/ui/ambient_container_view.h
+++ b/ash/ambient/ui/ambient_container_view.h
@@ -15,7 +15,6 @@
 
 class AmbientAssistantContainerView;
 class AmbientViewDelegate;
-class GlanceableInfoView;
 class PhotoView;
 class MediaStringView;
 
@@ -42,7 +41,6 @@
   // TODO(meilinw): Use LayoutManagers to lay out children instead of overriding
   // Layout(). See b/163170162.
   void LayoutPhotoView();
-  void LayoutGlanceableInfoView();
   void LayoutAssistantView();
   void LayoutMediaStringView();
 
@@ -54,7 +52,6 @@
   // Owned by view hierarchy.
   PhotoView* photo_view_ = nullptr;
   AmbientAssistantContainerView* ambient_assistant_container_view_ = nullptr;
-  GlanceableInfoView* glanceable_info_view_ = nullptr;
   MediaStringView* media_string_view_ = nullptr;
 
   // Observes events from its host widget.
diff --git a/ash/ambient/ui/glanceable_info_view.cc b/ash/ambient/ui/glanceable_info_view.cc
index df51bbf6..948db49 100644
--- a/ash/ambient/ui/glanceable_info_view.cc
+++ b/ash/ambient/ui/glanceable_info_view.cc
@@ -16,6 +16,7 @@
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/time/time_view.h"
+#include "ash/system/tray/tray_constants.h"
 #include "base/i18n/number_formatting.h"
 #include "base/strings/utf_string_conversions.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -57,15 +58,13 @@
       temperature_font_size_delta);
 }
 
-// Returns the border insets for |weather_info_| to be aligned to the time text
-// baseline.
-gfx::Insets GetWeatherInfoInsets() {
-  int time_font_descent =
-      GetTimeFontList().GetHeight() - GetTimeFontList().GetBaseline();
-  int temperature_font_descent = GetWeatherTemperatureFontList().GetHeight() -
-                                 GetWeatherTemperatureFontList().GetBaseline();
-  return gfx::Insets(
-      0, 0, /*bottom=*/time_font_descent - temperature_font_descent, 0);
+int GetTimeFontDescent() {
+  return GetTimeFontList().GetHeight() - GetTimeFontList().GetBaseline();
+}
+
+int GetTemperatureFontDescent() {
+  return GetWeatherTemperatureFontList().GetHeight() -
+         GetWeatherTemperatureFontList().GetBaseline();
 }
 
 }  // namespace
@@ -128,9 +127,11 @@
   views::BoxLayout* layout =
       SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal));
-  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
+  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
   layout->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kEnd);
-  layout->set_between_child_spacing(kSpacingBetweenTimeAndWeatherDip);
+
+  gfx::ShadowValues text_shadow_values = ambient::util::GetTextShadowValues();
+  gfx::Insets shadow_insets = gfx::ShadowValue::GetMargin(text_shadow_values);
 
   // Inits the time view.
   time_view_ = AddChildView(std::make_unique<tray::TimeView>(
@@ -139,35 +140,31 @@
   time_view_->SetTextFont(GetTimeFontList());
   time_view_->SetTextColor(kTextColor,
                            /*auto_color_readability_enabled=*/false);
-  time_view_->SetTextShadowValues(ambient::util::GetTextShadowValues());
-
-  // Inits and layouts the weather info.
-  weather_info_ = AddChildView(std::make_unique<views::View>());
-  views::BoxLayout* weather_info_layout =
-      weather_info_->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal));
-  // Aligns its child views to the center point.
-  weather_info_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kCenter);
-  weather_info_layout->set_between_child_spacing(
-      kSpacingBetweenWeatherIconAndTempDip);
-
-  // This view should be baseline-aligned to the time view.
-  weather_info_layout->set_inside_border_insets(GetWeatherInfoInsets());
+  time_view_->SetTextShadowValues(text_shadow_values);
+  // Remove the internal spacing in `time_view_` and adjust spacing for shadows.
+  time_view_->SetBorder(views::CreateEmptyBorder(
+      -kUnifiedTrayTextTopPadding, -kUnifiedTrayTimeLeftPadding, 0,
+      kSpacingBetweenTimeAndWeatherDip + shadow_insets.right()));
 
   // Inits the icon view.
-  weather_condition_icon_ =
-      weather_info_->AddChildView(std::make_unique<views::ImageView>());
+  weather_condition_icon_ = AddChildView(std::make_unique<views::ImageView>());
   const gfx::Size size = gfx::Size(kWeatherIconSizeDip, kWeatherIconSizeDip);
   weather_condition_icon_->SetSize(size);
   weather_condition_icon_->SetImageSize(size);
+  constexpr int kIconInternalPaddingDip = 4;
+  weather_condition_icon_->SetBorder(views::CreateEmptyBorder(
+      0, 0,
+      GetTimeFontDescent() - shadow_insets.bottom() - kIconInternalPaddingDip,
+      kSpacingBetweenWeatherIconAndTempDip + shadow_insets.left()));
 
   // Inits the temp view.
-  temperature_ = weather_info_->AddChildView(std::make_unique<views::Label>());
+  temperature_ = AddChildView(std::make_unique<views::Label>());
   temperature_->SetAutoColorReadabilityEnabled(false);
   temperature_->SetEnabledColor(kTextColor);
   temperature_->SetFontList(GetWeatherTemperatureFontList());
-  temperature_->SetShadows(ambient::util::GetTextShadowValues());
+  temperature_->SetShadows(text_shadow_values);
+  temperature_->SetBorder(views::CreateEmptyBorder(
+      0, 0, GetTimeFontDescent() - GetTemperatureFontDescent(), 0));
 }
 
 }  // namespace ash
diff --git a/ash/ambient/ui/glanceable_info_view.h b/ash/ambient/ui/glanceable_info_view.h
index d0d4593..31c0bf1 100644
--- a/ash/ambient/ui/glanceable_info_view.h
+++ b/ash/ambient/ui/glanceable_info_view.h
@@ -47,11 +47,7 @@
   // View for the time info. Owned by the view hierarchy.
   ash::tray::TimeView* time_view_ = nullptr;
 
-  // Container holding weather-related views. Owned by the view hierarchy.
-  views::View* weather_info_ = nullptr;
-
   // Views for weather icon and temperature.
-  // Child views of |weather_info_| and owned by the view hierarchy.
   views::ImageView* weather_condition_icon_ = nullptr;
   views::Label* temperature_ = nullptr;
 
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc
index 0337908..d0780d9 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.cc
+++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -4,6 +4,7 @@
 
 #include "ash/app_list/views/search_result_suggestion_chip_view.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "ash/app_list/app_list_metrics.h"
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index f14a3f7..a816c15 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -330,7 +330,7 @@
       <message name="IDS_ASH_STATUS_TRAY_CAST_CAST_DESKTOP" desc="The label used in the tray popup to tell the user we are casting the desktop.">
         Casting screen to <ph name="RECEIVER_NAME">$1<ex>Living Room</ex></ph>
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_DARK_THEME" desc="The label used as the header in the dark theme popup. [CHAR_LIMIT=14]">
+      <message name="IDS_ASH_STATUS_TRAY_DARK_THEME" desc="The label used as the header in the dark theme popup. [CHAR_LIMIT=17">
         Dark theme
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DARK_THEME_TOGGLE_TOOLTIP" desc="The tooltip text used for the button in the status tray to toggle the Dark theme feature on or off.">
@@ -943,6 +943,15 @@
       <message name="IDS_ASH_HOLDING_SPACE_SCREENSHOTS_TITLE" desc="Title of the screenshots area in the holding space bubble.">
         Screenshots
       </message>
+      <message name="IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_COPY_TO_CLIPBOARD" desc="Title of the option to copy items to clipboard.">
+        Copy to clipboard
+      </message>
+      <message name="IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PIN" desc="Title of the pin option in the holding space item context menu.">
+        Pin
+      </message>
+      <message name="IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_IN_FOLDER" desc="Title of the option to show item in its folder in the holding space item context menu.">
+        Show in folder
+      </message>
 
       <!-- Phone Hub tray-->
       <message name="IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME" desc="The accessible name of the Phone Hub tray bubble for screen readers.">
diff --git a/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_COPY_TO_CLIPBOARD.png.sha1 b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_COPY_TO_CLIPBOARD.png.sha1
new file mode 100644
index 0000000..8813b9e1
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_COPY_TO_CLIPBOARD.png.sha1
@@ -0,0 +1 @@
+b1012145df7abf2c183a8b9f8fc6f3fdf8efd6f8
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PIN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PIN.png.sha1
new file mode 100644
index 0000000..8813b9e1
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PIN.png.sha1
@@ -0,0 +1 @@
+b1012145df7abf2c183a8b9f8fc6f3fdf8efd6f8
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_IN_FOLDER.png.sha1 b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_IN_FOLDER.png.sha1
new file mode 100644
index 0000000..8813b9e1
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_IN_FOLDER.png.sha1
@@ -0,0 +1 @@
+b1012145df7abf2c183a8b9f8fc6f3fdf8efd6f8
\ No newline at end of file
diff --git a/ash/display/cros_display_config_unittest.cc b/ash/display/cros_display_config_unittest.cc
index 45d185b..77a5ba4 100644
--- a/ash/display/cros_display_config_unittest.cc
+++ b/ash/display/cros_display_config_unittest.cc
@@ -230,8 +230,7 @@
   TestObserver observer;
   mojo::AssociatedRemote<mojom::CrosDisplayConfigObserver> observer_remote;
   mojo::AssociatedReceiver<mojom::CrosDisplayConfigObserver> receiver(
-      &observer,
-      observer_remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      &observer, observer_remote.BindNewEndpointAndPassDedicatedReceiver());
   cros_display_config()->AddObserver(observer_remote.Unbind());
   base::RunLoop().RunUntilIdle();
 
@@ -751,8 +750,7 @@
   TestObserver observer;
   mojo::AssociatedRemote<mojom::CrosDisplayConfigObserver> observer_remote;
   mojo::AssociatedReceiver<mojom::CrosDisplayConfigObserver> receiver(
-      &observer,
-      observer_remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      &observer, observer_remote.BindNewEndpointAndPassDedicatedReceiver());
   cros_display_config()->AddObserver(observer_remote.Unbind());
   base::RunLoop().RunUntilIdle();
 
diff --git a/ash/public/cpp/holding_space/holding_space_constants.h b/ash/public/cpp/holding_space/holding_space_constants.h
index ff2b1e2e..5bcb3873 100644
--- a/ash/public/cpp/holding_space/holding_space_constants.h
+++ b/ash/public/cpp/holding_space/holding_space_constants.h
@@ -5,11 +5,20 @@
 #ifndef ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_CONSTANTS_H_
 #define ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_CONSTANTS_H_
 
+#include "ash/public/cpp/app_menu_constants.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace ash {
 
+// Context menu commands.
+enum HoldingSpaceCommandId {
+  kTogglePinItem,
+  kCopyToClipboard,
+  kShowInFolder,
+  kMaxValue = kShowInFolder
+};
+
 // Appearance.
 constexpr gfx::Insets kHoldingSpaceContainerPadding(16);
 constexpr int kHoldingSpaceContainerSeparation = 8;
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 53deb1e..ca2779e 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -179,14 +179,11 @@
 
   bool in_tablet_mode() const { return in_tablet_mode_; }
 
-  // Ink drop color for shelf items.
-  SkColor GetInkDropBaseColor() const;
-
-  // Opacity of the ink drop ripple for shelf items when the ripple is visible.
-  float GetInkDropVisibleOpacity() const;
-
   bool in_overview_mode() const { return overview_mode_; }
 
+  // Ink drop RippleAttributes for shelf items.
+  AshColorProvider::RippleAttributes GetInkDropRippleAttributes() const;
+
   // Gets the current color for the shelf control buttons.
   SkColor GetShelfControlButtonColor() const;
 
diff --git a/ash/scoped_animation_disabler.cc b/ash/scoped_animation_disabler.cc
index b1197620..97706dc 100644
--- a/ash/scoped_animation_disabler.cc
+++ b/ash/scoped_animation_disabler.cc
@@ -11,14 +11,17 @@
 
 ScopedAnimationDisabler::ScopedAnimationDisabler(aura::Window* window)
     : window_(window) {
+  DCHECK(window_);
   needs_disable_ = !window_->GetProperty(aura::client::kAnimationsDisabledKey);
   if (needs_disable_)
     window_->SetProperty(aura::client::kAnimationsDisabledKey, true);
 }
 
 ScopedAnimationDisabler::~ScopedAnimationDisabler() {
-  if (needs_disable_)
+  if (needs_disable_) {
+    DCHECK_EQ(window_->GetProperty(aura::client::kAnimationsDisabledKey), true);
     window_->ClearProperty(aura::client::kAnimationsDisabledKey);
+  }
 }
 
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash
diff --git a/ash/scoped_animation_disabler.h b/ash/scoped_animation_disabler.h
index 2c07ea3..f772251 100644
--- a/ash/scoped_animation_disabler.h
+++ b/ash/scoped_animation_disabler.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SCOPED_ANIMATION_DISABLER_H_
 #define ASH_SCOPED_ANIMATION_DISABLER_H_
 
-#include "base/macros.h"
+#include "ash/ash_export.h"
 
 namespace aura {
 class Window;
@@ -16,16 +16,16 @@
 // Helper class to perform window state changes without animations. Used to hide
 // /show/minimize windows without having their animation interfere with the ones
 // this class is in charge of.
-class ScopedAnimationDisabler {
+class ASH_EXPORT ScopedAnimationDisabler {
  public:
   explicit ScopedAnimationDisabler(aura::Window* window);
+  ScopedAnimationDisabler(const ScopedAnimationDisabler&) = delete;
+  ScopedAnimationDisabler& operator=(const ScopedAnimationDisabler&) = delete;
   ~ScopedAnimationDisabler();
 
  private:
-  aura::Window* window_;
+  aura::Window* const window_;
   bool needs_disable_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedAnimationDisabler);
 };
 
 }  // namespace ash
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index aa7e70b..d4f644b 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -175,9 +175,10 @@
     SetFocusPainter(nullptr);
     SetInkDropMode(InkDropMode::ON);
     set_has_ink_drop_action_on_click(true);
-    set_ink_drop_base_color(ShelfConfig::Get()->GetInkDropBaseColor());
-    set_ink_drop_visible_opacity(
-        ShelfConfig::Get()->GetInkDropVisibleOpacity());
+    AshColorProvider::RippleAttributes ripple_attributes =
+        ShelfConfig::Get()->GetInkDropRippleAttributes();
+    set_ink_drop_base_color(ripple_attributes.base_color);
+    set_ink_drop_visible_opacity(ripple_attributes.inkdrop_opacity);
 
     // Layer rendering is required when the shelf background is visible, which
     // happens when the wallpaper is not blurred.
@@ -291,9 +292,10 @@
     SetFocusPainter(nullptr);
     SetInkDropMode(InkDropMode::ON);
     set_has_ink_drop_action_on_click(true);
-    set_ink_drop_base_color(ShelfConfig::Get()->GetInkDropBaseColor());
-    set_ink_drop_visible_opacity(
-        ShelfConfig::Get()->GetInkDropVisibleOpacity());
+    AshColorProvider::RippleAttributes ripple_attributes =
+        ShelfConfig::Get()->GetInkDropRippleAttributes();
+    set_ink_drop_base_color(ripple_attributes.base_color);
+    set_ink_drop_visible_opacity(ripple_attributes.inkdrop_opacity);
 
     // Layer rendering is required when the shelf background is visible, which
     // happens when the wallpaper is not blurred.
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index 0b47559..65c22cf 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -21,8 +21,10 @@
       shelf_button_delegate_(shelf_button_delegate) {
   DCHECK(shelf_button_delegate_);
   set_hide_ink_drop_when_showing_context_menu(false);
-  set_ink_drop_base_color(ShelfConfig::Get()->GetInkDropBaseColor());
-  set_ink_drop_visible_opacity(ShelfConfig::Get()->GetInkDropVisibleOpacity());
+  AshColorProvider::RippleAttributes ripple_attributes =
+      ShelfConfig::Get()->GetInkDropRippleAttributes();
+  set_ink_drop_base_color(ripple_attributes.base_color);
+  set_ink_drop_visible_opacity(ripple_attributes.inkdrop_opacity);
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
   SetFocusPainter(views::Painter::CreateSolidFocusPainter(
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index b76428d..f46efb4 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -444,13 +444,9 @@
   return is_dense_ ? 48 : 56;
 }
 
-SkColor ShelfConfig::GetInkDropBaseColor() const {
-  return AshColorProvider::Get()->GetInkDropBaseColor(
-      AshColorProvider::AshColorMode::kDark);
-}
-
-float ShelfConfig::GetInkDropVisibleOpacity() const {
-  return AshColorProvider::Get()->GetInkDropVisibleOpacity();
+AshColorProvider::RippleAttributes ShelfConfig::GetInkDropRippleAttributes()
+    const {
+  return AshColorProvider::Get()->GetRippleAttributes(GetDefaultShelfColor());
 }
 
 SkColor ShelfConfig::GetShelfControlButtonColor() const {
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 677d11a..f018db1 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -272,17 +272,6 @@
   return IsThemed() ? GetBackgroundThemedColor() : GetBackgroundDefaultColor();
 }
 
-SkColor AshColorProvider::GetInkDropBaseColor(
-    AshColorMode given_color_mode) const {
-  AshColorMode color_mode =
-      color_mode_ != AshColorMode::kDefault ? color_mode_ : given_color_mode;
-  return color_mode == AshColorMode::kLight ? SK_ColorBLACK : SK_ColorWHITE;
-}
-
-float AshColorProvider::GetInkDropVisibleOpacity() const {
-  return 0.2f;
-}
-
 void AshColorProvider::DecoratePillButton(views::LabelButton* button,
                                           ButtonType type,
                                           const gfx::VectorIcon& icon) {
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index d7b1274..3eafb48c 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -205,12 +205,6 @@
   // |is_themed_|).
   SkColor GetBackgroundColor() const;
 
-  // Ink drop color for shelf items.
-  SkColor GetInkDropBaseColor(AshColorMode given_color_mode) const;
-
-  // Opacity of the ink drop ripple for shelf items when the ripple is visible.
-  float GetInkDropVisibleOpacity() const;
-
   // Helpers to style buttons based on the desired |type| and theme. Depending
   // on the type may style text, icon and background colors for both enabled and
   // disabled states. May overwrite an prior styles on |button|.
diff --git a/ash/system/holding_space/holding_space_item_chip_view.cc b/ash/system/holding_space/holding_space_item_chip_view.cc
index dc38334..e8784de6 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.cc
+++ b/ash/system/holding_space/holding_space_item_chip_view.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/style/ash_color_provider.h"
+#include "ash/system/holding_space/holding_space_item_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_item_style.h"
 #include "ash/system/tray/tray_popup_utils.h"
@@ -15,8 +16,6 @@
 #include "ash/system/user/rounded_image_view.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/text_constants.h"
-#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/background.h"
@@ -30,7 +29,7 @@
 namespace ash {
 
 HoldingSpaceItemChipView::HoldingSpaceItemChipView(const HoldingSpaceItem* item)
-    : item_(item) {
+    : HoldingSpaceItemView(item) {
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kHorizontal,
       gfx::Insets(kHoldingSpaceChipPadding), kHoldingSpaceChipChildSpacing));
@@ -38,7 +37,7 @@
   image_ =
       AddChildView(std::make_unique<tray::RoundedImageView>(kTrayItemSize / 2));
 
-  label_ = AddChildView(std::make_unique<views::Label>(item_->text()));
+  label_ = AddChildView(std::make_unique<views::Label>(item->text()));
   label_->SetElideBehavior(gfx::ELIDE_MIDDLE);
   layout->SetFlexForView(label_, 1);
 
@@ -47,18 +46,14 @@
 
   AddPinButton();
 
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-
   SetBackground(views::CreateRoundedRectBackground(
       AshColorProvider::Get()->GetControlsLayerColor(
           AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive),
       kHoldingSpaceChipCornerRadius));
 
-  GetViewAccessibility().OverrideName(item_->text());
-  SetFocusBehavior(FocusBehavior::ALWAYS);
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-  set_ink_drop_visible_opacity(ShelfConfig::Get()->GetInkDropVisibleOpacity());
+  set_ink_drop_visible_opacity(
+      ShelfConfig::Get()->GetInkDropRippleAttributes().inkdrop_opacity);
   SetNotifyEnterExitOnChild(true);
 
   // Ink drop layers should be clipped to match the corner radius of this view.
@@ -67,7 +62,7 @@
 
   // Subscribe to be notified of changes to `item_`'s image.
   image_subscription_ =
-      item_->image().AddImageSkiaChangedCallback(base::BindRepeating(
+      item->image().AddImageSkiaChangedCallback(base::BindRepeating(
           &HoldingSpaceItemChipView::Update, base::Unretained(this)));
 
   Update();
@@ -75,19 +70,6 @@
 
 HoldingSpaceItemChipView::~HoldingSpaceItemChipView() = default;
 
-SkColor HoldingSpaceItemChipView::GetInkDropBaseColor() const {
-  return ShelfConfig::Get()->GetInkDropBaseColor();
-}
-
-int HoldingSpaceItemChipView::GetDragOperations(const gfx::Point& point) {
-  return ui::DragDropTypes::DRAG_COPY;
-}
-
-void HoldingSpaceItemChipView::WriteDragData(const gfx::Point& point,
-                                             ui::OSExchangeData* data) {
-  data->SetFilename(item_->file_path());
-}
-
 void HoldingSpaceItemChipView::OnMouseEvent(ui::MouseEvent* event) {
   switch (event->type()) {
     case ui::ET_MOUSE_ENTERED:
@@ -128,11 +110,11 @@
 
 void HoldingSpaceItemChipView::Update() {
   image_->SetImage(
-      item_->image().image_skia(),
+      item()->image().image_skia(),
       gfx::Size(kHoldingSpaceChipIconSize, kHoldingSpaceChipIconSize));
 }
 
-BEGIN_METADATA(HoldingSpaceItemChipView, views::InkDropHostView)
+BEGIN_METADATA(HoldingSpaceItemChipView, HoldingSpaceItemView)
 END_METADATA
 
 }  // namespace ash
diff --git a/ash/system/holding_space/holding_space_item_chip_view.h b/ash/system/holding_space/holding_space_item_chip_view.h
index 925b73ad..a0b1a54d 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.h
+++ b/ash/system/holding_space/holding_space_item_chip_view.h
@@ -9,7 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/holding_space/holding_space_image.h"
-#include "ui/views/animation/ink_drop_host_view.h"
+#include "ash/system/holding_space/holding_space_item_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/metadata/metadata_header_macros.h"
 
@@ -28,7 +28,7 @@
 
 // A button with an image derived from a file's thumbnail and file's name as the
 // label.
-class ASH_EXPORT HoldingSpaceItemChipView : public views::InkDropHostView,
+class ASH_EXPORT HoldingSpaceItemChipView : public HoldingSpaceItemView,
                                             public views::ButtonListener {
  public:
   METADATA_HEADER(HoldingSpaceItemChipView);
@@ -38,10 +38,7 @@
   HoldingSpaceItemChipView& operator=(const HoldingSpaceItemChipView&) = delete;
   ~HoldingSpaceItemChipView() override;
 
-  // views::InkDropHostView:
-  SkColor GetInkDropBaseColor() const override;
-  int GetDragOperations(const gfx::Point& point) override;
-  void WriteDragData(const gfx::Point& point, ui::OSExchangeData*) override;
+  // HoldingSpaceItemView:
   void OnMouseEvent(ui::MouseEvent* event) override;
 
   // views::ButtonListener:
@@ -51,7 +48,6 @@
   void AddPinButton();
   void Update();
 
-  const HoldingSpaceItem* const item_;
   tray::RoundedImageView* image_ = nullptr;
   views::Label* label_ = nullptr;
   views::ToggleImageButton* pin_ = nullptr;
diff --git a/ash/system/holding_space/holding_space_item_context_menu.cc b/ash/system/holding_space/holding_space_item_context_menu.cc
new file mode 100644
index 0000000..0e4cecf6
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_context_menu.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/holding_space/holding_space_item_context_menu.h"
+
+#include "ash/public/cpp/holding_space/holding_space_constants.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+HoldingSpaceItemContextMenu::HoldingSpaceItemContextMenu() = default;
+
+HoldingSpaceItemContextMenu::~HoldingSpaceItemContextMenu() = default;
+
+ui::SimpleMenuModel* HoldingSpaceItemContextMenu::BuildMenuModel() {
+  context_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
+  context_menu_model_->AddItem(
+      HoldingSpaceCommandId::kShowInFolder,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_IN_FOLDER));
+  context_menu_model_->AddItem(
+      HoldingSpaceCommandId::kCopyToClipboard,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_COPY_TO_CLIPBOARD));
+  context_menu_model_->AddItem(
+      HoldingSpaceCommandId::kTogglePinItem,
+      l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PIN));
+
+  return context_menu_model_.get();
+}
+
+void HoldingSpaceItemContextMenu::ShowContextMenuForViewImpl(
+    views::View* source,
+    const gfx::Point& point,
+    ui::MenuSourceType source_type) {
+  int run_types = views::MenuRunner::USE_TOUCHABLE_LAYOUT |
+                  views::MenuRunner::CONTEXT_MENU |
+                  views::MenuRunner::FIXED_ANCHOR;
+
+  context_menu_runner_ =
+      std::make_unique<views::MenuRunner>(BuildMenuModel(), run_types);
+
+  context_menu_runner_->RunMenuAt(
+      source->GetWidget(), nullptr /*button_controller*/,
+      source->GetBoundsInScreen(), views::MenuAnchorPosition::kBubbleRight,
+      source_type);
+}
+
+void HoldingSpaceItemContextMenu::ExecuteCommand(int command_id,
+                                                 int event_flags) {
+  switch (command_id) {
+    case HoldingSpaceCommandId::kCopyToClipboard:
+      // TODO(crbug.com/1127240): Hookup API for copy to clipboard
+      break;
+    case HoldingSpaceCommandId::kShowInFolder:
+      // TODO(crbug.com/1127240): Hookup API for show in folder
+      break;
+    case HoldingSpaceCommandId::kTogglePinItem:
+      // TODO(crbug.com/1127240): Hookup API for toggling pin
+      break;
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_item_context_menu.h b/ash/system/holding_space/holding_space_item_context_menu.h
new file mode 100644
index 0000000..770430e1
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_context_menu.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_CONTEXT_MENU_H_
+#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_CONTEXT_MENU_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/views/context_menu_controller.h"
+
+namespace views {
+class MenuRunner;
+}  // namespace views
+
+namespace ash {
+
+// This class handles creation of the context menu view and handling
+// commands available in the menu.
+class ASH_EXPORT HoldingSpaceItemContextMenu
+    : public views::ContextMenuController,
+      public ui::SimpleMenuModel::Delegate {
+ public:
+  HoldingSpaceItemContextMenu();
+  HoldingSpaceItemContextMenu(const HoldingSpaceItemContextMenu&) = delete;
+  HoldingSpaceItemContextMenu& operator=(const HoldingSpaceItemContextMenu&) =
+      delete;
+  ~HoldingSpaceItemContextMenu() override;
+
+  // views::ContextMenuController:
+  void ShowContextMenuForViewImpl(views::View* source,
+                                  const gfx::Point& point,
+                                  ui::MenuSourceType source_type) override;
+
+  // SimpleMenuModel::Delegate:
+  void ExecuteCommand(int command_id, int event_flags) override;
+
+ private:
+  ui::SimpleMenuModel* BuildMenuModel();
+
+  std::unique_ptr<ui::SimpleMenuModel> context_menu_model_;
+  std::unique_ptr<views::MenuRunner> context_menu_runner_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_CONTEXT_MENU_H_
diff --git a/ash/system/holding_space/holding_space_item_screenshot_view.cc b/ash/system/holding_space/holding_space_item_screenshot_view.cc
new file mode 100644
index 0000000..50d7a42
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_screenshot_view.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/holding_space/holding_space_item_screenshot_view.h"
+
+#include "ash/public/cpp/holding_space/holding_space_constants.h"
+#include "ash/public/cpp/holding_space/holding_space_image.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/user/rounded_image_view.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
+
+namespace ash {
+
+HoldingSpaceItemScreenshotView::HoldingSpaceItemScreenshotView(
+    const HoldingSpaceItem* item)
+    : HoldingSpaceItemView(item) {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  image_ =
+      AddChildView(std::make_unique<tray::RoundedImageView>(kTrayItemSize / 2));
+
+  // Subscribe to be notified of changes to `item_`'s image.
+  image_subscription_ =
+      item->image().AddImageSkiaChangedCallback(base::BindRepeating(
+          &HoldingSpaceItemScreenshotView::Update, base::Unretained(this)));
+
+  Update();
+}
+
+HoldingSpaceItemScreenshotView::~HoldingSpaceItemScreenshotView() = default;
+
+void HoldingSpaceItemScreenshotView::Update() {
+  image_->SetImage(item()->image().image_skia(), kHoldingSpaceScreenshotSize);
+}
+
+BEGIN_METADATA(HoldingSpaceItemScreenshotView, HoldingSpaceItemView)
+END_METADATA
+
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_item_screenshot_view.h b/ash/system/holding_space/holding_space_item_screenshot_view.h
new file mode 100644
index 0000000..14035bf
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_screenshot_view.h
@@ -0,0 +1,42 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_SCREENSHOT_VIEW_H_
+#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_SCREENSHOT_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/public/cpp/holding_space/holding_space_image.h"
+#include "ash/system/holding_space/holding_space_item_view.h"
+#include "ui/views/metadata/metadata_header_macros.h"
+
+namespace ash {
+
+class HoldingSpaceItem;
+
+namespace tray {
+class RoundedImageView;
+}  // namespace tray
+
+class ASH_EXPORT HoldingSpaceItemScreenshotView : public HoldingSpaceItemView {
+ public:
+  METADATA_HEADER(HoldingSpaceItemScreenshotView);
+
+  explicit HoldingSpaceItemScreenshotView(const HoldingSpaceItem* item);
+  HoldingSpaceItemScreenshotView(const HoldingSpaceItemScreenshotView&) =
+      delete;
+  HoldingSpaceItemScreenshotView& operator=(
+      const HoldingSpaceItemScreenshotView&) = delete;
+  ~HoldingSpaceItemScreenshotView() override;
+
+ private:
+  void Update();
+
+  tray::RoundedImageView* image_ = nullptr;
+
+  std::unique_ptr<HoldingSpaceImage::Subscription> image_subscription_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_SCREENSHOT_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_item_view.cc b/ash/system/holding_space/holding_space_item_view.cc
new file mode 100644
index 0000000..c222172e
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_view.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/holding_space/holding_space_item_view.h"
+
+#include "ash/public/cpp/holding_space/holding_space_constants.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/shelf_config.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/holding_space/holding_space_item_context_menu.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/controls/menu/menu_runner.h"
+
+namespace ash {
+
+HoldingSpaceItemView::HoldingSpaceItemView(const HoldingSpaceItem* item)
+    : item_(item),
+      context_menu_(std::make_unique<HoldingSpaceItemContextMenu>()) {
+  set_context_menu_controller(context_menu_.get());
+
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
+
+  GetViewAccessibility().OverrideName(item->text());
+  SetFocusBehavior(FocusBehavior::ALWAYS);
+}
+
+HoldingSpaceItemView::~HoldingSpaceItemView() = default;
+
+int HoldingSpaceItemView::GetDragOperations(const gfx::Point& point) {
+  return ui::DragDropTypes::DRAG_COPY;
+}
+
+SkColor HoldingSpaceItemView::GetInkDropBaseColor() const {
+  return ShelfConfig::Get()->GetInkDropRippleAttributes().base_color;
+}
+
+void HoldingSpaceItemView::WriteDragData(const gfx::Point& point,
+                                         ui::OSExchangeData* data) {
+  data->SetFilename(item_->file_path());
+}
+
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_item_view.h b/ash/system/holding_space/holding_space_item_view.h
new file mode 100644
index 0000000..b03b15f
--- /dev/null
+++ b/ash/system/holding_space/holding_space_item_view.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEW_H_
+#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEW_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/views/animation/ink_drop_host_view.h"
+
+namespace ash {
+
+class HoldingSpaceItem;
+class HoldingSpaceItemContextMenu;
+
+// Base class for HoldingSpaceItemChipView and HoldingSpaceItemScreenshotView.
+class ASH_EXPORT HoldingSpaceItemView : public views::InkDropHostView {
+ public:
+  explicit HoldingSpaceItemView(const HoldingSpaceItem* item);
+  HoldingSpaceItemView(const HoldingSpaceItemView&) = delete;
+  HoldingSpaceItemView& operator=(const HoldingSpaceItemView&) = delete;
+  ~HoldingSpaceItemView() override;
+
+  // views::InkDropHostView:
+  int GetDragOperations(const gfx::Point& point) override;
+  SkColor GetInkDropBaseColor() const override;
+  void WriteDragData(const gfx::Point& point, ui::OSExchangeData*) override;
+
+  const HoldingSpaceItem* item() const { return item_; }
+
+ private:
+  const HoldingSpaceItem* const item_;
+  std::unique_ptr<HoldingSpaceItemContextMenu> const context_menu_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_screenshot_view.cc b/ash/system/holding_space/holding_space_screenshot_view.cc
deleted file mode 100644
index e2bce1a..0000000
--- a/ash/system/holding_space/holding_space_screenshot_view.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/system/holding_space/holding_space_screenshot_view.h"
-
-#include "ash/public/cpp/holding_space/holding_space_constants.h"
-#include "ash/public/cpp/holding_space/holding_space_item.h"
-#include "ash/system/tray/tray_constants.h"
-#include "ash/system/user/rounded_image_view.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/layout/fill_layout.h"
-#include "ui/views/metadata/metadata_impl_macros.h"
-
-namespace ash {
-
-HoldingSpaceScreenshotView::HoldingSpaceScreenshotView(
-    const HoldingSpaceItem* item)
-    : item_(item) {
-  SetLayoutManager(std::make_unique<views::FillLayout>());
-
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-
-  GetViewAccessibility().OverrideName(item_->text());
-  SetFocusBehavior(FocusBehavior::ALWAYS);
-
-  image_ =
-      AddChildView(std::make_unique<tray::RoundedImageView>(kTrayItemSize / 2));
-
-  // Subscribe to be notified of changes to `item_`'s image.
-  image_subscription_ =
-      item_->image().AddImageSkiaChangedCallback(base::BindRepeating(
-          &HoldingSpaceScreenshotView::Update, base::Unretained(this)));
-
-  Update();
-}
-
-HoldingSpaceScreenshotView::~HoldingSpaceScreenshotView() = default;
-
-int HoldingSpaceScreenshotView::GetDragOperations(const gfx::Point& point) {
-  return ui::DragDropTypes::DRAG_COPY;
-}
-
-void HoldingSpaceScreenshotView::WriteDragData(const gfx::Point& point,
-                                               ui::OSExchangeData* data) {
-  data->SetFilename(item_->file_path());
-}
-
-void HoldingSpaceScreenshotView::Update() {
-  image_->SetImage(item_->image().image_skia(), kHoldingSpaceScreenshotSize);
-}
-
-BEGIN_METADATA(HoldingSpaceScreenshotView, views::View)
-END_METADATA
-
-}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_screenshot_view.h b/ash/system/holding_space/holding_space_screenshot_view.h
deleted file mode 100644
index 0100a23..0000000
--- a/ash/system/holding_space/holding_space_screenshot_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_SCREENSHOT_VIEW_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_SCREENSHOT_VIEW_H_
-
-#include <memory>
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/holding_space/holding_space_image.h"
-#include "ui/views/metadata/metadata_header_macros.h"
-#include "ui/views/view.h"
-
-namespace ash {
-
-class HoldingSpaceItem;
-
-namespace tray {
-class RoundedImageView;
-}  // namespace tray
-
-class ASH_EXPORT HoldingSpaceScreenshotView : public views::View {
- public:
-  METADATA_HEADER(HoldingSpaceScreenshotView);
-
-  explicit HoldingSpaceScreenshotView(const HoldingSpaceItem* item);
-  HoldingSpaceScreenshotView(const HoldingSpaceScreenshotView&) = delete;
-  HoldingSpaceScreenshotView& operator=(const HoldingSpaceScreenshotView&) =
-      delete;
-  ~HoldingSpaceScreenshotView() override;
-
-  // views::View:
-  int GetDragOperations(const gfx::Point& point) override;
-  void WriteDragData(const gfx::Point& point, ui::OSExchangeData*) override;
-
- private:
-  void Update();
-
-  const HoldingSpaceItem* const item_;
-  tray::RoundedImageView* image_ = nullptr;
-
-  std::unique_ptr<HoldingSpaceImage::Subscription> image_subscription_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_SCREENSHOT_VIEW_H_
diff --git a/ash/system/holding_space/holding_space_test_api.cc b/ash/system/holding_space/holding_space_test_api.cc
index f9f6f01..f203695 100644
--- a/ash/system/holding_space/holding_space_test_api.cc
+++ b/ash/system/holding_space/holding_space_test_api.cc
@@ -11,7 +11,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/holding_space/holding_space_item_chip_view.h"
-#include "ash/system/holding_space/holding_space_screenshot_view.h"
+#include "ash/system/holding_space/holding_space_item_screenshot_view.h"
 #include "ash/system/holding_space/holding_space_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ui/aura/window.h"
@@ -114,7 +114,7 @@
 std::vector<views::View*> HoldingSpaceTestApi::GetScreenshotViews() {
   std::vector<views::View*> screenshot_views;
   if (holding_space_tray_->GetBubbleView()) {
-    FindDescendentsOfClass<HoldingSpaceScreenshotView>(
+    FindDescendentsOfClass<HoldingSpaceItemScreenshotView>(
         holding_space_tray_->GetBubbleView()->GetViewByID(
             kHoldingSpaceRecentFilesContainerId),
         &screenshot_views);
diff --git a/ash/system/holding_space/recent_files_container.cc b/ash/system/holding_space/recent_files_container.cc
index df22996..6950725 100644
--- a/ash/system/holding_space/recent_files_container.cc
+++ b/ash/system/holding_space/recent_files_container.cc
@@ -10,7 +10,7 @@
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/holding_space/holding_space_item_chips_container.h"
-#include "ash/system/holding_space/holding_space_screenshot_view.h"
+#include "ash/system/holding_space/holding_space_item_screenshot_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_item_style.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -48,7 +48,7 @@
   for (const auto& item : HoldingSpaceController::Get()->model()->items()) {
     if (item->type() == HoldingSpaceItem::Type::kScreenshot) {
       screenshots_container_->AddChildView(
-          std::make_unique<HoldingSpaceScreenshotView>(item.get()));
+          std::make_unique<HoldingSpaceItemScreenshotView>(item.get()));
     }
   }
 
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 149d4f66..52edc6ec 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -160,8 +160,11 @@
       widget_observer_(new TrayWidgetObserver(this)) {
   DCHECK(shelf_);
   SetNotifyEnterExitOnChild(true);
-  set_ink_drop_base_color(ShelfConfig::Get()->GetInkDropBaseColor());
-  set_ink_drop_visible_opacity(ShelfConfig::Get()->GetInkDropVisibleOpacity());
+  AshColorProvider::RippleAttributes ripple_attributes =
+      ShelfConfig::Get()->GetInkDropRippleAttributes();
+
+  set_ink_drop_base_color(ripple_attributes.base_color);
+  set_ink_drop_visible_opacity(ripple_attributes.inkdrop_opacity);
 
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
   SetLayoutManager(std::make_unique<views::FillLayout>());
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index f37a8c90..49cfd3f 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -27,6 +27,7 @@
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_utils.h"
+#include "ash/wm/window_cycle_controller.h"
 #include "ash/wm/window_util.h"
 #include "base/auto_reset.h"
 #include "base/check_op.h"
@@ -545,6 +546,13 @@
 
   MaybeUpdateShelfItems(old_active->windows(), active_desk_->windows());
 
+  // If in the middle of a window cycle gesture, reset the window cycle list
+  // contents so it contains the new active desk's windows.
+  if (features::IsAltTabLimitedToActiveDesk()) {
+    auto* window_cycle_controller = Shell::Get()->window_cycle_controller();
+    window_cycle_controller->MaybeResetCycleList();
+  }
+
   for (auto& observer : observers_)
     observer.OnDeskActivationChanged(active_desk_, old_active);
 }
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 3f99cbc0..dbb7b02 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -30,7 +30,8 @@
 
 // Returns the most recently active window from the |window_list| or nullptr
 // if the list is empty.
-aura::Window* GetActiveWindow(const WindowCycleList::WindowList& window_list) {
+aura::Window* GetActiveWindow(
+    const WindowCycleController::WindowList& window_list) {
   return window_list.empty() ? nullptr : window_list[0];
 }
 
@@ -95,18 +96,8 @@
   // (http://crbug.com/895265).
   Shell::Get()->wallpaper_controller()->MaybeClosePreviewWallpaper();
 
-  WindowCycleList::WindowList window_list =
-      Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList(
-          features::IsAltTabLimitedToActiveDesk() ? kActiveDesk : kAllDesks);
-  // Window cycle list windows will handle showing their transient related
-  // windows, so if a window in |window_list| has a transient root also in
-  // |window_list|, we can remove it as the transient root will handle showing
-  // the window.
-  window_util::RemoveTransientDescendants(&window_list);
-
-  active_desk_container_id_before_cycle_ =
-      desks_util::GetActiveDeskContainerId();
-  active_window_before_window_cycle_ = GetActiveWindow(window_list);
+  WindowCycleController::WindowList window_list = CreateWindowList();
+  SaveCurrentActiveDeskAndWindow(window_list);
 
   window_cycle_list_ = std::make_unique<WindowCycleList>(window_list);
   event_filter_ = std::make_unique<WindowCycleEventFilter>();
@@ -124,6 +115,17 @@
   StopCycling();
 }
 
+void WindowCycleController::MaybeResetCycleList() {
+  if (!IsCycling())
+    return;
+
+  WindowCycleController::WindowList window_list = CreateWindowList();
+  SaveCurrentActiveDeskAndWindow(window_list);
+
+  DCHECK(window_cycle_list_);
+  window_cycle_list_->ReplaceWindows(window_list);
+}
+
 void WindowCycleController::StepToWindow(aura::Window* window) {
   DCHECK(window_cycle_list_);
   window_cycle_list_->StepToWindow(window);
@@ -136,6 +138,25 @@
 //////////////////////////////////////////////////////////////////////////////
 // WindowCycleController, private:
 
+WindowCycleController::WindowList WindowCycleController::CreateWindowList() {
+  WindowCycleController::WindowList window_list =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList(
+          features::IsAltTabLimitedToActiveDesk() ? kActiveDesk : kAllDesks);
+  // Window cycle list windows will handle showing their transient related
+  // windows, so if a window in |window_list| has a transient root also in
+  // |window_list|, we can remove it as the transient root will handle showing
+  // the window.
+  window_util::RemoveTransientDescendants(&window_list);
+  return window_list;
+}
+
+void WindowCycleController::SaveCurrentActiveDeskAndWindow(
+    const WindowCycleController::WindowList& window_list) {
+  active_desk_container_id_before_cycle_ =
+      desks_util::GetActiveDeskContainerId();
+  active_window_before_window_cycle_ = GetActiveWindow(window_list);
+}
+
 void WindowCycleController::Step(Direction direction) {
   DCHECK(window_cycle_list_);
   window_cycle_list_->Step(direction);
diff --git a/ash/wm/window_cycle_controller.h b/ash/wm/window_cycle_controller.h
index 7512a36..bdaca12 100644
--- a/ash/wm/window_cycle_controller.h
+++ b/ash/wm/window_cycle_controller.h
@@ -34,6 +34,8 @@
 // order.
 class ASH_EXPORT WindowCycleController {
  public:
+  using WindowList = std::vector<aura::Window*>;
+
   enum Direction { FORWARD, BACKWARD };
 
   WindowCycleController();
@@ -60,6 +62,10 @@
   void CompleteCycling();
   void CancelCycling();
 
+  // If the window cycle list is open, re-construct it. Do nothing if not
+  // cycling.
+  void MaybeResetCycleList();
+
   // Skip window cycle list directly to |window|.
   void StepToWindow(aura::Window* window);
 
@@ -72,6 +78,16 @@
   }
 
  private:
+  // Gets a list of windows from the currently open windows, removing windows
+  // with transient roots already in the list. The returned list of windows
+  // is used to populate the window cycle list.
+  WindowList CreateWindowList();
+
+  // Populates |active_desk_container_id_before_cycle_| and
+  // |active_window_before_window_cycle_| when the window cycle list is
+  // initialized.
+  void SaveCurrentActiveDeskAndWindow(const WindowList& window_list);
+
   // Cycles to the next or previous window based on |direction|.
   void Step(Direction direction);
 
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index 0a00ac73..f90e416 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -850,6 +850,20 @@
   EXPECT_TRUE(base::Contains(cycle_windows, win1.get()));
   cycle_controller->CompleteCycling();
   EXPECT_EQ(win0.get(), window_util::GetActiveWindow());
+
+  // Swap desks while cycling, contents should update.
+  cycle_controller->HandleCycleWindow(WindowCycleController::FORWARD);
+  cycle_windows = GetWindows(cycle_controller);
+  EXPECT_EQ(2u, cycle_windows.size());
+  EXPECT_TRUE(base::Contains(cycle_windows, win0.get()));
+  EXPECT_TRUE(base::Contains(cycle_windows, win1.get()));
+  ActivateDesk(desk_2);
+  EXPECT_TRUE(cycle_controller->IsCycling());
+  cycle_windows = GetWindows(cycle_controller);
+  EXPECT_EQ(1u, cycle_windows.size());
+  EXPECT_TRUE(base::Contains(cycle_windows, win2.get()));
+  cycle_controller->CompleteCycling();
+  EXPECT_EQ(win2.get(), window_util::GetActiveWindow());
 }
 
 class InteractiveWindowCycleControllerTest : public WindowCycleControllerTest {
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 222eee14..dc6c6fb 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -296,6 +296,24 @@
   WindowCycleView& operator=(const WindowCycleView&) = delete;
   ~WindowCycleView() override = default;
 
+  void UpdateWindows(const WindowCycleList::WindowList& windows) {
+    for (auto* window : windows) {
+      auto* view = mirror_container_->AddChildView(
+          std::make_unique<WindowCycleItemView>(window));
+      window_view_map_[window] = view;
+
+      no_previews_set_.insert(view);
+    }
+
+    // Resize the widget.
+    aura::Window* root_window = Shell::GetRootWindowForNewWindows();
+    gfx::Rect widget_rect = root_window->GetBoundsInScreen();
+    widget_rect.ClampToCenteredSize(GetPreferredSize());
+    GetWidget()->SetBounds(widget_rect);
+
+    SetTargetWindow(windows[0]);
+  }
+
   void FadeInLayer() {
     DCHECK(GetWidget());
 
@@ -514,6 +532,25 @@
   Shell::Get()->frame_throttling_controller()->EndThrottling();
 }
 
+void WindowCycleList::ReplaceWindows(const WindowList& windows) {
+  if (windows.empty()) {
+    Shell::Get()->window_cycle_controller()->CancelCycling();
+    return;
+  }
+
+  for (auto* existing_window : windows_)
+    RemoveWindow(existing_window);
+
+  current_index_ = 0;
+  windows_ = windows;
+
+  for (auto* new_window : windows_)
+    new_window->AddObserver(this);
+
+  if (ShouldShowUi() && cycle_view_)
+    cycle_view_->UpdateWindows(windows_);
+}
+
 void WindowCycleList::Step(int offset) {
   if (windows_.empty())
     return;
@@ -580,6 +617,29 @@
 }
 
 void WindowCycleList::OnWindowDestroying(aura::Window* window) {
+  RemoveWindow(window);
+
+  if (cycle_view_ && windows_.empty()) {
+    // This deletes us.
+    Shell::Get()->window_cycle_controller()->CancelCycling();
+  }
+}
+
+void WindowCycleList::OnDisplayMetricsChanged(const display::Display& display,
+                                              uint32_t changed_metrics) {
+  if (cycle_ui_widget_ &&
+      display.id() ==
+          display::Screen::GetScreen()
+              ->GetDisplayNearestWindow(cycle_ui_widget_->GetNativeWindow())
+              .id() &&
+      (changed_metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION))) {
+    Shell::Get()->window_cycle_controller()->CancelCycling();
+    // |this| is deleted.
+    return;
+  }
+}
+
+void WindowCycleList::RemoveWindow(aura::Window* window) {
   window->RemoveObserver(this);
 
   WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
@@ -596,25 +656,6 @@
     auto* new_target_window =
         windows_.empty() ? nullptr : windows_[current_index_];
     cycle_view_->HandleWindowDestruction(window, new_target_window);
-    if (windows_.empty()) {
-      // This deletes us.
-      Shell::Get()->window_cycle_controller()->CancelCycling();
-      return;
-    }
-  }
-}
-
-void WindowCycleList::OnDisplayMetricsChanged(const display::Display& display,
-                                              uint32_t changed_metrics) {
-  if (cycle_ui_widget_ &&
-      display.id() ==
-          display::Screen::GetScreen()
-              ->GetDisplayNearestWindow(cycle_ui_widget_->GetNativeWindow())
-              .id() &&
-      (changed_metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION))) {
-    Shell::Get()->window_cycle_controller()->CancelCycling();
-    // |this| is deleted.
-    return;
   }
 }
 
diff --git a/ash/wm/window_cycle_list.h b/ash/wm/window_cycle_list.h
index 16b899b..f281af8 100644
--- a/ash/wm/window_cycle_list.h
+++ b/ash/wm/window_cycle_list.h
@@ -42,6 +42,10 @@
   WindowCycleList& operator=(const WindowCycleList&) = delete;
   ~WindowCycleList() override;
 
+  // Removes the existing windows and replaces them with |windows|. If
+  // |windows| is empty, cancels cycling.
+  void ReplaceWindows(const WindowList& windows);
+
   // Cycles to the next or previous window based on |direction|.
   void Step(WindowCycleController::Direction direction);
 
@@ -75,6 +79,10 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
+  // Removes |window| from the window list. Also removes the window from
+  // |cycle_view_| if |cycle_view_| exists.
+  void RemoveWindow(aura::Window* window);
+
   // Returns true if the window list overlay should be shown.
   bool ShouldShowUi();
 
diff --git a/base/android/java_exception_reporter.cc b/base/android/java_exception_reporter.cc
index 6859003c..fa3a72a 100644
--- a/base/android/java_exception_reporter.cc
+++ b/base/android/java_exception_reporter.cc
@@ -61,8 +61,11 @@
 }
 
 void SetJavaException(const char* exception) {
-  DCHECK(g_java_exception_callback);
-  g_java_exception_callback(exception);
+  // No need to print exception because they are already logged via
+  // env->ExceptionDescribe() within jni_android.cc.
+  if (g_java_exception_callback) {
+    g_java_exception_callback(exception);
+  }
 }
 
 void JNI_JavaExceptionReporter_ReportJavaException(
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc
index 9456cfd..c40ff80 100644
--- a/base/json/json_reader.cc
+++ b/base/json/json_reader.cc
@@ -46,7 +46,6 @@
   ret.value = parser.Parse(json);
   if (!ret.value) {
     ret.error_message = parser.GetErrorMessage();
-    ret.error_code = parser.error_code();
     ret.error_line = parser.error_line();
     ret.error_column = parser.error_column();
   }
diff --git a/base/json/json_reader.h b/base/json/json_reader.h
index 007f843..65753a4 100644
--- a/base/json/json_reader.h
+++ b/base/json/json_reader.h
@@ -76,7 +76,6 @@
 
     // Contains default values if |value| exists, or the error status if |value|
     // is base::nullopt.
-    int error_code = ValueDeserializer::kErrorCodeNoError;
     std::string error_message;
     int error_line = 0;
     int error_column = 0;
diff --git a/base/json/json_reader_fuzzer.cc b/base/json/json_reader_fuzzer.cc
index 5f27ac2..ea9c62051 100644
--- a/base/json/json_reader_fuzzer.cc
+++ b/base/json/json_reader_fuzzer.cc
@@ -24,9 +24,6 @@
 
   JSONReader::ValueWithError json_val =
       JSONReader::ReadAndReturnValueWithError(input_string, options);
-  CHECK((json_val.error_code == base::ValueDeserializer::kErrorCodeNoError) ==
-        json_val.value.has_value());
-
   if (json_val.value) {
     // Check that the value can be serialized and deserialized back to an
     // equivalent |Value|.
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 519a884..4ff15f1 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -189,8 +189,6 @@
   auto value_with_error =
       JSONReader::ReadAndReturnValueWithError("1e1000", JSON_PARSE_RFC);
   ASSERT_FALSE(value_with_error.value);
-  ASSERT_NE(base::ValueDeserializer::kErrorCodeNoError,
-            value_with_error.error_code);
 }
 
 TEST(JSONReaderTest, FractionalNumbers) {
diff --git a/base/json/json_string_value_serializer.cc b/base/json/json_string_value_serializer.cc
index d98a62e3..7cd2119 100644
--- a/base/json/json_string_value_serializer.cc
+++ b/base/json/json_string_value_serializer.cc
@@ -55,7 +55,7 @@
     return base::Value::ToUniquePtrValue(std::move(*ret.value));
 
   if (error_code)
-    *error_code = ret.error_code;
+    *error_code = base::ValueDeserializer::kErrorCodeInvalidFormat;
   if (error_str)
     *error_str = std::move(ret.error_message);
   return nullptr;
diff --git a/base/profiler/module_cache.cc b/base/profiler/module_cache.cc
index ec08963..4d93da2 100644
--- a/base/profiler/module_cache.cc
+++ b/base/profiler/module_cache.cc
@@ -54,38 +54,41 @@
 }
 
 void ModuleCache::UpdateNonNativeModules(
-    const std::vector<const Module*>& to_remove,
-    std::vector<std::unique_ptr<const Module>> to_add) {
+    const std::vector<const Module*>& defunct_modules,
+    std::vector<std::unique_ptr<const Module>> new_modules) {
   // Insert the modules to remove into a set to support O(log(n)) lookup below.
-  flat_set<const Module*> to_remove_set(to_remove.begin(), to_remove.end());
+  flat_set<const Module*> defunct_modules_set(defunct_modules.begin(),
+                                              defunct_modules.end());
 
   // Reorder the modules to be removed to the last slots in the set, then move
   // them to the inactive modules, then erase the moved-from modules from the
-  // set. The flat_set docs endorse using base::EraseIf() which performs the
-  // same operations -- exclusive of the moves -- so this is OK even though it
-  // might seem like we're messing with the internal set representation.
+  // set. This is a variation on the standard erase-remove idiom, which is
+  // explicitly endorsed for implementing erase behavior on flat_sets.
   //
-  // remove_if is O(m*log(r)) where m is the number of current modules and r is
-  // the number of modules to remove. insert and erase are both O(r).
-  auto first_module_to_remove = std::remove_if(
+  // stable_partition is O(m*log(r)) where m is the number of current modules
+  // and r is the number of modules to remove. insert and erase are both O(r).
+  auto first_module_defunct_modules = std::stable_partition(
       non_native_modules_.begin(), non_native_modules_.end(),
-      [&to_remove_set](const std::unique_ptr<const Module>& module) {
-        return to_remove_set.find(module.get()) != to_remove_set.end();
+      [&defunct_modules_set](const std::unique_ptr<const Module>& module) {
+        return defunct_modules_set.find(module.get()) ==
+               defunct_modules_set.end();
       });
   // All modules requested to be removed should have been found.
-  DCHECK_EQ(static_cast<ptrdiff_t>(to_remove.size()),
-            std::distance(first_module_to_remove, non_native_modules_.end()));
+  DCHECK_EQ(
+      static_cast<ptrdiff_t>(defunct_modules.size()),
+      std::distance(first_module_defunct_modules, non_native_modules_.end()));
   inactive_non_native_modules_.insert(
       inactive_non_native_modules_.end(),
-      std::make_move_iterator(first_module_to_remove),
+      std::make_move_iterator(first_module_defunct_modules),
       std::make_move_iterator(non_native_modules_.end()));
-  non_native_modules_.erase(first_module_to_remove, non_native_modules_.end());
+  non_native_modules_.erase(first_module_defunct_modules,
+                            non_native_modules_.end());
 
   // Insert the modules to be added. This operation is O((m + a) + a*log(a))
   // where m is the number of current modules and a is the number of modules to
   // be added.
-  non_native_modules_.insert(std::make_move_iterator(to_add.begin()),
-                             std::make_move_iterator(to_add.end()));
+  non_native_modules_.insert(std::make_move_iterator(new_modules.begin()),
+                             std::make_move_iterator(new_modules.end()));
 }
 
 void ModuleCache::AddCustomNativeModule(std::unique_ptr<const Module> module) {
diff --git a/base/profiler/module_cache.h b/base/profiler/module_cache.h
index 9bc601e..db0b2c0 100644
--- a/base/profiler/module_cache.h
+++ b/base/profiler/module_cache.h
@@ -84,14 +84,14 @@
   // GetModuleForAddress() will return the non-native module rather than the
   // native module for the memory region it occupies.
   //
-  // Modules in |to_remove| are removed from the set of active modules;
+  // Modules in |defunct_modules| are removed from the set of active modules;
   // specifically they no longer participate in the GetModuleForAddress()
   // lookup. They continue to exist for the lifetime of the ModuleCache,
   // however, so that existing references to them remain valid. Modules in
-  // |to_add| are added to the set of active non-native modules.
+  // |new_modules| are added to the set of active non-native modules.
   void UpdateNonNativeModules(
-      const std::vector<const Module*>& to_remove,
-      std::vector<std::unique_ptr<const Module>> to_add);
+      const std::vector<const Module*>& defunct_modules,
+      std::vector<std::unique_ptr<const Module>> new_modules);
 
   // Adds a custom native module to the cache. This is intended to support
   // native modules that require custom handling. In general, native modules
diff --git a/base/profiler/module_cache_unittest.cc b/base/profiler/module_cache_unittest.cc
index 919899e0..b6eb063f 100644
--- a/base/profiler/module_cache_unittest.cc
+++ b/base/profiler/module_cache_unittest.cc
@@ -226,6 +226,28 @@
   EXPECT_TRUE(was_destroyed);
 }
 
+// Regression test to validate that when modules are partitioned into modules to
+// keep and modules to remove, the modules to remove are not destroyed.
+// https://crbug.com/1127466 case 2.
+MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesPartitioning) {
+  int destroyed_count = 0;
+  const auto record_destroyed = [&destroyed_count]() { ++destroyed_count; };
+  {
+    ModuleCache cache;
+    std::vector<std::unique_ptr<const ModuleCache::Module>> modules;
+    modules.push_back(std::make_unique<FakeModule>(
+        1, 1, false, BindLambdaForTesting(record_destroyed)));
+    const ModuleCache::Module* module1 = modules.back().get();
+    modules.push_back(std::make_unique<FakeModule>(
+        2, 1, false, BindLambdaForTesting(record_destroyed)));
+    cache.UpdateNonNativeModules({}, std::move(modules));
+    cache.UpdateNonNativeModules({module1}, {});
+
+    EXPECT_EQ(0, destroyed_count);
+  }
+  EXPECT_EQ(2, destroyed_count);
+}
+
 MAYBE_TEST(ModuleCacheTest, UpdateNonNativeModulesReplace) {
   ModuleCache cache;
   // Replace a module with another larger module at the same base address.
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index 841fc38..b10707d9 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -30,7 +30,6 @@
 #include <type_traits>
 
 #include "base/base_export.h"
-#include "base/check.h"
 #include "base/check_op.h"
 #include "base/strings/char_traits.h"
 #include "base/strings/string16.h"
@@ -159,13 +158,25 @@
   // in a "const char*" or a "string" wherever a "StringPiece" is
   // expected (likewise for char16, string16, StringPiece16).
   constexpr BasicStringPiece() : ptr_(nullptr), length_(0) {}
+  // TODO(crbug.com/1049498): Construction from nullptr is not allowed for
+  // std::basic_string_view, so remove the special handling for it.
   // Note: This doesn't just use STRING_TYPE::traits_type::length(), since that
-  // isn't constexpr until C++17. Furthermore, this CHECKs `str`, since passing
-  // a nullptr to std::basic_string_view is undefined behavior. In order to
-  // ensure we don't pass nullptr to CharTraits::length we perform the CHECK
-  // during the initialization of `length_`.
+  // isn't constexpr until C++17.
   constexpr BasicStringPiece(const value_type* str)
-      : ptr_(str), length_((CHECK(str), CharTraits<value_type>::length(str))) {}
+      : ptr_(str), length_(!str ? 0 : CharTraits<value_type>::length(str)) {}
+  // Explicitly disallow construction from nullptr. Note that this does not
+  // catch construction from runtime strings that might be null.
+  // Note: The following is just a more elaborate way of spelling
+  // `BasicStringPiece(nullptr_t) = delete`, but unfortunately the terse form is
+  // not supported by the PNaCl toolchain.
+  // TODO(crbug.com/1049498): Remove once we CHECK(str) in the constructor
+  // above.
+  template <class T, class = std::enable_if_t<std::is_null_pointer<T>::value>>
+  BasicStringPiece(T) {
+    static_assert(sizeof(T) == 0,  // Always false.
+                  "StringPiece does not support construction from nullptr, use "
+                  "the default constructor instead.");
+  }
   BasicStringPiece(const STRING_TYPE& str)
       : ptr_(str.data()), length_(str.size()) {}
   constexpr BasicStringPiece(const value_type* offset, size_type len)
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc
index 506cff2..ee4e0cf 100644
--- a/base/strings/string_piece_unittest.cc
+++ b/base/strings/string_piece_unittest.cc
@@ -650,7 +650,6 @@
 }
 
 TEST(StringPieceTest, OutOfBoundsDeath) {
-  { ASSERT_DEATH_IF_SUPPORTED(StringPiece(nullptr), ""); }
   {
     constexpr StringPiece piece;
     ASSERT_DEATH_IF_SUPPORTED(piece[0], "");
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 84b5e35..c4071d9 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -783,7 +783,10 @@
       ret[index:index + 1] = expanded_configs
 
 
-def _DepsFromPaths(dep_paths, target_type, filter_root_targets=True):
+def _DepsFromPaths(dep_paths,
+                   target_type,
+                   filter_root_targets=True,
+                   recursive_resource_deps=False):
   """Resolves all groups and trims dependency branches that we never want.
 
   E.g. When a resource or asset depends on an apk target, the intent is to
@@ -806,6 +809,10 @@
   # Don't allow java libraries to cross through assets/resources.
   if target_type in _RESOURCE_TYPES:
     allowlist.extend(_RESOURCE_TYPES)
+    # Pretend that this target directly depends on all of its transitive
+    # dependencies.
+    if recursive_resource_deps:
+      dep_paths = GetAllDepsConfigsInOrder(dep_paths)
 
   return _DepsFromPathsWithFilters(dep_paths, blocklist, allowlist)
 
@@ -914,6 +921,10 @@
       action='store_true',
       help='Whether resources passed in via --resources-zip should override '
       'resources with the same name')
+  parser.add_option(
+      '--recursive-resource-deps',
+      action='store_true',
+      help='Whether deps should be walked recursively to find resource deps.')
 
   # android_assets options
   parser.add_option('--asset-sources', help='List of asset sources.')
@@ -1177,7 +1188,9 @@
   }
 
   deps_configs_paths = build_utils.ParseGnList(options.deps_configs)
-  deps = _DepsFromPaths(deps_configs_paths, options.type)
+  deps = _DepsFromPaths(deps_configs_paths,
+                        options.type,
+                        recursive_resource_deps=options.recursive_resource_deps)
   processor_deps = _DepsFromPaths(
       build_utils.ParseGnList(options.annotation_processor_configs or ''),
       options.type, filter_root_targets=False)
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py
index 9d3adcc..15d65ddc7 100644
--- a/build/android/pylib/gtest/gtest_test_instance.py
+++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import HTMLParser
+import json
 import logging
 import os
 import re
@@ -254,6 +255,29 @@
   return results
 
 
+def ParseGTestJSON(json_content):
+  """Parse results in the JSON Test Results format."""
+  results = []
+  if not json_content:
+    return results
+
+  json_data = json.loads(json_content)
+
+  openstack = json_data['tests'].items()
+
+  while openstack:
+    name, value = openstack.pop()
+
+    if 'expected' in value and 'actual' in value:
+      result_type = base_test_result.ResultType.PASS if value[
+          'actual'] == 'PASS' else base_test_result.ResultType.FAIL
+      results.append(base_test_result.BaseTestResult(name, result_type))
+    else:
+      openstack += [("%s.%s" % (name, k), v) for k, v in value.iteritems()]
+
+  return results
+
+
 def TestNameWithoutDisabledPrefix(test_name):
   """Modify the test name without disabled prefix if prefix 'DISABLED_' or
   'FLAKY_' presents.
@@ -281,6 +305,7 @@
     self._extract_test_list_from_filter = args.extract_test_list_from_filter
     self._filter_tests_lock = threading.Lock()
     self._gs_test_artifacts_bucket = args.gs_test_artifacts_bucket
+    self._isolated_script_test_output = args.isolated_script_test_output
     self._isolated_script_test_perf_output = (
         args.isolated_script_test_perf_output)
     self._shard_timeout = args.shard_timeout
@@ -428,6 +453,10 @@
     return self._gtest_filter
 
   @property
+  def isolated_script_test_output(self):
+    return self._isolated_script_test_output
+
+  @property
   def isolated_script_test_perf_output(self):
     return self._isolated_script_test_perf_output
 
diff --git a/build/android/pylib/gtest/gtest_test_instance_test.py b/build/android/pylib/gtest/gtest_test_instance_test.py
index b1a74dc..2a8c9b98 100755
--- a/build/android/pylib/gtest/gtest_test_instance_test.py
+++ b/build/android/pylib/gtest/gtest_test_instance_test.py
@@ -216,6 +216,48 @@
     actual = gtest_test_instance.ParseGTestXML(None)
     self.assertEquals([], actual)
 
+  def testParseGTestJSON_none(self):
+    actual = gtest_test_instance.ParseGTestJSON(None)
+    self.assertEquals([], actual)
+
+  def testParseGTestJSON_example(self):
+    raw_json = """
+      {
+        "tests": {
+          "mojom_tests": {
+            "parse": {
+              "ast_unittest": {
+                "ASTTest": {
+                  "testNodeBase": {
+                    "expected": "PASS",
+                    "actual": "PASS",
+                    "artifacts": {
+                      "screenshot": ["screenshots/page.png"]
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "interrupted": false,
+        "path_delimiter": ".",
+        "version": 3,
+        "seconds_since_epoch": 1406662283.764424,
+        "num_failures_by_type": {
+          "FAIL": 0,
+          "PASS": 1
+        },
+        "artifact_types": {
+          "screenshot": "image/png"
+        }
+      }"""
+    actual = gtest_test_instance.ParseGTestJSON(raw_json)
+    self.assertEquals(1, len(actual))
+    self.assertEquals('mojom_tests.parse.ast_unittest.ASTTest.testNodeBase',
+                      actual[0].GetName())
+    self.assertEquals(base_test_result.ResultType.PASS, actual[0].GetType())
+
   def testTestNameWithoutDisabledPrefix_disabled(self):
     test_name_list = [
       'A.DISABLED_B',
diff --git a/build/android/pylib/local/device/local_device_gtest_run.py b/build/android/pylib/local/device/local_device_gtest_run.py
index 3ca6042..b38ff12b 100644
--- a/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -621,10 +621,15 @@
       tombstones.ClearAllTombstones(device)
     test_perf_output_filename = next(self._test_perf_output_filenames)
 
+    if self._test_instance.isolated_script_test_output:
+      suffix = '.json'
+    else:
+      suffix = '.xml'
+
     with device_temp_file.DeviceTempFile(
         adb=device.adb,
         dir=self._delegate.ResultsDirectory(device),
-        suffix='.xml') as device_tmp_results_file:
+        suffix=suffix) as device_tmp_results_file:
       with contextlib_ext.Optional(
           device_temp_file.NamedDeviceTemporaryDirectory(
               adb=device.adb, dir='/sdcard/'),
@@ -641,9 +646,13 @@
           if self._test_instance.gs_test_artifacts_bucket:
             flags.append('--test_artifacts_dir=%s' % test_artifacts_dir.name)
 
+          if self._test_instance.isolated_script_test_output:
+            flags.append('--isolated-script-test-output=%s' %
+                         device_tmp_results_file.name)
+
           if test_perf_output_filename:
-            flags.append('--isolated_script_test_perf_output=%s'
-                         % isolated_script_test_perf_output.name)
+            flags.append('--isolated-script-test-perf-output=%s' %
+                         isolated_script_test_perf_output.name)
 
           logging.info('flags:')
           for f in flags:
@@ -658,24 +667,27 @@
 
           if self._test_instance.enable_xml_result_parsing:
             try:
-              gtest_xml = device.ReadFile(
-                  device_tmp_results_file.name,
-                  as_root=True)
-            except device_errors.CommandFailedError as e:
-              logging.warning(
-                  'Failed to pull gtest results XML file %s: %s',
-                  device_tmp_results_file.name,
-                  str(e))
+              gtest_xml = device.ReadFile(device_tmp_results_file.name)
+            except device_errors.CommandFailedError:
+              logging.exception('Failed to pull gtest results XML file %s',
+                                device_tmp_results_file.name)
               gtest_xml = None
 
+          if self._test_instance.isolated_script_test_output:
+            try:
+              gtest_json = device.ReadFile(device_tmp_results_file.name)
+            except device_errors.CommandFailedError:
+              logging.exception('Failed to pull gtest results JSON file %s',
+                                device_tmp_results_file.name)
+              gtest_json = None
+
           if test_perf_output_filename:
             try:
               device.PullFile(isolated_script_test_perf_output.name,
                               test_perf_output_filename)
-            except device_errors.CommandFailedError as e:
-              logging.warning(
-                  'Failed to pull chartjson results %s: %s',
-                  isolated_script_test_perf_output.name, str(e))
+            except device_errors.CommandFailedError:
+              logging.exception('Failed to pull chartjson results %s',
+                                isolated_script_test_perf_output.name)
 
           test_artifacts_url = self._UploadTestArtifacts(device,
                                                          test_artifacts_dir)
@@ -695,6 +707,8 @@
     # TODO(jbudorick): Transition test scripts away from parsing stdout.
     if self._test_instance.enable_xml_result_parsing:
       results = gtest_test_instance.ParseGTestXML(gtest_xml)
+    elif self._test_instance.isolated_script_test_output:
+      results = gtest_test_instance.ParseGTestJSON(gtest_json)
     else:
       results = gtest_test_instance.ParseGTestOutput(
           output, self._test_instance.symbolizer, device.product_cpu_abi)
diff --git a/build/android/pylib/results/json_results.py b/build/android/pylib/results/json_results.py
index 6a10ba4..38ede80e 100644
--- a/build/android/pylib/results/json_results.py
+++ b/build/android/pylib/results/json_results.py
@@ -6,6 +6,7 @@
 import itertools
 import json
 import logging
+import time
 
 from pylib.base import base_test_result
 
@@ -111,6 +112,58 @@
   }
 
 
+def GenerateJsonTestResultFormatDict(test_run_results):
+  """Create a results dict from |test_run_results| suitable for writing to JSON.
+
+  Args:
+    test_run_results: a list of base_test_result.TestRunResults objects.
+  Returns:
+    A results dict that mirrors the standard JSON Test Results Format.
+  """
+
+  tests = {}
+  pass_count = 0
+  fail_count = 0
+
+  for test_run_result in test_run_results:
+    if isinstance(test_run_result, list):
+      results_iterable = itertools.chain(*(t.GetAll() for t in test_run_result))
+    else:
+      results_iterable = test_run_result.GetAll()
+
+    for r in results_iterable:
+      element = tests
+      for key in r.GetName().split('.'):
+        if key not in element:
+          element[key] = {}
+        element = element[key]
+
+      element['expected'] = 'PASS'
+
+      if r.GetType() == base_test_result.ResultType.PASS:
+        element['actual'] = 'PASS'
+        pass_count += 1
+      else:
+        element['actual'] = 'FAIL'
+        fail_count += 1
+
+      if r.GetDuration() != 0:
+        element['time'] = r.GetDuration()
+
+  # Fill in required fields.
+  return {
+      'interrupted': False,
+      'num_failures_by_type': {
+          'FAIL': fail_count,
+          'PASS': pass_count,
+      },
+      'path_delimiter': '.',
+      'seconds_since_epoch': time.time(),
+      'tests': tests,
+      'version': 3,
+  }
+
+
 def GenerateJsonResultsFile(test_run_result, file_path, global_tags=None,
                             **kwargs):
   """Write |test_run_result| to JSON.
@@ -129,6 +182,21 @@
     logging.info('Generated json results file at %s', file_path)
 
 
+def GenerateJsonTestResultFormatFile(test_run_result, file_path, **kwargs):
+  """Write |test_run_result| to JSON.
+
+  This uses the official Chromium Test Results Format.
+
+  Args:
+    test_run_result: a base_test_result.TestRunResults object.
+    file_path: The path to the JSON file to write.
+  """
+  with open(file_path, 'w') as json_result_file:
+    json_result_file.write(
+        json.dumps(GenerateJsonTestResultFormatDict(test_run_result), **kwargs))
+    logging.info('Generated json results file at %s', file_path)
+
+
 def ParseResultsFromJson(json_results):
   """Creates a list of BaseTestResult objects from JSON.
 
diff --git a/build/android/pylib/results/json_results_test.py b/build/android/pylib/results/json_results_test.py
index 68e71f57..2c1a422 100755
--- a/build/android/pylib/results/json_results_test.py
+++ b/build/android/pylib/results/json_results_test.py
@@ -202,6 +202,40 @@
     self.assertTrue('output_snippet_base64' in test_iteration_result)
     self.assertEquals('', test_iteration_result['output_snippet_base64'])
 
+  def testGenerateJsonTestResultFormatDict_passedResult(self):
+    result = base_test_result.BaseTestResult('test.package.TestName',
+                                             base_test_result.ResultType.PASS)
+
+    all_results = base_test_result.TestRunResults()
+    all_results.AddResult(result)
+
+    results_dict = json_results.GenerateJsonTestResultFormatDict([all_results])
+    self.assertEquals(1, len(results_dict['tests']))
+    self.assertEquals(1, len(results_dict['tests']['test']))
+    self.assertEquals(1, len(results_dict['tests']['test']['package']))
+    self.assertEquals(
+        'PASS',
+        results_dict['tests']['test']['package']['TestName']['expected'])
+    self.assertEquals(
+        'PASS', results_dict['tests']['test']['package']['TestName']['actual'])
+
+  def testGenerateJsonTestResultFormatDict_failedResult(self):
+    result = base_test_result.BaseTestResult('test.package.TestName',
+                                             base_test_result.ResultType.FAIL)
+
+    all_results = base_test_result.TestRunResults()
+    all_results.AddResult(result)
+
+    results_dict = json_results.GenerateJsonTestResultFormatDict([all_results])
+    self.assertEquals(1, len(results_dict['tests']))
+    self.assertEquals(1, len(results_dict['tests']['test']))
+    self.assertEquals(1, len(results_dict['tests']['test']['package']))
+    self.assertEquals(
+        'PASS',
+        results_dict['tests']['test']['package']['TestName']['expected'])
+    self.assertEquals(
+        'FAIL', results_dict['tests']['test']['package']['TestName']['actual'])
+
 
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 6c5848c9..521afea 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -227,6 +227,12 @@
       dest='run_disabled', action='store_true',
       help='Also run disabled tests if applicable.')
 
+  # This is currently only implemented for gtests.
+  parser.add_argument('--isolated-script-test-output',
+                      help='If present, store test results on this path.')
+  parser.add_argument('--isolated-script-test-perf-output',
+                      help='If present, store chartjson results on this path.')
+
   AddTestLauncherOptions(parser)
 
 
@@ -349,9 +355,6 @@
       help='Host directory to which app data files will be'
            ' saved. Used with --app-data-file.')
   parser.add_argument(
-      '--isolated-script-test-perf-output',
-      help='If present, store chartjson results on this path.')
-  parser.add_argument(
       '--delete-stale-data',
       dest='delete_stale_data', action='store_true',
       help='Delete stale test data on the device.')
@@ -835,6 +838,8 @@
     finally:
       if args.json_results_file and os.path.exists(json_file.name):
         shutil.move(json_file.name, args.json_results_file)
+      elif args.isolated_script_test_output and os.path.exists(json_file.name):
+        shutil.move(json_file.name, args.isolated_script_test_output)
       else:
         os.remove(json_file.name)
 
@@ -846,10 +851,16 @@
       global_results_tags.add('UNRELIABLE_RESULTS')
       raise
     finally:
-      json_results.GenerateJsonResultsFile(
-          all_raw_results, json_file.name,
-          global_tags=list(global_results_tags),
-          indent=2)
+      if args.isolated_script_test_output:
+        json_results.GenerateJsonTestResultFormatFile(all_raw_results,
+                                                      json_file.name,
+                                                      indent=2)
+      else:
+        json_results.GenerateJsonResultsFile(
+            all_raw_results,
+            json_file.name,
+            global_tags=list(global_results_tags),
+            indent=2)
 
   @contextlib.contextmanager
   def upload_logcats_file():
@@ -953,7 +964,8 @@
                          str(tot_tests),
                          str(iteration_count))
 
-    if args.local_output or not local_utils.IsOnSwarming():
+    if (args.local_output or not local_utils.IsOnSwarming()
+        ) and not args.isolated_script_test_output:
       with out_manager.ArchivedTempfile(
           'test_results_presentation.html',
           'test_results_presentation',
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 3724ee59..5290e4ad 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -575,6 +575,10 @@
         invoker.version_code,
       ]
     }
+    if (defined(invoker.recursive_resource_deps) &&
+        invoker.recursive_resource_deps) {
+      args += [ "--recursive-resource-deps" ]
+    }
     if (current_toolchain != default_toolchain) {
       # This has to be a built-time error rather than a GN assert because many
       # packages have a mix of java and non-java targets. For example, the
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 8bf6bca8..85d76f8 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -897,6 +897,8 @@
   #     other.
   #   r_text_file: (optional) path to pre-generated R.txt to be used when
   #     generating R.java instead of resource-based aapt-generated one.
+  #   recursive_resource_deps: (optional) whether deps should be walked
+  #     recursively to find resource deps.
   #
   # Example:
   #   android_resources("foo_resources") {
@@ -982,6 +984,7 @@
                                "android_manifest_dep",
                                "custom_package",
                                "resource_overlay",
+                               "recursive_resource_deps",
                              ])
 
       # No package means resources override their deps.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index cfc02703..2dffbde 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200914.1.1
+0.20200914.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index cfc02703..19ffc48 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200914.1.1
+0.20200914.2.1
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index eb78104..a5641c1 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -596,11 +596,9 @@
   if (layer_tree_impl()->IsActiveTree())
     CleanUpTilingsOnActiveLayer(last_append_quads_tilings_);
 
-  const float old_ideal_contents_scale = ideal_contents_scale_;
   UpdateIdealScales();
 
-  const bool should_adjust_raster_scale =
-      ShouldAdjustRasterScale(old_ideal_contents_scale);
+  const bool should_adjust_raster_scale = ShouldAdjustRasterScale();
   if (should_adjust_raster_scale)
     RecalculateRasterScales();
   UpdateTilingsForRasterScaleAndTranslation(should_adjust_raster_scale);
@@ -1168,33 +1166,6 @@
   return adjusted_raster_scale;
 }
 
-// Log either the tile area saved or added due to directly compositing an
-// image. This is logged every time we choose a raster source scale for a
-// directly composited image.
-void PictureLayerImpl::LogDirectlyCompositedImageRasterScaleUMAs() const {
-  base::CheckedNumeric<int> actual_area =
-      ScaleToCeiledSize(bounds(), raster_source_scale_).GetCheckedArea();
-  base::CheckedNumeric<int> ideal_area =
-      ScaleToCeiledSize(bounds(), ideal_source_scale_).GetCheckedArea();
-  if (actual_area.IsValid() && ideal_area.IsValid()) {
-    int area_difference =
-        std::abs(static_cast<int>((actual_area - ideal_area).ValueOrDie()));
-    bool raster_area_matches = raster_source_scale_ == ideal_source_scale_;
-    UMA_HISTOGRAM_BOOLEAN(
-        "Compositing.Renderer.DirectlyCompositedImage.TileAreaMatches",
-        raster_area_matches);
-    if (raster_source_scale_ < ideal_source_scale_) {
-      UMA_HISTOGRAM_COUNTS_10M(
-          "Compositing.Renderer.DirectlyCompositedImage.TileAreaSaved",
-          area_difference);
-    } else if (raster_source_scale_ > ideal_source_scale_) {
-      UMA_HISTOGRAM_COUNTS_10M(
-          "Compositing.Renderer.DirectlyCompositedImage.TileAreaAdded",
-          area_difference);
-    }
-  }
-}
-
 PictureLayerTiling* PictureLayerImpl::AddTiling(
     const gfx::AxisTransform2d& raster_transform) {
   DCHECK(CanHaveTilings());
@@ -1295,8 +1266,7 @@
   SanityCheckTilingState();
 }
 
-bool PictureLayerImpl::ShouldAdjustRasterScale(
-    float old_ideal_contents_scale) const {
+bool PictureLayerImpl::ShouldAdjustRasterScale() const {
   if (!raster_contents_scale_)
     return true;
 
@@ -1324,23 +1294,8 @@
     // changed. We should recalculate in order to raster at the intrinsic image
     // size. Note that this is not a comparison of the used raster_source_scale_
     // and desired because of the adjustments in RecalculateRasterScales.
-    bool ideal_contents_scale_changed =
-        old_ideal_contents_scale != 0 &&
-        old_ideal_contents_scale != ideal_contents_scale_;
     bool default_raster_scale_changed =
         default_raster_scale != directly_composited_image_initial_raster_scale_;
-    if (ideal_contents_scale_changed && !default_raster_scale_changed) {
-      // Log a histogram to indicate that we most likely saved raster costs,
-      // if the ideal contents scale changed but we did not need to recalculate
-      // raster scales because this layer is a directly composited image.
-      bool transform_trigger =
-          draw_properties().screen_space_transform_is_animating ||
-          HasWillChangeTransformHint();
-      UMA_HISTOGRAM_BOOLEAN(
-          "Compositing.Renderer.DirectlyCompositedImage."
-          "AvoidRasterAdjustmentWithTransformTrigger",
-          transform_trigger);
-    }
     return default_raster_scale_changed;
   }
 
@@ -1431,14 +1386,7 @@
 void PictureLayerImpl::RecalculateRasterScales() {
   if (directly_composited_image_size_) {
     float used_raster_scale = CalculateDirectlyCompositedImageRasterScale();
-    const bool should_directly_composite =
-        ShouldDirectlyCompositeImage(used_raster_scale);
-
-    UMA_HISTOGRAM_BOOLEAN(
-        "Compositing.Renderer.DirectlyCompositedImage."
-        "RasterScaleDirectlyComposited",
-        should_directly_composite);
-    if (should_directly_composite) {
+    if (ShouldDirectlyCompositeImage(used_raster_scale)) {
       directly_composited_image_initial_raster_scale_ =
           GetDefaultDirectlyCompositedImageRasterScale();
       raster_source_scale_ = used_raster_scale;
@@ -1446,8 +1394,6 @@
       raster_device_scale_ = 1.f;
       raster_contents_scale_ = raster_source_scale_;
       low_res_raster_contents_scale_ = raster_contents_scale_;
-
-      LogDirectlyCompositedImageRasterScaleUMAs();
       return;
     }
 
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index cfa31c95..935281ee 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -175,7 +175,7 @@
       const PictureLayerTiling& high_res) const;
   void UpdateTilingsForRasterScaleAndTranslation(bool adjusted_raster_scale);
   void AddLowResolutionTilingIfNeeded();
-  bool ShouldAdjustRasterScale(float old_ideal_contents_scale) const;
+  bool ShouldAdjustRasterScale() const;
   void RecalculateRasterScales();
   // Returns false if raster translation is not applicable.
   bool CalculateRasterTranslation(gfx::Vector2dF& raster_translation) const;
@@ -199,7 +199,6 @@
   // factors, and bumps up the reduced scale if those layers end up increasing
   // their contents scale.
   float CalculateDirectlyCompositedImageRasterScale() const;
-  void LogDirectlyCompositedImageRasterScaleUMAs() const;
 
   void SanityCheckTilingState() const;
 
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 3c10b66..b59d5c98 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -14,7 +14,6 @@
 
 #include "base/location.h"
 #include "base/stl_util.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "cc/animation/animation_host.h"
 #include "cc/base/math_util.h"
@@ -5640,210 +5639,6 @@
                                ->contents_scale_key());
 }
 
-TEST_F(LegacySWPictureLayerImplTest, CompositedImageHistograms) {
-  base::HistogramTester histogram_tester;
-
-  gfx::Size layer_bounds(5, 5);
-  scoped_refptr<FakeRasterSource> pending_raster_source =
-      FakeRasterSource::CreateFilled(layer_bounds);
-
-  SetupPendingTree(pending_raster_source);
-
-  // Set the image and bounds to values that make the layer not eligible for
-  // direct compositing. This must be reflected by a |contents_scale_key()| of
-  // 0.2f (matching the ideal source scale).
-  gfx::Size image_size(300, 300);
-  pending_layer()->SetDirectlyCompositedImageSize(image_size);
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.2f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  EXPECT_FLOAT_EQ(0.2f, pending_layer()
-                            ->picture_layer_tiling_set()
-                            ->FindTilingWithResolution(HIGH_RESOLUTION)
-                            ->contents_scale_key());
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      false, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      true, 0);
-
-  // At ideal scale of 1, we save 270000 pixels for a 600x600 layer directly
-  // compositing a 300x300 image.
-  pending_layer()->SetBounds(gfx::Size(600, 600));
-  pending_layer()->SetDirectlyCompositedImageSize(image_size);
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaSaved",
-      270000, 1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaSaved",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaAdded",
-      0);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      true, 0);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      false, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      true, 1);
-
-  // Matching bounds should log TileAreaMatches.
-  pending_layer()->SetDirectlyCompositedImageSize(gfx::Size(600, 600));
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      true, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      false, 1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaSaved",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaAdded",
-      0);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      true, 2);
-
-  // Changing the bounds to be smaller than the image should add a TileAreaAdded
-  // histogram bucket count.
-  pending_layer()->SetBounds(gfx::Size(300, 300));
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaAdded",
-      270000, 1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaSaved",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaAdded",
-      1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      true, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      false, 2);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      true, 3);
-
-  // So far we haven't avoided any re-raster (as sizes have been updated at
-  // every stage of the test). Similarly, just updating tiles with no scale
-  // changes should not result in those histograms being logged.
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      false, 0);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      true, 0);
-
-  // Reduce the ideal scale - directly composited image should not re-raster.
-  // Neither will-change:transform or active transformation so the 'false'
-  // bucket should get an entry.
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.5f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      false, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      true, 0);
-
-  // Set will-change:transform and update tiles with a different ideal scale.
-  GetTransformNode(pending_layer())->will_change_transform = true;
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 0.6f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      false, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "AvoidRasterAdjustmentWithTransformTrigger",
-      true, 1);
-
-  // None of the operations past the first one should have incremented this
-  // histogram.
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      false, 1);
-}
-
-TEST_F(LegacySWPictureLayerImplTest, CompositedImageHistogramsOverflow) {
-  base::HistogramTester histogram_tester;
-
-  constexpr int kLargeDimension = std::numeric_limits<int>::max() / 2;
-  gfx::Size layer_bounds(kLargeDimension, kLargeDimension);
-  scoped_refptr<FakeRasterSource> pending_raster_source =
-      FakeRasterSource::CreateFilled(layer_bounds);
-
-  // Overflow the area calculation for the histograms - we should still get
-  // the correct high res scale key, but no histograms should be logged.
-  SetupPendingTree(pending_raster_source);
-  pending_layer()->SetDirectlyCompositedImageSize(layer_bounds);
-  SetupDrawPropertiesAndUpdateTiles(pending_layer(), 1.f, 1.f, 1.f, 1.f, 1.f,
-                                    false);
-  EXPECT_FLOAT_EQ(1.f, pending_layer()
-                           ->picture_layer_tiling_set()
-                           ->FindTilingWithResolution(HIGH_RESOLUTION)
-                           ->contents_scale_key());
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaSaved",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaAdded",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "TileAreaMatches",
-      0);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      true, 1);
-  histogram_tester.ExpectBucketCount(
-      "Compositing.Renderer.DirectlyCompositedImage."
-      "RasterScaleDirectlyComposited",
-      false, 0);
-}
-
 TEST_F(LegacySWPictureLayerImplTest,
        ChangeRasterTranslationNukePendingLayerTiles) {
   gfx::Size layer_bounds(200, 200);
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 8335921e..7185f52 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -849,12 +849,19 @@
                    FrameTerminationStatus::kDidNotProduceFrame) {
           state = ChromeFrameReporter::STATE_NO_UPDATE_DESIRED;
         } else {
-          state = ChromeFrameReporter::STATE_PRESENTED_ALL;
+          state = has_partial_update()
+                      ? ChromeFrameReporter::STATE_PRESENTED_PARTIAL
+                      : ChromeFrameReporter::STATE_PRESENTED_ALL;
         }
         auto* reporter = context.event()->set_chrome_frame_reporter();
         reporter->set_state(state);
         reporter->set_frame_source(args_.frame_id.source_id);
         reporter->set_frame_sequence(args_.frame_id.sequence_number);
+        if (IsDroppedFrameAffectingSmoothness()) {
+          DCHECK(state == ChromeFrameReporter::STATE_DROPPED ||
+                 state == ChromeFrameReporter::STATE_PRESENTED_PARTIAL);
+          reporter->set_affects_smoothness(true);
+        }
         // TODO(crbug.com/1086974): Set 'drop reason' if applicable.
       });
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 77fb3c0..ec3c961 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=87
 MINOR=0
-BUILD=4264
+BUILD=4265
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d0aa1fa0..597ba0d 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -465,7 +465,6 @@
     "//third_party/android_deps:androidx_customview_customview_java",
     "//third_party/android_deps:androidx_gridlayout_gridlayout_java",
     "//third_party/android_deps:androidx_interpolator_interpolator_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_v13_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java8_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_runtime_java",
@@ -3119,12 +3118,6 @@
     "java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java",
     "java/src/org/chromium/chrome/browser/screenshot/EditorScreenshotTask.java",
     "java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfModelObserverBridge.java",
-    "java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java",
     "java/src/org/chromium/chrome/browser/sharing/SharingJNIBridge.java",
     "java/src/org/chromium/chrome/browser/sharing/SharingServiceProxy.java",
     "java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 55ccab9f..a12cad4c 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -762,6 +762,7 @@
   "java/res/drawable/tile_view_highlight_plain.xml",
   "java/res/drawable/tile_view_icon_background_modern.xml",
   "java/res/drawable/toolbar_shadow.xml",
+  "java/res/drawable/trending_up_black_24dp.xml",
   "java/res/drawable/visa_card.xml",
   "java/res/font/google_sans.xml",
   "java/res/font/google_sans_medium.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index dff36488..0d6e529 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -351,6 +351,7 @@
   "java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java",
   "java/src/org/chromium/chrome/browser/contextmenu/LensAsyncManager.java",
   "java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuChipController.java",
   "java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java",
@@ -1342,19 +1343,7 @@
   "java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetAdapter.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManager.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinator.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBarController.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfModelObserverBridge.java",
   "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java",
-  "java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java",
   "java/src/org/chromium/chrome/browser/services/AccountsChangedReceiver.java",
   "java/src/org/chromium/chrome/browser/services/AndroidChildAccountHelper.java",
   "java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 2fd2ca1c..f15b0ac 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -202,8 +202,6 @@
   "junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapterTest.java",
-  "junit/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManagerTest.java",
-  "junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java",
   "junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java",
   "junit/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandlerTest.java",
   "junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 8c0e91f..d3514a68 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -104,6 +104,7 @@
   "javatests/src/org/chromium/chrome/browser/compositor/layouts/MockResourcesForLayout.java",
   "javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java",
   "javatests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java",
+  "javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuChipControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderViewTest.java",
   "javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuItemViewTest.java",
@@ -483,7 +484,6 @@
   "javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java",
-  "javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinatorTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java",
   "javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/shape_detection/ShapeDetectionTest.java",
diff --git a/chrome/android/expectations/lint-suppressions.xml b/chrome/android/expectations/lint-suppressions.xml
index 287b445..0190b8b 100644
--- a/chrome/android/expectations/lint-suppressions.xml
+++ b/chrome/android/expectations/lint-suppressions.xml
@@ -273,15 +273,11 @@
     <!-- crbug.com/1076538 remove this line and the following two lines after the bug is resolved -->
     <ignore regexp="The resource `R.string.accessibility_tab_suggestion_group_tabs_message` appears to be unused"/>
     <ignore regexp="The resource `R.string.tab_suggestion_group_tabs_message` appears to be unused"/>
-    <!-- crbug.com/1114311 remove this line and the following two lines after the bug is resolved -->
-    <ignore regexp="The resource `R.string.languages_set_application_language_prompt` appears to be unused"/>
-    <ignore regexp="The resource `R.string.languages_set_as_application_language` appears to be unused"/>
     <!-- crbug.com/1111942 remove this line and following 7 lines after the bug is resolved -->
     <ignore regexp="The resource `R.string.accessibility_tab_switcher` appears to be unused"/>
     <ignore regexp="The resource `R.string.accessibility_close_tab_group_button` appears to be unused"/>
     <ignore regexp="The resource `R.string.accessibility_close_tab_group_button_with_group_name` appears to be unused"/>
     <ignore regexp="The resource `R.string.accessibility_expand_tab_group_with_group_name` appears to be unused"/>
-    <ignore regexp="The resource `R.string.tab_grid_dialog_toolbar_edit_group_name` appears to be unused"/>
     <!-- Old-style and new-style WebAPKs use same resources for simplicity. Old-style WebAPKs do
          not use R.style.SplashTheme but new-style WebAPKs do.
          TODO(crbug.com/971254): Remove suppression once old-style WebAPKs are deprecated. -->
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 70ff913..00635667e 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -61,8 +61,6 @@
     "//third_party/android_deps:androidx_coordinatorlayout_coordinatorlayout_java",
     "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_gridlayout_gridlayout_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_ui_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_runtime_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index c9185fe2..188e7a86 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -210,7 +210,6 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_core_core_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_v13_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_runtime_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_viewmodel_java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index cb1656da..fac19dcd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -227,8 +227,10 @@
 
         // Setup ScrimView click Runnable.
         mScrimClickRunnable = () -> {
-            mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
-            mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, false);
+            if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
+                mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, false);
+            }
             hideDialog(true);
             RecordUserAction.record("TabGridDialog.Exit");
         };
@@ -247,7 +249,9 @@
 
         mToolbarMenuCallback = result -> {
             if (result == R.id.ungroup_tab) {
-                mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
+                if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                    mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
+                }
                 mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, false);
                 List<Tab> tabs = getRelatedTabs(mCurrentTabId);
                 if (mTabSelectionEditorController != null) {
@@ -279,6 +283,12 @@
                                                               .build();
                 mShareDelegateSupplier.get().share(shareParams, chromeShareExtras);
             }
+
+            if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                if (result == R.id.edit_group_name) {
+                    mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, true);
+                }
+            }
         };
 
         // Setup toolbar button click listeners.
@@ -308,6 +318,9 @@
             mTabSelectionEditorController.hide();
         }
         saveCurrentGroupModifiedTitle();
+        if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+            mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, false);
+        }
         mDialogController.resetWithListOfTabs(null);
     }
 
@@ -431,8 +444,12 @@
     private void setupToolbarEditText() {
         mKeyboardVisibilityListener = isShowing -> {
             mModel.set(TabGridPanelProperties.TITLE_CURSOR_VISIBILITY, isShowing);
-            mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, isShowing);
-            mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, isShowing);
+            if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, isShowing);
+                mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, isShowing);
+            } else if (TabUiFeatureUtilities.isLaunchPolishEnabled() && !isShowing) {
+                mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, false);
+            }
             if (!isShowing) {
                 saveCurrentGroupModifiedTitle();
             }
@@ -455,14 +472,20 @@
         };
         mModel.set(TabGridPanelProperties.TITLE_TEXT_WATCHER, textWatcher);
 
-        View.OnFocusChangeListener onFocusChangeListener =
-                (v, hasFocus) -> mIsUpdatingTitle = hasFocus;
+        View.OnFocusChangeListener onFocusChangeListener = (v, hasFocus) -> {
+            mIsUpdatingTitle = hasFocus;
+            if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) return;
+            mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, hasFocus);
+            mModel.set(TabGridPanelProperties.IS_TITLE_TEXT_FOCUSED, hasFocus);
+        };
         mModel.set(TabGridPanelProperties.TITLE_TEXT_ON_FOCUS_LISTENER, onFocusChangeListener);
     }
 
     private View.OnClickListener getCollapseButtonClickListener() {
         return view -> {
-            mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
+            if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                mModel.set(TabGridPanelProperties.IS_KEYBOARD_VISIBLE, false);
+            }
             hideDialog(true);
             RecordUserAction.record("TabGridDialog.Exit");
         };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
index a5ad8c3..e78cb15 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
@@ -149,6 +149,11 @@
         itemList.add(new ListItem(ListItemType.MENU_ITEM,
                 buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_share_group,
                         R.id.share_tab_group)));
+        if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+            itemList.add(new ListItem(ListItemType.MENU_ITEM,
+                    buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_edit_group_name,
+                            R.id.edit_group_name)));
+        }
         return itemList;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
index 273d1f0..f35c9fd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
@@ -131,11 +131,19 @@
         } else if (TITLE_CURSOR_VISIBILITY == propertyKey) {
             viewHolder.toolbarView.setTitleCursorVisibility(model.get(TITLE_CURSOR_VISIBILITY));
         } else if (IS_TITLE_TEXT_FOCUSED == propertyKey) {
+            if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                viewHolder.toolbarView.updateTitleTextFocus(model.get(IS_TITLE_TEXT_FOCUSED));
+                return;
+            }
             // Don't explicitly request focus since it should happen automatically.
             if (!model.get(IS_TITLE_TEXT_FOCUSED)) {
                 viewHolder.toolbarView.clearTitleTextFocus();
             }
         } else if (IS_KEYBOARD_VISIBLE == propertyKey) {
+            if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                viewHolder.toolbarView.updateKeyboardVisibility(model.get(IS_KEYBOARD_VISIBLE));
+                return;
+            }
             // Don't explicitly show keyboard since it should happen automatically.
             if (!model.get(IS_KEYBOARD_VISIBLE)) {
                 viewHolder.toolbarView.hideKeyboard();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index a5564c1d..cc3f603 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -80,6 +80,40 @@
         mTitleTextView.setCursorVisible(isVisible);
     }
 
+    void updateTitleTextFocus(boolean shouldFocus) {
+        if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) return;
+        if (mTitleTextView.isFocused() == shouldFocus) return;
+        if (shouldFocus) {
+            mTitleTextView.requestFocus();
+        } else {
+            clearTitleTextFocus();
+        }
+    }
+
+    void updateKeyboardVisibility(boolean shouldShow) {
+        if (!TabUiFeatureUtilities.isLaunchPolishEnabled()) return;
+        // This is equal to the animation duration of toolbar menu hiding.
+        int showKeyboardDelay = 150;
+        if (shouldShow) {
+            // TODO(crbug.com/1116644) Figure out why a call to show keyboard without delay still
+            // won't work when the window gets focus in onWindowFocusChanged call.
+            // Wait until the current window has focus to show the keyboard. This is to deal with
+            // the case where the keyboard showing is caused by toolbar menu. In this case, we need
+            // to wait for the menu window to hide and current window to gain focus so that we can
+            // show the keyboard.
+            KeyboardVisibilityDelegate delegate = KeyboardVisibilityDelegate.getInstance();
+            postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    assert hasWindowFocus();
+                    delegate.showKeyboard(mTitleTextView);
+                }
+            }, showKeyboardDelay);
+        } else {
+            hideKeyboard();
+        }
+    }
+
     void clearTitleTextFocus() {
         mTitleTextView.clearFocus();
     }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index b2b21b1..922e1497 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -625,6 +625,37 @@
 
     @Test
     @MediumTest
+    // clang-format off
+    @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID,
+            ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
+    @CommandLineFlags.Add({"force-fieldtrials=Study/Group", TAB_GROUP_LAUNCH_POLISH_PARAMS})
+    public void testTabGroupNaming_KeyboardVisibility() throws ExecutionException {
+        // clang-format on
+        final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        createTabs(cta, false, 2);
+        enterTabSwitcher(cta);
+        verifyTabSwitcherCardCount(cta, 2);
+
+        // Create a tab group.
+        mergeAllNormalTabsToAGroup(cta);
+        verifyTabSwitcherCardCount(cta, 1);
+        openDialogFromTabSwitcherAndVerify(cta, 2,
+                cta.getResources().getQuantityString(
+                        R.plurals.bottom_tab_grid_title_placeholder, 2, 2));
+
+        // Test title text focus in dialog in tab switcher.
+        testTitleTextFocus(cta);
+
+        // Test title text focus in dialog from tab strip.
+        openDialogFromTabSwitcherAndVerify(cta, 2, null);
+        clickFirstTabInDialog(cta);
+        waitForDialogHidingAnimation(cta);
+        openDialogFromStripAndVerify(cta, 2, null);
+        testTitleTextFocus(cta);
+    }
+
+    @Test
+    @MediumTest
     @DisableIf.Build(supported_abis_includes = "x86", message = "https://crbug.com/1124336")
     public void testDialogInitialShowFromStrip() throws Exception {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
@@ -1035,6 +1066,11 @@
                     // Verify if we can grab focus on the editText or not.
                     assertEquals(isEnabled, v.isFocused());
                 });
+        // Verify if the keyboard shows or not.
+        CriteriaHelper.pollUiThread(()
+                                            -> isEnabled
+                        == KeyboardVisibilityDelegate.getInstance().isKeyboardShowing(
+                                cta, cta.getCompositorViewHolder()));
     }
 
     private void openDialogToolbarMenuAndVerify(ChromeTabbedActivity cta) {
@@ -1046,11 +1082,17 @@
                     if (noMatchException != null) throw noMatchException;
                     Assert.assertTrue(v instanceof ListView);
                     ListView listView = (ListView) v;
-                    assertEquals(2, listView.getCount());
                     verifyTabGridDialogToolbarMenuItem(listView, 0,
                             cta.getString(R.string.tab_grid_dialog_toolbar_remove_from_group));
                     verifyTabGridDialogToolbarMenuItem(listView, 1,
                             cta.getString(R.string.tab_grid_dialog_toolbar_share_group));
+                    if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
+                        assertEquals(3, listView.getCount());
+                        verifyTabGridDialogToolbarMenuItem(listView, 2,
+                                cta.getString(R.string.tab_grid_dialog_toolbar_edit_group_name));
+                    } else {
+                        assertEquals(2, listView.getCount());
+                    }
                 });
     }
 
@@ -1218,4 +1260,39 @@
                        isDescendantOfA(withId(R.id.dialog_container_view))))
                 .check((v, e) -> assertEquals(s, v.getContentDescription()));
     }
+
+    private void testTitleTextFocus(ChromeTabbedActivity cta) throws ExecutionException {
+        // Click the text field to grab focus and click back button to lose focus.
+        onView(allOf(withParent(withId(R.id.main_content)), withId(R.id.title))).perform(click());
+        verifyTitleTextFocus(cta, true);
+        Espresso.pressBack();
+        verifyTitleTextFocus(cta, false);
+        verifyShowingDialog(cta, 2, null);
+
+        // Use toolbar menu to grab focus and click back button to lose focus.
+        openDialogToolbarMenuAndVerify(cta);
+        selectTabGridDialogToolbarMenuItem(cta, "Edit group name");
+        verifyTitleTextFocus(cta, true);
+        Espresso.pressBack();
+        verifyTitleTextFocus(cta, false);
+        verifyShowingDialog(cta, 2, null);
+
+        // Click the text field to grab focus and click scrim to lose focus.
+        onView(allOf(withParent(withId(R.id.main_content)), withId(R.id.title))).perform(click());
+        verifyTitleTextFocus(cta, true);
+        clickScrimToExitDialog(cta);
+        waitForDialogHidingAnimation(cta);
+        verifyTitleTextFocus(cta, false);
+    }
+
+    private void verifyTitleTextFocus(ChromeTabbedActivity cta, boolean shouldFocus) {
+        CriteriaHelper.pollUiThread(() -> {
+            View titleTextView = cta.findViewById(R.id.tab_group_toolbar).findViewById(R.id.title);
+            KeyboardVisibilityDelegate delegate = KeyboardVisibilityDelegate.getInstance();
+            boolean keyboardVisible =
+                    delegate.isKeyboardShowing(cta, cta.getCompositorViewHolder());
+            boolean isFocused = titleTextView.isFocused();
+            return (!shouldFocus ^ isFocused) && (!shouldFocus ^ keyboardVisible);
+        });
+    }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java
index 95e8248..936f815 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedImageFetchClient.java
@@ -36,9 +36,21 @@
     }
 
     @Override
-    public void sendRequest(String url, ImageFetchClient.HttpResponseConsumer responseConsumer) {
+    public int sendCancelableRequest(
+            String url, ImageFetchClient.HttpResponseConsumer responseConsumer) {
         assert ThreadUtils.runningOnUiThread();
-        FeedImageFetchClientJni.get().sendRequest(url, responseConsumer);
+        return FeedImageFetchClientJni.get().sendRequest(url, responseConsumer);
+    }
+
+    @Override
+    public void sendRequest(String url, ImageFetchClient.HttpResponseConsumer responseConsumer) {
+        sendCancelableRequest(url, responseConsumer);
+    }
+
+    @Override
+    public void cancel(int requestId) {
+        assert ThreadUtils.runningOnUiThread();
+        FeedImageFetchClientJni.get().cancel(requestId);
     }
 
     @CalledByNative
@@ -49,6 +61,7 @@
 
     @NativeMethods
     interface Natives {
-        void sendRequest(String url, ImageFetchClient.HttpResponseConsumer responseConsumer);
+        int sendRequest(String url, ImageFetchClient.HttpResponseConsumer responseConsumer);
+        void cancel(int requestId);
     }
 }
diff --git a/chrome/android/java/res/drawable/trending_up_black_24dp.xml b/chrome/android/java/res/drawable/trending_up_black_24dp.xml
new file mode 100644
index 0000000..b20c66c2
--- /dev/null
+++ b/chrome/android/java/res/drawable/trending_up_black_24dp.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        tools:targetApi="21"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M16,6v2h2.58l-5.17,5.17 -4,-4L2,16.59 3.41,18l6,-6 4,4L20,9.42V12h2V6z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/chrome/android/java/res/layout/share_sheet_content.xml b/chrome/android/java/res/layout/share_sheet_content.xml
index 20aafc0..c316ac2 100644
--- a/chrome/android/java/res/layout/share_sheet_content.xml
+++ b/chrome/android/java/res/layout/share_sheet_content.xml
@@ -45,32 +45,30 @@
       <TextView
         android:id="@+id/title_preview"
         android:ellipsize="end"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_toEndOf="@id/image_preview"
         android:maxLines="1"
         android:minHeight="18dp"
         android:paddingEnd="16dp"
         android:paddingStart="12dp"
-        android:gravity="center_vertical"
-        android:textAlignment="viewStart"
-        android:textAppearance="@style/TextAppearance.TextMediumThick.Primary"/>
+        android:paddingBottom="4dp"
+        android:textAlignment="viewStart"/>
 
       <TextView
-        android:id="@+id/url_preview"
+        android:id="@+id/subtitle_preview"
         android:ellipsize="end"
         android:layout_below="@id/title_preview"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_toEndOf="@id/image_preview"
+        android:layout_centerVertical="true"
         android:maxLines="1"
         android:minHeight="18dp"
         android:paddingEnd="16dp"
         android:paddingStart="12dp"
-        android:paddingTop="4dp"
-        android:gravity="center_vertical"
         android:textAlignment="viewStart"
-        android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
+        android:textAppearance="@style/TextAppearance.TextMedium.Primary"/>
   </RelativeLayout>
 
   <View
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 8e4cb23..1831bdb5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -47,10 +47,10 @@
 import org.chromium.chrome.browser.xsurface.ProcessScopeDependencyProvider;
 import org.chromium.components.browser_ui.widget.FeatureHighlightProvider;
 import org.chromium.components.external_intents.AuthenticatorNavigationInterceptor;
+import org.chromium.components.policy.AppRestrictionsProvider;
+import org.chromium.components.policy.CombinedPolicyProvider;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.SystemAccountManagerDelegate;
-import org.chromium.policy.AppRestrictionsProvider;
-import org.chromium.policy.CombinedPolicyProvider;
 
 import java.util.Collections;
 import java.util.List;
@@ -262,6 +262,13 @@
                 new AppRestrictionsProvider(ContextUtils.getApplicationContext()));
     }
 
+    // TODO(zmin): Delete once the internal code is migrated.
+    public void registerPolicyProviders(
+            org.chromium.policy.CombinedPolicyProvider combinedProvider) {
+        combinedProvider.registerProvider(
+                new AppRestrictionsProvider(ContextUtils.getApplicationContext()));
+    }
+
     /**
      * TODO(crbug.com/1102812) : Remove this method after updating the downstream to use the new
      * method {@link getOfflinePagesCctAllowlist} instead.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 3cb5daf..dce76474 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -179,14 +179,14 @@
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.page_info.PageInfoController;
+import org.chromium.components.policy.CombinedPolicyProvider;
+import org.chromium.components.policy.CombinedPolicyProvider.PolicyChangeListener;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.webapk.lib.client.WebApkValidator;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.common.ContentSwitches;
-import org.chromium.policy.CombinedPolicyProvider;
-import org.chromium.policy.CombinedPolicyProvider.PolicyChangeListener;
 import org.chromium.printing.PrintManagerDelegateImpl;
 import org.chromium.printing.PrintingController;
 import org.chromium.printing.PrintingControllerImpl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/send_tab_to_self/SendTabToSelfNotificationReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/app/send_tab_to_self/SendTabToSelfNotificationReceiver.java
index ec9198c..96f0fa7b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/send_tab_to_self/SendTabToSelfNotificationReceiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/send_tab_to_self/SendTabToSelfNotificationReceiver.java
@@ -12,7 +12,7 @@
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
-import org.chromium.chrome.browser.send_tab_to_self.NotificationManager;
+import org.chromium.chrome.browser.share.send_tab_to_self.NotificationManager;
 
 /** Handles changes to notifications based on user action or timeout. */
 public class SendTabToSelfNotificationReceiver extends BroadcastReceiver {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
index 241c10c..0cb83b1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -695,7 +695,10 @@
         } else if (itemId == R.id.contextmenu_share_link) {
             recordContextMenuSelection(params, ContextMenuUma.Action.SHARE_LINK);
             ShareParams linkShareParams =
-                    new ShareParams.Builder(getWindow(), params.getUrl(), params.getUrl()).build();
+                    new ShareParams
+                            .Builder(
+                                    getWindow(), ContextMenuUtils.getTitle(params), params.getUrl())
+                            .build();
             mShareDelegateSupplier.get().share(
                     linkShareParams, new ChromeShareExtras.Builder().setSaveLastUsed(true).build());
         } else if (itemId == R.id.contextmenu_search_with_google_lens) {
@@ -735,7 +738,7 @@
                     ChromePreferenceKeys.CONTEXT_MENU_SEARCH_SIMILAR_PRODUCTS_CLICKED, true);
         } else if (itemId == R.id.contextmenu_share_image) {
             recordContextMenuSelection(params, ContextMenuUma.Action.SHARE_IMAGE);
-            shareImage(renderFrameHost, params.getSrcUrl());
+            shareImage(renderFrameHost, params);
         } else if (itemId == R.id.contextmenu_open_in_chrome) {
             recordContextMenuSelection(params, ContextMenuUma.Action.OPEN_IN_CHROME);
             mDelegate.onOpenInChrome(params.getUrl(), params.getPageUrl());
@@ -820,9 +823,9 @@
      * Package-private, allowing access only from the context menu item to ensure that
      * it will use the right activity set when the menu was displayed.
      * @param renderFrameHost {@link RenderFrameHost} to get the encoded images from.
-     * @param srcUrl url of the image.
+     * @param params The {@link ContextMenuParams} for the image.
      */
-    private void shareImage(RenderFrameHost renderFrameHost, String srcUrl) {
+    private void shareImage(RenderFrameHost renderFrameHost, ContextMenuParams params) {
         retrieveImage(renderFrameHost, ContextMenuImageFormat.ORIGINAL, (Uri imageUri) -> {
             if (!mShareDelegateSupplier.get().isSharingHubV15Enabled()) {
                 ShareHelper.shareImage(getWindow(), null, imageUri);
@@ -831,14 +834,15 @@
             ContentResolver contentResolver =
                     ContextUtils.getApplicationContext().getContentResolver();
             ShareParams imageShareParams =
-                    new ShareParams.Builder(getWindow(), /*title=*/"", /*url=*/"")
+                    new ShareParams
+                            .Builder(getWindow(), ContextMenuUtils.getTitle(params), /*url=*/"")
                             .setFileUris(new ArrayList<>(Collections.singletonList(imageUri)))
                             .setFileContentType(contentResolver.getType(imageUri))
                             .build();
             mShareDelegateSupplier.get().share(imageShareParams,
                     new ChromeShareExtras.Builder()
                             .setSaveLastUsed(true)
-                            .setImageSrcUrl(srcUrl)
+                            .setImageSrcUrl(params.getSrcUrl())
                             .build());
         });
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java
new file mode 100644
index 0000000..ac20ff0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.contextmenu;
+
+import android.text.TextUtils;
+import android.webkit.URLUtil;
+
+import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
+
+/**
+ * Provides utility methods for generating context menus.
+ */
+public final class ContextMenuUtils {
+    private ContextMenuUtils() {}
+
+    /**
+     * Returns the title for the given {@link ContextMenuParams}.
+     */
+    static String getTitle(ContextMenuParams params) {
+        if (!TextUtils.isEmpty(params.getTitleText())) {
+            return params.getTitleText();
+        }
+        if (!TextUtils.isEmpty(params.getLinkText())) {
+            return params.getLinkText();
+        }
+        if (params.isImage() || params.isVideo() || params.isFile()) {
+            return URLUtil.guessFileName(params.getSrcUrl(), null, null);
+        }
+        return "";
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java
index 9df3f26..b9c61472 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java
@@ -5,11 +5,9 @@
 package org.chromium.chrome.browser.contextmenu;
 
 import android.app.Activity;
-import android.content.Context;
 import android.graphics.Bitmap;
 import android.text.SpannableString;
 import android.text.TextUtils;
-import android.webkit.URLUtil;
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
@@ -26,12 +24,9 @@
     private PropertyModel mModel;
     private RevampedContextMenuHeaderMediator mMediator;
 
-    private Context mContext;
-
     RevampedContextMenuHeaderCoordinator(Activity activity, @PerformanceClass int performanceClass,
             ContextMenuParams params, Profile profile) {
-        mContext = activity;
-        mModel = buildModel(getTitle(params), getUrl(activity, params, profile));
+        mModel = buildModel(ContextMenuUtils.getTitle(params), getUrl(activity, params, profile));
         mMediator = new RevampedContextMenuHeaderMediator(
                 activity, mModel, performanceClass, params, profile);
     }
@@ -51,19 +46,6 @@
                 .build();
     }
 
-    private String getTitle(ContextMenuParams params) {
-        if (!TextUtils.isEmpty(params.getTitleText())) {
-            return params.getTitleText();
-        }
-        if (!TextUtils.isEmpty(params.getLinkText())) {
-            return params.getLinkText();
-        }
-        if (params.isImage() || params.isVideo() || params.isFile()) {
-            return URLUtil.guessFileName(params.getSrcUrl(), null, null);
-        }
-        return "";
-    }
-
     private CharSequence getUrl(Activity activity, ContextMenuParams params, Profile profile) {
         CharSequence url = params.getUrl();
         if (!TextUtils.isEmpty(url)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfo.java
index c73583d..040162a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfo.java
@@ -19,8 +19,8 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.TaskTraits;
-import org.chromium.policy.AppRestrictionsProvider;
-import org.chromium.policy.PolicySwitches;
+import org.chromium.components.policy.AppRestrictionsProvider;
+import org.chromium.components.policy.PolicySwitches;
 
 import java.util.LinkedList;
 import java.util.Locale;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java
index e66dab7f..35c3c3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.policy.EnterpriseInfo;
 import org.chromium.chrome.browser.policy.PolicyServiceFactory;
 import org.chromium.components.browser_ui.widget.LoadingView;
-import org.chromium.policy.PolicyService;
+import org.chromium.components.policy.PolicyService;
 
 /**
  * Another FirstRunFragment that is only used when running with CCT.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index 97c6c6e..2a6a3e3a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -41,12 +41,12 @@
 import org.chromium.components.crash.browser.ChildProcessCrashObserver;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 import org.chromium.components.module_installer.util.ModuleUtil;
+import org.chromium.components.policy.CombinedPolicyProvider;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.DeviceUtils;
 import org.chromium.content_public.browser.SpeechRecognition;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.net.NetworkChangeNotifier;
-import org.chromium.policy.CombinedPolicyProvider;
 import org.chromium.ui.resources.ResourceExtractor;
 
 import java.io.File;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index bf3760f..2d18938 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -10,6 +10,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
 
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
@@ -29,6 +30,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Bridge to the native AutocompleteControllerAndroid.
@@ -327,14 +329,14 @@
     }
 
     @CalledByNative
-    private static OmniboxSuggestion buildOmniboxSuggestion(int nativeType, boolean isSearchType,
-            int relevance, int transition, String contents, int[] contentClassificationOffsets,
-            int[] contentClassificationStyles, String description,
-            int[] descriptionClassificationOffsets, int[] descriptionClassificationStyles,
-            SuggestionAnswer answer, String fillIntoEdit, GURL url, GURL imageUrl,
-            String imageDominantColor, boolean isStarred, boolean isDeletable,
-            String postContentType, byte[] postData, int groupId, List<QueryTile> tiles,
-            byte[] clipboardImageData, boolean hasTabMatch) {
+    private static OmniboxSuggestion buildOmniboxSuggestion(int nativeType, int[] nativeSubtypes,
+            boolean isSearchType, int relevance, int transition, String contents,
+            int[] contentClassificationOffsets, int[] contentClassificationStyles,
+            String description, int[] descriptionClassificationOffsets,
+            int[] descriptionClassificationStyles, SuggestionAnswer answer, String fillIntoEdit,
+            GURL url, GURL imageUrl, String imageDominantColor, boolean isStarred,
+            boolean isDeletable, String postContentType, byte[] postData, int groupId,
+            List<QueryTile> tiles, byte[] clipboardImageData, boolean hasTabMatch) {
         assert contentClassificationOffsets.length == contentClassificationStyles.length;
         List<MatchClassification> contentClassifications = new ArrayList<>();
         for (int i = 0; i < contentClassificationOffsets.length; i++) {
@@ -349,8 +351,13 @@
                     descriptionClassificationOffsets[i], descriptionClassificationStyles[i]));
         }
 
-        return new OmniboxSuggestion(nativeType, isSearchType, relevance, transition, contents,
-                contentClassifications, description, descriptionClassifications, answer,
+        Set<Integer> subtypes = new ArraySet(nativeSubtypes.length);
+        for (int i = 0; i < nativeSubtypes.length; i++) {
+            subtypes.add(nativeSubtypes[i]);
+        }
+
+        return new OmniboxSuggestion(nativeType, subtypes, isSearchType, relevance, transition,
+                contents, contentClassifications, description, descriptionClassifications, answer,
                 fillIntoEdit, url, imageUrl, imageDominantColor, isStarred, isDeletable,
                 postContentType, postData, groupId, tiles, clipboardImageData, hasTabMatch);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManager.java
index cc037e2..167d09b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManager.java
@@ -14,6 +14,7 @@
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_IS_DELETABLE_PREFIX;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_IS_SEARCH_TYPE_PREFIX;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_IS_STARRED_PREFIX;
+import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_TYPE_PREFIX;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_POST_CONTENT_DATA_PREFIX;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_POST_CONTENT_TYPE_PREFIX;
@@ -25,7 +26,9 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
 
+import org.chromium.base.Function;
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
@@ -34,7 +37,9 @@
 import org.chromium.url.GURL;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * CachedZeroSuggestionsManager manages caching and restoring zero suggestions.
@@ -88,6 +93,9 @@
                     suggestion.getDescription());
             prefs.writeInt(KEY_ZERO_SUGGEST_NATIVE_TYPE_PREFIX.createKey(numCachableSuggestions),
                     suggestion.getType());
+            prefs.writeStringSet(
+                    KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX.createKey(numCachableSuggestions),
+                    convertSet(suggestion.getSubtypes(), v -> v.toString()));
             prefs.writeBoolean(
                     KEY_ZERO_SUGGEST_IS_SEARCH_TYPE_PREFIX.createKey(numCachableSuggestions),
                     suggestion.isSearchSuggestion());
@@ -163,9 +171,20 @@
             int groupId = prefs.readInt(
                     KEY_ZERO_SUGGEST_GROUP_ID_PREFIX.createKey(i), OmniboxSuggestion.INVALID_GROUP);
 
-            OmniboxSuggestion suggestion = new OmniboxSuggestion(nativeType, isSearchType, 0, 0,
-                    displayText, classifications, description, classifications, null, null, url,
-                    GURL.emptyGURL(), null, isStarred, isDeletable, postContentType, postData,
+            Set<Integer> subtypes = null;
+            try {
+                Set<String> subtypeStrings = prefs.readStringSet(
+                        KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX.createKey(i), null);
+                subtypes = convertSet(subtypeStrings, v -> Integer.parseInt(v));
+            } catch (NumberFormatException e) {
+                // Subtype information contains malformed elements, suggesting that the
+                // entire cache may be damaged.
+                return Collections.emptyList();
+            }
+
+            OmniboxSuggestion suggestion = new OmniboxSuggestion(nativeType, subtypes, isSearchType,
+                    0, 0, displayText, classifications, description, classifications, null, null,
+                    url, GURL.emptyGURL(), null, isStarred, isDeletable, postContentType, postData,
                     groupId, null, null, false);
             suggestions.add(suggestion);
         }
@@ -263,4 +282,23 @@
                 && suggestion.getType() != OmniboxSuggestionType.CLIPBOARD_TEXT
                 && suggestion.getType() != OmniboxSuggestionType.CLIPBOARD_IMAGE;
     }
+
+    /**
+     * Convert the set of type T to set of type U objects.
+     *
+     * @param <T> Type of data held in the input set (inferred).
+     * @param <U> Type of data held in the output set (inferred).
+     * @param input Input set.
+     * @param converter Function object that converts type T into type U.
+     * @return A set of input objects converted to string.
+     */
+    private static <T, U> Set<U> convertSet(Set<T> input, Function<T, U> converter) {
+        if (input == null) return null;
+
+        Set<U> result = new ArraySet<>(input.size());
+        for (T item : input) {
+            result.add(converter.apply(item));
+        }
+        return result;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java
index eab8852..cded0596 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java
@@ -6,6 +6,7 @@
 
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.util.ObjectsCompat;
 
@@ -15,7 +16,9 @@
 import org.chromium.url.GURL;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Container class with information about each omnibox suggestion item.
@@ -55,6 +58,7 @@
     }
 
     private final int mType;
+    private final @NonNull Set<Integer> mSubtypes;
     private final boolean mIsSearchType;
     private final String mDisplayText;
     private final List<MatchClassification> mDisplayTextClassifications;
@@ -76,14 +80,19 @@
     private final byte[] mClipboardImageData;
     private final boolean mHasTabMatch;
 
-    public OmniboxSuggestion(int nativeType, boolean isSearchType, int relevance, int transition,
-            String displayText, List<MatchClassification> displayTextClassifications,
-            String description, List<MatchClassification> descriptionClassifications,
-            SuggestionAnswer answer, String fillIntoEdit, GURL url, GURL imageUrl,
-            String imageDominantColor, boolean isStarred, boolean isDeletable,
-            String postContentType, byte[] postData, int groupId, List<QueryTile> queryTiles,
-            byte[] clipboardImageData, boolean hasTabMatch) {
+    public OmniboxSuggestion(int nativeType, Set<Integer> subtypes, boolean isSearchType,
+            int relevance, int transition, String displayText,
+            List<MatchClassification> displayTextClassifications, String description,
+            List<MatchClassification> descriptionClassifications, SuggestionAnswer answer,
+            String fillIntoEdit, GURL url, GURL imageUrl, String imageDominantColor,
+            boolean isStarred, boolean isDeletable, String postContentType, byte[] postData,
+            int groupId, List<QueryTile> queryTiles, byte[] clipboardImageData,
+            boolean hasTabMatch) {
+        if (subtypes == null) {
+            subtypes = Collections.emptySet();
+        }
         mType = nativeType;
+        mSubtypes = subtypes;
         mIsSearchType = isSearchType;
         mRelevance = relevance;
         mTransition = transition;
@@ -207,6 +216,13 @@
         return mRelevance;
     }
 
+    /**
+     * @return Set of suggestion subtypes.
+     */
+    public @NonNull Set<Integer> getSubtypes() {
+        return mSubtypes;
+    }
+
     @Override
     public int hashCode() {
         int hash = 37 * mType + mDisplayText.hashCode() + mFillIntoEdit.hashCode()
@@ -222,7 +238,7 @@
         }
 
         OmniboxSuggestion suggestion = (OmniboxSuggestion) obj;
-        return mType == suggestion.mType
+        return mType == suggestion.mType && ObjectsCompat.equals(mSubtypes, suggestion.mSubtypes)
                 && TextUtils.equals(mFillIntoEdit, suggestion.mFillIntoEdit)
                 && TextUtils.equals(mDisplayText, suggestion.mDisplayText)
                 && ObjectsCompat.equals(
@@ -249,13 +265,14 @@
 
     @Override
     public String toString() {
-        List<String> pieces = Arrays.asList("mType=" + mType, "mIsSearchType=" + mIsSearchType,
-                "mDisplayText=" + mDisplayText, "mDescription=" + mDescription,
-                "mFillIntoEdit=" + mFillIntoEdit, "mUrl=" + mUrl, "mImageUrl=" + mImageUrl,
-                "mImageDominatColor=" + mImageDominantColor, "mRelevance=" + mRelevance,
-                "mTransition=" + mTransition, "mIsStarred=" + mIsStarred,
-                "mIsDeletable=" + mIsDeletable, "mPostContentType=" + mPostContentType,
-                "mPostData=" + Arrays.toString(mPostData), "mGroupId=" + mGroupId,
+        List<String> pieces = Arrays.asList("mType=" + mType, "mSubtypes=" + mSubtypes.toString(),
+                "mIsSearchType=" + mIsSearchType, "mDisplayText=" + mDisplayText,
+                "mDescription=" + mDescription, "mFillIntoEdit=" + mFillIntoEdit, "mUrl=" + mUrl,
+                "mImageUrl=" + mImageUrl, "mImageDominatColor=" + mImageDominantColor,
+                "mRelevance=" + mRelevance, "mTransition=" + mTransition,
+                "mIsStarred=" + mIsStarred, "mIsDeletable=" + mIsDeletable,
+                "mPostContentType=" + mPostContentType, "mPostData=" + Arrays.toString(mPostData),
+                "mGroupId=" + mGroupId,
                 "mDisplayTextClassifications=" + mDisplayTextClassifications,
                 "mDescriptionClassifications=" + mDescriptionClassifications, "mAnswer=" + mAnswer);
         return pieces.toString();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java
index 795a72bf..55845c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java
@@ -121,7 +121,7 @@
                 TemplateUrlServiceFactory.get().getUrlForVoiceSearchQuery(result.getMatch());
         List<MatchClassification> classifications = new ArrayList<>();
         classifications.add(new MatchClassification(0, MatchClassificationStyle.NONE));
-        suggestions.add(new OmniboxSuggestion(OmniboxSuggestionType.VOICE_SUGGEST, true, 0, 1,
+        suggestions.add(new OmniboxSuggestion(OmniboxSuggestionType.VOICE_SUGGEST, null, true, 0, 1,
                 result.getMatch(), classifications, null, classifications, null, null, voiceUrl,
                 GURL.emptyGURL(), null, false, false, null, null, OmniboxSuggestion.INVALID_GROUP,
                 null, null, false));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index 99a84750..5c57be1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -91,6 +91,9 @@
                     return SuggestionIcon.HISTORY;
 
                 default:
+                    if (suggestion.getSubtypes().contains(/* SUBTYPE_TRENDS = */ 143)) {
+                        return SuggestionIcon.TRENDS;
+                    }
                     return SuggestionIcon.MAGNIFIER;
             }
         } else {
@@ -129,6 +132,10 @@
                 icon = R.drawable.btn_mic;
                 break;
 
+            case SuggestionIcon.TRENDS:
+                icon = R.drawable.trending_up_black_24dp;
+                break;
+
             default:
                 // All other cases are invalid.
                 assert false : "Suggestion type " + type + " is not valid.";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
index 38b2e12..9133328 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
@@ -23,7 +23,8 @@
 public class SuggestionViewProperties {
     @IntDef({SuggestionIcon.UNSET, SuggestionIcon.BOOKMARK, SuggestionIcon.HISTORY,
             SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE,
-            SuggestionIcon.CALCULATOR, SuggestionIcon.FAVICON, SuggestionIcon.TOTAL_COUNT})
+            SuggestionIcon.CALCULATOR, SuggestionIcon.FAVICON, SuggestionIcon.TRENDS,
+            SuggestionIcon.TOTAL_COUNT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SuggestionIcon {
         // This enum is used to back UMA histograms, and should therefore be treated as append-only.
@@ -36,7 +37,8 @@
         int VOICE = 5;
         int CALCULATOR = 6;
         int FAVICON = 7;
-        int TOTAL_COUNT = 8;
+        int TRENDS = 8;
+        int TOTAL_COUNT = 9;
     }
 
     /** The suggestion icon type shown. @see SuggestionIcon. Used for metric collection purposes. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
index 0d84294..25dca80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
@@ -9,6 +9,8 @@
 import org.chromium.chrome.browser.ChromeAccessorActivity;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
 import org.chromium.chrome.browser.sync.AndroidSyncSettings;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -24,7 +26,7 @@
     private static AndroidSyncSettings sAndroidSyncSettings;
 
     @Override
-    protected void handleAction(ChromeActivity triggeringActivity) {
+    public void handleAction(ChromeActivity triggeringActivity) {
         Tab tab = triggeringActivity.getActivityTabProvider().get();
         if (tab == null) return;
         NavigationEntry entry = tab.getWebContents().getNavigationController().getVisibleEntry();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
index b59b3b2..0613149 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
@@ -111,6 +111,10 @@
         }
         Toast.setGlobalExtraYOffset(
                 root.getResources().getDimensionPixelSize(bottomControlsHeightId));
+
+        // Set the visibility of BottomControls to false by default. Components within
+        // BottomControls should update the visibility explicitly if needed.
+        mMediator.setBottomControlsVisible(false);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index e0293f3..3a75b73c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -45,6 +45,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.Criteria;
@@ -242,8 +243,11 @@
     @Test
     @SmallTest
     @Feature({"Browser", "Main", "Bookmark", "RenderTest"})
+    @DisableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP,
+            ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_ICONS})
     @DisableIf.Device(type = {UiDisableIf.TABLET}) // See https://crbug.com/1065043.
-    public void testBookmarkMenuItem() throws IOException {
+    public void
+    testBookmarkMenuItem() throws IOException {
         MenuItem bookmarkStar =
                 AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
                         .findItem(R.id.bookmark_this_page_id);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtilsTest.java
new file mode 100644
index 0000000..10cc619
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtilsTest.java
@@ -0,0 +1,65 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.contextmenu;
+
+import static org.junit.Assert.assertEquals;
+
+import android.webkit.URLUtil;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.blink_public.common.ContextMenuDataMediaType;
+import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
+
+/**
+ * Unit tests for {@link ContextMenuUtils}.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ContextMenuUtilsTest {
+    private static final String sTitleText = "titleText";
+    private static final String sLinkText = "linkText";
+    private static final String sSrcUrl = "https://www.google.com/";
+
+    @Test
+    @SmallTest
+    public void getTitle_hasTitleText() {
+        ContextMenuParams params = new ContextMenuParams(0,
+                org.chromium.blink_public.common.ContextMenuDataMediaType.IMAGE, "", "", sLinkText,
+                "", sSrcUrl, sTitleText, null, false, 0, 0, 0);
+
+        assertEquals(sTitleText, ContextMenuUtils.getTitle(params));
+    }
+
+    @Test
+    @SmallTest
+    public void getTitle_noTitleTextHasLinkText() {
+        ContextMenuParams params = new ContextMenuParams(0, ContextMenuDataMediaType.IMAGE, "", "",
+                sLinkText, "", sSrcUrl, "", null, false, 0, 0, 0);
+
+        assertEquals(sLinkText, ContextMenuUtils.getTitle(params));
+    }
+
+    @Test
+    @SmallTest
+    public void getTitle_noTitleTextOrLinkText() {
+        ContextMenuParams params = new ContextMenuParams(0, ContextMenuDataMediaType.IMAGE, "", "",
+                "", "", sSrcUrl, "", null, false, 0, 0, 0);
+
+        assertEquals(URLUtil.guessFileName(sSrcUrl, null, null), ContextMenuUtils.getTitle(params));
+    }
+
+    @Test
+    @SmallTest
+    public void getTitle_noShareParams() {
+        ContextMenuParams params = new ContextMenuParams(
+                0, ContextMenuDataMediaType.NONE, "", "", "", "", "", "", null, false, 0, 0, 0);
+
+        assertEquals("", ContextMenuUtils.getTitle(params));
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
index 69f6d41..c45c8d0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
@@ -49,13 +49,13 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.contextmenu.RevampedContextMenuUtils;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.net.test.EmbeddedTestServer;
-import org.chromium.policy.test.annotations.Policies;
 import org.chromium.ui.base.Clipboard;
 
 import java.io.IOException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index 12f15ce..5683993 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -45,11 +45,11 @@
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.MultiActivityTestRule;
+import org.chromium.components.policy.AbstractAppRestrictionsProvider;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
-import org.chromium.policy.AbstractAppRestrictionsProvider;
 
 import java.util.HashMap;
 import java.util.List;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
index db37bb7..f43765d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
@@ -38,10 +38,10 @@
 import org.chromium.chrome.browser.policy.EnterpriseInfo;
 import org.chromium.chrome.browser.policy.PolicyServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.policy.PolicyService;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.PolicyService;
 import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.lang.annotation.Retention;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
index fab4a56..da2279b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
@@ -43,12 +43,12 @@
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.TabLoadObserver;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
 import org.chromium.net.test.EmbeddedTestServer;
-import org.chromium.policy.test.annotations.Policies;
 
 /**
  * Integration test for {@link HomepagePolicyManager}.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepageTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepageTestRule.java
index cbc2459..191b79f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepageTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepageTestRule.java
@@ -15,11 +15,11 @@
 
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.components.policy.AbstractAppRestrictionsProvider;
+import org.chromium.components.policy.AppRestrictionsProvider;
+import org.chromium.components.policy.CombinedPolicyProvider;
+import org.chromium.components.policy.test.PolicyData;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.AbstractAppRestrictionsProvider;
-import org.chromium.policy.AppRestrictionsProvider;
-import org.chromium.policy.CombinedPolicyProvider;
-import org.chromium.policy.test.PolicyData;
 
 import java.util.Arrays;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
index 7af1760d..28e93b7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
@@ -21,8 +21,8 @@
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteResultUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteResultUnitTest.java
index 5f0b9390..4b6a7518 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteResultUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteResultUnitTest.java
@@ -42,6 +42,17 @@
         List<OmniboxSuggestion> list2 = Arrays.asList(
                 buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
 
+        // Element 0: 2 subtypes
+        list1.get(0).getSubtypes().add(10);
+        list1.get(0).getSubtypes().add(17);
+        list2.get(0).getSubtypes().add(10);
+        list2.get(0).getSubtypes().add(17);
+
+        // Element 1: 0 subtypes.
+        // Element 2: 1 subtype.
+        list1.get(2).getSubtypes().add(4);
+        list2.get(2).getSubtypes().add(4);
+
         SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
         SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
 
@@ -199,6 +210,31 @@
 
     @Test
     @SmallTest
+    public void autocompleteResult_differentSubtypesAreNotEqual() {
+        List<OmniboxSuggestion> list1 = Arrays.asList(
+                OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
+                        .addSubtype(10)
+                        .build(),
+                OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
+                        .addSubtype(17)
+                        .build());
+
+        List<OmniboxSuggestion> list2 = Arrays.asList(
+                OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
+                        .addSubtype(10)
+                        .build(),
+                OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
+                        .addSubtype(4)
+                        .build());
+
+        AutocompleteResult res1 = new AutocompleteResult(list1, null);
+        AutocompleteResult res2 = new AutocompleteResult(list2, null);
+
+        Assert.assertNotEquals(res1, res2);
+    }
+
+    @Test
+    @SmallTest
     public void autocompleteResult_newItemsAreNotEqual() {
         List<OmniboxSuggestion> list1 =
                 Arrays.asList(buildSuggestionForIndex(1), buildSuggestionForIndex(2));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java
index ac0df82c..ffc173be 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Unit tests for {@link CachedZeroSuggestionsManager}.
@@ -346,4 +347,78 @@
                 dataWithInvalidItems.getSuggestionsList(), dataWithInvalidItems.getGroupsDetails());
         assertAutocompleteResultEquals(dataWithInvalidItems, dataExpected);
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void cacheAndRestoreSuggestionSubtypes() {
+        List<OmniboxSuggestion> list = Arrays.asList(
+                createSuggestionBuilder(1, OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED)
+                        .addSubtype(1)
+                        .addSubtype(4)
+                        .build(),
+                createSuggestionBuilder(2, OmniboxSuggestionType.HISTORY_URL)
+                        .addSubtype(17)
+                        .build(),
+                createSuggestionBuilder(3, OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY)
+                        .addSubtype(2)
+                        .addSubtype(10)
+                        .addSubtype(30)
+                        .build(),
+                createSuggestionBuilder(4, OmniboxSuggestionType.SEARCH_HISTORY).build());
+
+        AutocompleteResult dataToCache = new AutocompleteResult(list, null);
+        CachedZeroSuggestionsManager.saveToCache(dataToCache);
+        AutocompleteResult dataFromCache = CachedZeroSuggestionsManager.readFromCache();
+        assertAutocompleteResultEquals(dataToCache, dataFromCache);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void rejectCacheIfSubtypesAreMalformed() {
+        List<OmniboxSuggestion> list = Arrays.asList(
+                createSuggestionBuilder(1, OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED)
+                        .addSubtype(1)
+                        .addSubtype(4)
+                        .build(),
+                createSuggestionBuilder(2, OmniboxSuggestionType.HISTORY_URL)
+                        .addSubtype(17)
+                        .build());
+
+        AutocompleteResult dataToCache = new AutocompleteResult(list, null);
+        CachedZeroSuggestionsManager.saveToCache(dataToCache);
+
+        // Insert garbage for the Suggestion Subtypes.
+        final SharedPreferencesManager manager = SharedPreferencesManager.getInstance();
+        final Set<String> garbageSubtypes = Set.of("invalid");
+        manager.writeStringSet(
+                ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX.createKey(1),
+                garbageSubtypes);
+
+        AutocompleteResult dataFromCache = CachedZeroSuggestionsManager.readFromCache();
+        assertAutocompleteResultEquals(new AutocompleteResult(null, null), dataFromCache);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void rejectCacheIfSubtypesIncludeNull() {
+        List<OmniboxSuggestion> list = Arrays.asList(
+                createSuggestionBuilder(1, OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED)
+                        .addSubtype(1)
+                        .build());
+
+        AutocompleteResult dataToCache = new AutocompleteResult(list, null);
+        CachedZeroSuggestionsManager.saveToCache(dataToCache);
+
+        final SharedPreferencesManager manager = SharedPreferencesManager.getInstance();
+        final Set<String> garbageSubtypes = Set.of("null");
+        manager.writeStringSet(
+                ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX.createKey(0),
+                garbageSubtypes);
+
+        AutocompleteResult dataFromCache = CachedZeroSuggestionsManager.readFromCache();
+        assertAutocompleteResultEquals(new AutocompleteResult(null, null), dataFromCache);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionBuilderForTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionBuilderForTest.java
index d5aa6901..afc12a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionBuilderForTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionBuilderForTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions;
 
+import androidx.collection.ArraySet;
+
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.components.omnibox.SuggestionAnswer;
@@ -12,6 +14,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Utility class for all omnibox suggestions related tests that aids constructing of Omnibox
@@ -20,6 +23,7 @@
 public class OmniboxSuggestionBuilderForTest {
     // Fields below directly represent fields used in OmniboxSuggestion.java.
     private @OmniboxSuggestionType int mType;
+    private Set<Integer> mSubtypes;
     private boolean mIsSearchType;
     private String mDisplayText;
     private List<OmniboxSuggestion.MatchClassification> mDisplayTextClassifications;
@@ -56,6 +60,7 @@
 
     public OmniboxSuggestionBuilderForTest(@OmniboxSuggestionType int type) {
         mType = type;
+        mSubtypes = new ArraySet<>();
         mDisplayTextClassifications = new ArrayList<>();
         mDescriptionClassifications = new ArrayList<>();
         mUrl = GURL.emptyGURL();
@@ -79,11 +84,11 @@
      * @return New OmniboxSuggestion.
      */
     public OmniboxSuggestion build() {
-        return new OmniboxSuggestion(mType, mIsSearchType, mRelevance, mTransition, mDisplayText,
-                mDisplayTextClassifications, mDescription, mDescriptionClassifications, mAnswer,
-                mFillIntoEdit, mUrl, mImageUrl, mImageDominantColor, mIsStarred, mIsDeletable,
-                mPostContentType, mPostData, mGroupId, mQueryTiles, mClipboardImageData,
-                mHasTabMatch);
+        return new OmniboxSuggestion(mType, mSubtypes, mIsSearchType, mRelevance, mTransition,
+                mDisplayText, mDisplayTextClassifications, mDescription,
+                mDescriptionClassifications, mAnswer, mFillIntoEdit, mUrl, mImageUrl,
+                mImageDominantColor, mIsStarred, mIsDeletable, mPostContentType, mPostData,
+                mGroupId, mQueryTiles, mClipboardImageData, mHasTabMatch);
     }
 
     /**
@@ -220,4 +225,13 @@
         mType = type;
         return this;
     }
+
+    /**
+     * @param subtype Suggestion subtype.
+     * @return Omnibox suggestion builder.
+     */
+    public OmniboxSuggestionBuilderForTest addSubtype(int subtype) {
+        mSubtypes.add(subtype);
+        return this;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
index fb1d27d..9c8f14f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
@@ -124,37 +124,37 @@
      * Create Suggestion for test.
      * Do not use directly; use helper methods to create specific suggestion type instead.
      */
-    private void createSuggestion(int type, boolean isSearch, boolean isBookmark,
-            boolean hasTabMatch, String title, String description) {
-        mSuggestion = OmniboxSuggestionBuilderForTest.searchWithType(type)
-                              .setDisplayText(title)
-                              .setDescription(description)
-                              .setIsSearch(isSearch)
-                              .setIsStarred(isBookmark)
-                              .setHasTabMatch(hasTabMatch)
-                              .build();
+    private OmniboxSuggestionBuilderForTest createSuggestionBuilder(int type, String title) {
+        return OmniboxSuggestionBuilderForTest.searchWithType(type).setDisplayText(title);
+    }
+
+    /** Create bookmark suggestion for test. */
+    private void createBookmarkSuggestion(int type, String title) {
+        mSuggestion =
+                createSuggestionBuilder(type, title).setIsSearch(false).setIsStarred(true).build();
         mModel = mProcessor.createModel();
         mProcessor.populateModel(mSuggestion, mModel, 0);
     }
 
-    /** Create bookmark suggestion for test. */
-    private void createBookmarkSuggestion(int type, String title, String description) {
-        createSuggestion(type, false, true, false, title, description);
-    }
-
     /** Create search suggestion for test. */
-    private void createSearchSuggestion(int type, String title, String description) {
-        createSuggestion(type, true, false, false, title, description);
+    private void createSearchSuggestion(int type, String title) {
+        mSuggestion = createSuggestionBuilder(type, title).setIsSearch(true).build();
+        mModel = mProcessor.createModel();
+        mProcessor.populateModel(mSuggestion, mModel, 0);
     }
 
     /** Create URL suggestion for test. */
-    private void createUrlSuggestion(int type, String title, String description) {
-        createSuggestion(type, false, false, false, title, description);
+    private void createUrlSuggestion(int type, String title) {
+        mSuggestion = createSuggestionBuilder(type, title).setIsSearch(false).build();
+        mModel = mProcessor.createModel();
+        mProcessor.populateModel(mSuggestion, mModel, 0);
     }
 
     /** Create switch to tab suggestion for test. */
-    private void createSwitchToTabSuggestion(int type, String title, String description) {
-        createSuggestion(type, false, false, true, title, description);
+    private void createSwitchToTabSuggestion(int type, String title) {
+        mSuggestion = createSuggestionBuilder(type, title).setHasTabMatch(true).build();
+        mModel = mProcessor.createModel();
+        mProcessor.populateModel(mSuggestion, mModel, 0);
     }
 
     private void assertSuggestionTypeAndIcon(
@@ -195,7 +195,7 @@
 
         mProcessor.onNativeInitialized();
         for (int[] testCase : testCases) {
-            createSearchSuggestion(testCase[0], "", "");
+            createSearchSuggestion(testCase[0], "");
             Assert.assertTrue(mModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
             assertSuggestionTypeAndIcon(testCase[0], testCase[1]);
         }
@@ -230,7 +230,7 @@
 
         mProcessor.onNativeInitialized();
         for (int[] testCase : testCases) {
-            createUrlSuggestion(testCase[0], "", "");
+            createUrlSuggestion(testCase[0], "");
             Assert.assertFalse(mModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
             assertSuggestionTypeAndIcon(testCase[0], testCase[1]);
         }
@@ -265,7 +265,7 @@
 
         mProcessor.onNativeInitialized();
         for (int[] testCase : testCases) {
-            createBookmarkSuggestion(testCase[0], "", "");
+            createBookmarkSuggestion(testCase[0], "");
             Assert.assertFalse(mModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
             assertSuggestionTypeAndIcon(testCase[0], testCase[1]);
         }
@@ -274,15 +274,41 @@
     @Test
     @SmallTest
     @UiThreadTest
+    @DisableFeatures({ChromeFeatureList.OMNIBOX_COMPACT_SUGGESTIONS,
+            ChromeFeatureList.OMNIBOX_SUGGESTIONS_WRAP_AROUND})
+    public void
+    getSuggestionIconTypeForTrendingQueries() {
+        int[][] testCases = {
+                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.TRENDS},
+                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.HISTORY},
+                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.TRENDS},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.TRENDS},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.HISTORY},
+                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
+        };
+
+        mProcessor.onNativeInitialized();
+        for (int[] testCase : testCases) {
+            mSuggestion = createSuggestionBuilder(testCase[0], "").addSubtype(143).build();
+            mModel = mProcessor.createModel();
+            mProcessor.populateModel(mSuggestion, mModel, 0);
+            Assert.assertTrue(mModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
+            assertSuggestionTypeAndIcon(testCase[0], testCase[1]);
+        }
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
     public void refineIconNotShownForWhatYouTypedSuggestions() {
         final String typed = "Typed content";
         doReturn(typed).when(mUrlBarText).getTextWithoutAutocomplete();
-        createSearchSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, typed, "");
+        createSearchSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, typed);
         PropertyModel model = mProcessor.createModel();
         mProcessor.populateModel(mSuggestion, model, 0);
         Assert.assertNull(mModel.get(BaseSuggestionViewProperties.ACTIONS));
 
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, typed, "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, typed);
         mProcessor.populateModel(mSuggestion, model, 0);
         Assert.assertNull(mModel.get(BaseSuggestionViewProperties.ACTIONS));
     }
@@ -294,12 +320,12 @@
         final String typed = "Typed conte";
         final String refined = "Typed content";
         doReturn(typed).when(mUrlBarText).getTextWithoutAutocomplete();
-        createSearchSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, refined, "");
+        createSearchSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, refined);
         PropertyModel model = mProcessor.createModel();
         mProcessor.populateModel(mSuggestion, model, 0);
         Assert.assertNotNull(mModel.get(BaseSuggestionViewProperties.ACTIONS));
 
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, refined, "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, refined);
         mProcessor.populateModel(mSuggestion, model, 0);
         Assert.assertNotNull(mModel.get(BaseSuggestionViewProperties.ACTIONS));
 
@@ -315,7 +341,7 @@
     @UiThreadTest
     public void switchTabIconShownForSwitchToTabSuggestions() {
         final String tabMatch = "tab match";
-        createSwitchToTabSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, tabMatch, "");
+        createSwitchToTabSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, tabMatch);
         PropertyModel model = mProcessor.createModel();
         mProcessor.populateModel(mSuggestion, model, 0);
         Assert.assertNotNull(mModel.get(BaseSuggestionViewProperties.ACTIONS));
@@ -336,7 +362,7 @@
         final ArgumentCaptor<LargeIconCallback> callback =
                 ArgumentCaptor.forClass(LargeIconCallback.class);
         mProcessor.onNativeInitialized();
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "", "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
@@ -359,7 +385,7 @@
         final ArgumentCaptor<LargeIconCallback> callback =
                 ArgumentCaptor.forClass(LargeIconCallback.class);
         mProcessor.onNativeInitialized();
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "", "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
@@ -379,10 +405,10 @@
     @EnableFeatures(ChromeFeatureList.OMNIBOX_SUGGESTIONS_WRAP_AROUND)
     public void searchSuggestions_searchQueriesCanWrapAroundWithFeatureEnabled() {
         mProcessor.onNativeInitialized();
-        createSearchSuggestion(OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, "", "");
+        createSearchSuggestion(OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, "");
         Assert.assertEquals(mModel.get(SuggestionViewProperties.ALLOW_WRAP_AROUND), true);
 
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "", "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         Assert.assertEquals(mModel.get(SuggestionViewProperties.ALLOW_WRAP_AROUND), false);
     }
 
@@ -393,10 +419,10 @@
             ChromeFeatureList.OMNIBOX_SUGGESTIONS_WRAP_AROUND})
     public void searchSuggestions_searchQueriesDontWrapAroundWithFeatureDisabled() {
         mProcessor.onNativeInitialized();
-        createSearchSuggestion(OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, "", "");
+        createSearchSuggestion(OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, "");
         Assert.assertEquals(mModel.get(SuggestionViewProperties.ALLOW_WRAP_AROUND), false);
 
-        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "", "");
+        createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         Assert.assertEquals(mModel.get(SuggestionViewProperties.ALLOW_WRAP_AROUND), false);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java
index c08081db1..52308a02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.policy.CombinedPolicyProvider;
+import org.chromium.components.policy.PolicyProvider;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.CombinedPolicyProvider;
-import org.chromium.policy.PolicyProvider;
 
 /** Instrumentation tests for {@link CombinedPolicyProvider} */
 @RunWith(ChromeJUnit4ClassRunner.class)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
index ad6c47a..d81acad 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
@@ -31,13 +31,13 @@
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.search_engines.TemplateUrlService.LoadListener;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 import java.util.List;
 import java.util.concurrent.ExecutionException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
index 6ec9f0b5..ae550a7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
@@ -21,10 +21,10 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 /**
  * Tests for the Settings menu.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 795cc977..2c9d106e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -62,12 +62,12 @@
 import org.chromium.components.embedder_support.browser_context.BrowserContextHandle;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.permissions.nfc.NfcSystemLevelSetting;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.device.geolocation.LocationProviderOverrider;
 import org.chromium.device.geolocation.MockLocationProvider;
-import org.chromium.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
index 4c346c8..ce5239f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
@@ -19,6 +19,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -102,6 +103,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug/1128073")
     public void testGetProfileOnNullTabInIncognito() {
         mActivityTestRule.startMainActivityOnBlankPage();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -114,6 +116,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug/1128073")
     public void testGetProfileOnMockTabInIncognito() {
         mActivityTestRule.startMainActivityOnBlankPage();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -149,6 +152,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug/1128073")
     public void testGetProfileOnMockTabInRegularCCT() {
         // Create an launch a regular CCT.
         Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index 03b76af..9f58c24 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -7,6 +7,7 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/ssl/ChromeSecurityStateModelDelegate.java",
   "+chrome/browser/performance_hints/android/java",
   "+chrome/browser/profiles/android",
+  "+chrome/browser/share",
   "+chrome/browser/tab",
   "+chrome/browser/tabmodel",
   "+chrome/browser/thumbnail/generator/android/java",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
index 9330dd04..2d0a69b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
@@ -29,8 +29,8 @@
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.components.policy.PolicySwitches;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.PolicySwitches;
 import org.chromium.testing.local.CustomShadowUserManager;
 
 import java.util.Arrays;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/OWNERS
deleted file mode 100644
index f8e745b..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-file://components/send_tab_to_self/OWNERS
-
-# COMPONENT: UI>Browser>Sharing
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
index 090e29c..b0292ef7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
@@ -26,6 +26,9 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileJni;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridgeJni;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
 import org.chromium.chrome.browser.sync.AndroidSyncSettings;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index a51c978e..acadc5c 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-87.0.4261.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-87.0.4262.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/android/chrome_main_delegate_android.cc b/chrome/app/android/chrome_main_delegate_android.cc
index 7af2f11..6943ab4 100644
--- a/chrome/app/android/chrome_main_delegate_android.cc
+++ b/chrome/app/android/chrome_main_delegate_android.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/android/chrome_startup_flags.h"
 #include "chrome/browser/android/metrics/uma_utils.h"
 #include "chrome/common/profiler/main_thread_stack_sampling_profiler.h"
-#include "components/policy/core/browser/android/android_combined_policy_provider.h"
+#include "components/policy/core/common/android/android_combined_policy_provider.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "content/public/browser/browser_main_runner.h"
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 5780f0b3..ea8e1d4 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -268,7 +268,6 @@
   } else if (process_type == switches::kUtilityProcess ||
              process_type == switches::kGpuProcess ||
              process_type == switches::kCloudPrintServiceProcess ||
-             process_type == service_manager::switches::kProcessTypeService ||
              process_type == switches::kPpapiBrokerProcess) {
     score = content::kMiscOomScore;
 #if BUILDFLAG(ENABLE_NACL)
@@ -277,8 +276,6 @@
     score = content::kPluginOomScore;
 #endif
   } else if (process_type == service_manager::switches::kZygoteProcess ||
-             process_type ==
-                 service_manager::switches::kProcessTypeServiceManager ||
              process_type.empty()) {
     // For zygotes and unlabeled process types, we want to still make
     // them killable by the OOM killer.
@@ -1238,16 +1235,6 @@
   return g_chrome_content_utility_client.Pointer();
 }
 
-service_manager::ProcessType ChromeMainDelegate::OverrideProcessType() {
-  const auto& command_line = *base::CommandLine::ForCurrentProcess();
-  if (command_line.GetSwitchValueASCII(switches::kProcessType) ==
-      service_manager::switches::kProcessTypeService) {
-    // Don't mess with embedded service command lines.
-    return service_manager::ProcessType::kDefault;
-  }
-  return service_manager::ProcessType::kDefault;
-}
-
 void ChromeMainDelegate::PreCreateMainMessageLoop() {
 #if defined(OS_MAC)
   // Tell Cocoa to finish its initialization, which we want to do manually
diff --git a/chrome/app/chrome_main_delegate.h b/chrome/app/chrome_main_delegate.h
index c32e99ab..1056536 100644
--- a/chrome/app/chrome_main_delegate.h
+++ b/chrome/app/chrome_main_delegate.h
@@ -58,7 +58,6 @@
                           delegates) override;
   void ZygoteForked() override;
 #endif
-  service_manager::ProcessType OverrideProcessType() override;
   void PreCreateMainMessageLoop() override;
   void PostEarlyInitialization(bool is_running_tests) override;
   bool ShouldCreateFeatureList() override;
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 2846051..8853fad8 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4573,9 +4573,6 @@
   <message name="IDS_CROSTINI_TERMINAL_STATUS_INSTALL_IMAGE_LOADER" desc="Text shown in the crostini terminal when it is checking whether the virtual machine component is installed">
     Checking the virtual machine
   </message>
-  <message name="IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE" desc="Text shown in the crostini terminal when it is starting the VM controller">
-    Starting the virtual machine controller
-  </message>
   <message name="IDS_CROSTINI_TERMINAL_STATUS_CREATE_DISK_IMAGE" desc="Text shown in the crostini terminal when it is checking whether the VM image is installed">
     Checking the virtual machine image
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1
deleted file mode 100644
index c2dd105..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d5dfef986211c69d831ac4515dd9defdc772cfc3
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index efe3364..e10625e8 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -190,6 +190,7 @@
       "notification_vpn.icon",
       "notification_wifi_off.icon",
       "notification_wifi.icon",
+      "person_add.icon",
       "shutdown_guest_os.icon",
       "warning_badge_circle.icon",
     ]
diff --git a/chrome/app/vector_icons/person_add.icon b/chrome/app/vector_icons/person_add.icon
new file mode 100644
index 0000000..e6efbd4
--- /dev/null
+++ b/chrome/app/vector_icons/person_add.icon
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 15, 12,
+R_CUBIC_TO, 2.21f, 0, 4, -1.79f, 4, -4,
+R_CUBIC_TO, 0, -2.21f, -1.79f, -4, -4, -4,
+R_CUBIC_TO, -2.21f, 0, -4, 1.79f, -4, 4,
+R_CUBIC_TO, 0, 2.21f, 1.79f, 4, 4, 4,
+CLOSE,
+R_MOVE_TO, -9, -2,
+V_LINE_TO, 7,
+H_LINE_TO, 4,
+R_V_LINE_TO, 3,
+H_LINE_TO, 1,
+R_V_LINE_TO, 2,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, 3,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, -3,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, -2,
+H_LINE_TO, 6,
+CLOSE,
+R_MOVE_TO, 9, 4,
+R_CUBIC_TO, -2.67f, 0, -8, 1.34f, -8, 4,
+R_V_LINE_TO, 2,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, -2,
+R_CUBIC_TO, 0, -2.66f, -5.33f, -4, -8, -4,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a1fd5cd..f110408 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1695,6 +1695,8 @@
     "storage/appcache_feature_prefs.h",
     "storage/durable_storage_permission_context.cc",
     "storage/durable_storage_permission_context.h",
+    "subresource_filter/ads_intervention_manager.cc",
+    "subresource_filter/ads_intervention_manager.h",
     "subresource_filter/chrome_subresource_filter_client.cc",
     "subresource_filter/chrome_subresource_filter_client.h",
     "subresource_filter/subresource_filter_content_settings_manager.cc",
@@ -6029,7 +6031,6 @@
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings_js",
-      "//chrome/browser/ui/webui/chromeos/file_manager:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/machine_learning:mojo_bindings_js",
       "//chrome/browser/ui/webui/settings/chromeos:mojom_js",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5fc83de..bbf4ef1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1351,55 +1351,88 @@
     {"6 matches", kOmniboxMaxURLMatches6, base::size(kOmniboxMaxURLMatches6),
      nullptr}};
 
-const FeatureEntry::FeatureVariation kDynamicMaxAutocompleteVariations[] = {
-    {
-        "9 suggestions if 0 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "0"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
-        2,
-        nullptr,
-    },
-    {
-        "9 suggestions if 1 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "1"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
-        2,
-        nullptr,
-    },
-    {
-        "9 suggestions if 2 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "2"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
-        2,
-        nullptr,
-    },
-    {
-        "10 suggestions if 0 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "0"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
-        2,
-        nullptr,
-    },
-    {
-        "10 suggestions if 1 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "1"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
-        2,
-        nullptr,
-    },
-    {
-        "10 suggestions if 2 or less URLs",
-        (FeatureEntry::FeatureParam[]){
-            {"OmniboxDynamicMaxAutocompleteUrlCutoff", "2"},
-            {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
-        2,
-        nullptr,
-    }};
+const FeatureEntry::FeatureVariation
+    kOmniboxDynamicMaxAutocompleteVariations[] = {
+        {
+            "9 suggestions if 0 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "0"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
+            2,
+            nullptr,
+        },
+        {
+            "9 suggestions if 1 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "1"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
+            2,
+            nullptr,
+        },
+        {
+            "9 suggestions if 2 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "2"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "9"}},
+            2,
+            nullptr,
+        },
+        {
+            "10 suggestions if 0 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "0"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
+            2,
+            nullptr,
+        },
+        {
+            "10 suggestions if 1 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "1"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
+            2,
+            nullptr,
+        },
+        {
+            "10 suggestions if 2 or less URLs",
+            (FeatureEntry::FeatureParam[]){
+                {"OmniboxDynamicMaxAutocompleteUrlCutoff", "2"},
+                {"OmniboxDynamicMaxAutocompleteIncreasedLimit", "10"}},
+            2,
+            nullptr,
+        }};
+
+const FeatureEntry::FeatureVariation kOmniboxBubbleUrlSuggestionsVariations[] =
+    {{
+         "Gap 200, Buffer 100",
+         (FeatureEntry::FeatureParam[]){
+             {"OmniboxBubbleUrlSuggestionsAbsoluteGap", "200"},
+             {"OmniboxBubbleUrlSuggestionsRelativeGap", "1"},
+             {"OmniboxBubbleUrlSuggestionsAbsoluteBuffer", "100"},
+             {"OmniboxBubbleUrlSuggestionsRelativeBuffer", "1"}},
+         4,
+         nullptr,
+     },
+     {
+         "Gap 200, Buffer 200",
+         (FeatureEntry::FeatureParam[]){
+             {"OmniboxBubbleUrlSuggestionsAbsoluteGap", "200"},
+             {"OmniboxBubbleUrlSuggestionsRelativeGap", "1"},
+             {"OmniboxBubbleUrlSuggestionsAbsoluteBuffer", "200"},
+             {"OmniboxBubbleUrlSuggestionsRelativeBuffer", "1"}},
+         4,
+         nullptr,
+     },
+     {
+         "Gap 400, Buffer 200",
+         (FeatureEntry::FeatureParam[]){
+             {"OmniboxBubbleUrlSuggestionsAbsoluteGap", "400"},
+             {"OmniboxBubbleUrlSuggestionsRelativeGap", "1"},
+             {"OmniboxBubbleUrlSuggestionsAbsoluteBuffer", "200"},
+             {"OmniboxBubbleUrlSuggestionsRelativeBuffer", "1"}},
+         4,
+         nullptr,
+     }};
 
 const FeatureEntry::FeatureParam kMarkHttpAsDangerous[] = {
     {security_state::features::kMarkHttpAsFeatureParameterName,
@@ -2547,6 +2580,10 @@
      flag_descriptions::kAshEnableUnifiedDesktopName,
      flag_descriptions::kAshEnableUnifiedDesktopDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(switches::kEnableUnifiedDesktop)},
+    {"ash-enable-interactive-window-cycle-list",
+     flag_descriptions::kInteractiveWindowCycleList,
+     flag_descriptions::kInteractiveWindowCycleListDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kInteractiveWindowCycleList)},
     {"bluetooth-aggressive-appearance-filter",
      flag_descriptions::kBluetoothAggressiveAppearanceFilterName,
      flag_descriptions::kBluetoothAggressiveAppearanceFilterDescription,
@@ -3957,7 +3994,14 @@
      flag_descriptions::kOmniboxDynamicMaxAutocompleteName,
      flag_descriptions::kOmniboxDynamicMaxAutocompleteDescription, kOsAll,
      FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kDynamicMaxAutocomplete,
-                                    kDynamicMaxAutocompleteVariations,
+                                    kOmniboxDynamicMaxAutocompleteVariations,
+                                    "OmniboxBundledExperimentV1")},
+
+    {"omnibox-bubble-url-suggestions",
+     flag_descriptions::kOmniboxBubbleUrlSuggestionsName,
+     flag_descriptions::kOmniboxBubbleUrlSuggestionsDescription, kOsAll,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kBubbleUrlSuggestions,
+                                    kOmniboxBubbleUrlSuggestionsVariations,
                                     "OmniboxBundledExperimentV1")},
 
     {"omnibox-ui-swap-title-and-url",
@@ -5884,11 +5928,6 @@
      FEATURE_VALUE_TYPE(chromeos::features::kUseWallpaperStagingUrl)},
 #endif  // defined(OS_CHROMEOS)
 
-    {"passive-mixed-content-warning",
-     flag_descriptions::kPassiveMixedContentWarningName,
-     flag_descriptions::kPassiveMixedContentWarningDescription, kOsAll,
-     FEATURE_VALUE_TYPE(security_state::features::kPassiveMixedContentWarning)},
-
     {"autofill-enable-virtual-card",
      flag_descriptions::kAutofillEnableVirtualCardName,
      flag_descriptions::kAutofillEnableVirtualCardDescription, kOsDesktop,
@@ -6325,6 +6364,11 @@
     {"enable-tab-search", flag_descriptions::kEnableTabSearchName,
      flag_descriptions::kEnableTabSearchDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kTabSearch)},
+
+    {"enable-tab-search-fixed-entrypoint",
+     flag_descriptions::kEnableTabSearchFixedEntrypointName,
+     flag_descriptions::kEnableTabSearchFixedEntrypointDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kTabSearchFixedEntrypoint)},
 #endif  // BUILDFLAG(ENABLE_TAB_SEARCH)
 
 #if defined(OS_ANDROID)
@@ -6409,6 +6453,17 @@
      FEATURE_VALUE_TYPE(language::kDetailedLanguageSettings)},
 #endif
 
+    {"sync-autofill-wallet-offer-data",
+     flag_descriptions::kSyncAutofillWalletOfferDataName,
+     flag_descriptions::kSyncAutofillWalletOfferDataDescription, kOsAll,
+     FEATURE_VALUE_TYPE(switches::kSyncAutofillWalletOfferData)},
+
+#if defined(OS_CHROMEOS)
+    {"enable-holding-space", flag_descriptions::kHoldingSpaceName,
+     flag_descriptions::kHoldingSpaceDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kTemporaryHoldingSpace)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/feed/v2/feed_image_fetch_client.cc b/chrome/browser/android/feed/v2/feed_image_fetch_client.cc
index 906cad70..4e9b520e 100644
--- a/chrome/browser/android/feed/v2/feed_image_fetch_client.cc
+++ b/chrome/browser/android/feed/v2/feed_image_fetch_client.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/feed/core/v2/public/feed_service.h"
+#include "components/feed/core/v2/public/feed_stream_api.h"
 #include "components/feed/core/v2/public/types.h"
 
 using base::android::JavaParamRef;
@@ -26,9 +27,21 @@
       base::android::ToJavaByteArray(env, response.response_bytes));
 }
 
+FeedStreamApi* GetFeedStream() {
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  if (!profile)
+    return nullptr;
+
+  FeedService* feed_service = FeedServiceFactory::GetForBrowserContext(profile);
+  if (!feed_service)
+    return nullptr;
+
+  return feed_service->GetStream();
+}
+
 }  // namespace
 
-void JNI_FeedImageFetchClient_SendRequest(
+jint JNI_FeedImageFetchClient_SendRequest(
     JNIEnv* env,
     const JavaParamRef<jstring>& j_url,
     const JavaParamRef<jobject>& j_response_callback) {
@@ -36,17 +49,24 @@
   // with OnFetchFinished.
   base::android::ScopedJavaGlobalRef<jobject> callback(j_response_callback);
 
-  Profile* profile = ProfileManager::GetLastUsedProfile();
-  if (!profile)
-    return OnFetchFinished(env, std::move(callback), {});
+  FeedStreamApi* stream = GetFeedStream();
+  if (!stream) {
+    OnFetchFinished(env, std::move(callback), {});
+    return 0;
+  }
 
-  FeedService* feed_service = FeedServiceFactory::GetForBrowserContext(profile);
-  if (!feed_service || !feed_service->GetStream())
-    return OnFetchFinished(env, std::move(callback), {});
+  return stream
+      ->FetchImage(GURL(base::android::ConvertJavaStringToUTF8(env, j_url)),
+                   base::BindOnce(&OnFetchFinished, env, std::move(callback)))
+      .GetUnsafeValue();
+}
 
-  feed_service->GetStream()->FetchImage(
-      GURL(base::android::ConvertJavaStringToUTF8(env, j_url)),
-      base::BindOnce(&OnFetchFinished, env, std::move(callback)));
+void JNI_FeedImageFetchClient_Cancel(JNIEnv* env, jint j_request_id) {
+  FeedStreamApi* stream = GetFeedStream();
+  if (!stream)
+    return;
+
+  stream->CancelImageFetch(ImageFetchId::FromUnsafeValue(j_request_id));
 }
 
 }  // namespace feed
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 4a03f01..c38f57b7 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -541,7 +541,7 @@
     for (size_t i = match.contents_class.size(); i > 0; --i) {
       ACMatchClassification classification(match.contents_class[i - 1]);
       int updated_offset = std::max(
-          0 , static_cast<int>(classification.offset) - match_offset_delta);
+          0, static_cast<int>(classification.offset) - match_offset_delta);
       out_classifications->insert(
           out_classifications->begin(),
           ACMatchClassification(updated_offset, classification.style));
@@ -618,10 +618,11 @@
 
   BookmarkModel* bookmark_model =
       BookmarkModelFactory::GetForBrowserContext(profile_);
+  const std::vector<int> subtypes(match.subtypes.begin(), match.subtypes.end());
   return Java_AutocompleteController_buildOmniboxSuggestion(
-      env, match.type, AutocompleteMatch::IsSearchType(match.type),
-      match.relevance, match.transition, jcontents,
-      ToJavaIntArray(env, contents_class_offsets),
+      env, match.type, ToJavaIntArray(env, subtypes),
+      AutocompleteMatch::IsSearchType(match.type), match.relevance,
+      match.transition, jcontents, ToJavaIntArray(env, contents_class_offsets),
       ToJavaIntArray(env, contents_class_styles), description,
       ToJavaIntArray(env, description_class_offsets),
       ToJavaIntArray(env, description_class_styles), janswer, fill_into_edit,
diff --git a/chrome/browser/android/send_tab_to_self/android_notification_handler.cc b/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
index b4f3a2c7..ad0bcd4 100644
--- a/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
+++ b/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
@@ -10,8 +10,8 @@
 #include "base/android/jni_string.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/android/chrome_jni_headers/NotificationManager_jni.h"
 #include "chrome/android/chrome_jni_headers/SendTabToSelfNotificationReceiver_jni.h"
+#include "chrome/browser/share/android/jni_headers/NotificationManager_jni.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 
 using base::android::AttachCurrentThread;
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
index bdeede8b..65d75f9 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
@@ -9,14 +9,14 @@
 #include "base/android/jni_string.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
-#include "chrome/android/chrome_jni_headers/SendTabToSelfAndroidBridge_jni.h"
-#include "chrome/android/chrome_jni_headers/TargetDeviceInfo_jni.h"
 #include "chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.h"
 #include "chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_client_service_factory.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
+#include "chrome/browser/share/android/jni_headers/SendTabToSelfAndroidBridge_jni.h"
+#include "chrome/browser/share/android/jni_headers/TargetDeviceInfo_jni.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 #include "components/send_tab_to_self/send_tab_to_self_infobar_delegate.h"
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.cc
index c393a239..2e3f69a3 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.cc
@@ -6,7 +6,7 @@
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "chrome/android/chrome_jni_headers/SendTabToSelfEntry_jni.h"
+#include "chrome/browser/share/android/jni_headers/SendTabToSelfEntry_jni.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 
 using base::android::AttachCurrentThread;
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc
index 1d9c74f..1fb2f11 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_infobar.cc
@@ -12,10 +12,10 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/android/chrome_jni_headers/SendTabToSelfInfoBar_jni.h"
 #include "chrome/browser/android/resource_mapper.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/share/android/jni_headers/SendTabToSelfInfoBar_jni.h"
 #include "components/infobars/android/infobar_android.h"
 #include "components/infobars/core/infobar_delegate.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_model_observer_bridge.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_model_observer_bridge.cc
index a4af1dd8..2250342 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_model_observer_bridge.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_model_observer_bridge.cc
@@ -10,10 +10,10 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/time/time.h"
-#include "chrome/android/chrome_jni_headers/SendTabToSelfModelObserverBridge_jni.h"
 #include "chrome/browser/android/send_tab_to_self/send_tab_to_self_entry_bridge.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/share/android/jni_headers/SendTabToSelfModelObserverBridge_jni.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index fbd423e2b..0ee32277 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/apps/app_service/app_icon_factory.h"
 
-#include <map>
 #include <memory>
 #include <utility>
 
@@ -76,7 +75,6 @@
 using SizeToImageSkiaRep = std::map<int, gfx::ImageSkiaRep>;
 using ScaleToImageSkiaReps = std::map<float, SizeToImageSkiaRep>;
 using MaskImageSkiaReps = std::pair<SkBitmap, ScaleToImageSkiaReps>;
-using ScaleToSize = std::map<float, int>;
 
 MaskImageSkiaReps& GetMaskResourceIconCache() {
   static base::NoDestructor<MaskImageSkiaReps> mask_cache;
@@ -119,8 +117,8 @@
   return image_rep;
 }
 
-ScaleToSize GetScaleToSize(const gfx::ImageSkia& image_skia) {
-  ScaleToSize scale_to_size;
+apps::ScaleToSize GetScaleToSize(const gfx::ImageSkia& image_skia) {
+  apps::ScaleToSize scale_to_size;
   if (image_skia.image_reps().empty()) {
     scale_to_size[1.0f] = image_skia.size().width();
   } else {
@@ -131,18 +129,6 @@
   return scale_to_size;
 }
 
-gfx::ImageSkia LoadMaskImage(const ScaleToSize& scale_to_size) {
-  gfx::ImageSkia mask_image;
-  for (const auto& it : scale_to_size) {
-    float scale = it.first;
-    int size_hint_in_dip = it.second;
-    mask_image.AddRepresentation(
-        GetMaskAsImageSkiaRep(scale, size_hint_in_dip));
-  }
-
-  return mask_image;
-}
-
 bool IsConsistentPixelSize(const gfx::ImageSkiaRep& rep,
                            const gfx::ImageSkia& image_skia) {
   // The pixel size calculation method must be consistent with
@@ -1105,6 +1091,18 @@
 
 #if defined(OS_CHROMEOS)
 
+gfx::ImageSkia LoadMaskImage(const ScaleToSize& scale_to_size) {
+  gfx::ImageSkia mask_image;
+  for (const auto& it : scale_to_size) {
+    float scale = it.first;
+    int size_hint_in_dip = it.second;
+    mask_image.AddRepresentation(
+        GetMaskAsImageSkiaRep(scale, size_hint_in_dip));
+  }
+
+  return mask_image;
+}
+
 gfx::ImageSkia ApplyBackgroundAndMask(const gfx::ImageSkia& image) {
   return gfx::ImageSkiaOperations::CreateButtonBackground(
       SK_ColorWHITE, image, LoadMaskImage(GetScaleToSize(image)));
diff --git a/chrome/browser/apps/app_service/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon_factory.h
index c22c656..5aae37c 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.h
+++ b/chrome/browser/apps/app_service/app_icon_factory.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_ICON_FACTORY_H_
 #define CHROME_BROWSER_APPS_APP_SERVICE_APP_ICON_FACTORY_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -26,6 +27,8 @@
 
 namespace apps {
 
+using ScaleToSize = std::map<float, int>;
+
 // A bitwise-or of icon post-processing effects.
 //
 // It derives from a uint32_t because it needs to be the same size as the
@@ -88,6 +91,8 @@
                                            float rep_icon_scale);
 
 #if defined(OS_CHROMEOS)
+gfx::ImageSkia LoadMaskImage(const ScaleToSize& scale_to_size);
+
 gfx::ImageSkia ApplyBackgroundAndMask(const gfx::ImageSkia& image);
 
 gfx::ImageSkia CompositeImagesAndApplyMask(
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index c109b726..da866a0f 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -213,7 +213,7 @@
 
 arc::mojom::IntentInfoPtr CreateArcIntent(apps::mojom::IntentPtr intent) {
   arc::mojom::IntentInfoPtr arc_intent;
-  if (!intent->url.has_value()) {
+  if (!intent->url.has_value() && !intent->share_text.has_value()) {
     return arc_intent;
   }
   arc_intent = arc::mojom::IntentInfo::New();
@@ -230,7 +230,14 @@
   } else {
     arc_intent->action = arc::kIntentActionView;
   }
-  arc_intent->data = intent->url->spec();
+  if (intent->url.has_value()) {
+    arc_intent->data = intent->url->spec();
+  }
+  if (intent->share_text.has_value()) {
+    arc_intent->extras = base::flat_map<std::string, std::string>();
+    arc_intent->extras.value().insert(std::make_pair(
+        "android.intent.extra.TEXT", intent->share_text.value()));
+  }
   return arc_intent;
 }
 
@@ -761,14 +768,8 @@
         !intent->activity_name.value().empty()) {
       activity->activity_name = intent->activity_name.value();
     }
-    // At the moment, the only case we have mime_type field set is to share
-    // files, in this case, convert the file urls to content urls and use
-    // arc file system instance to launch the app with files.
-    if (intent->mime_type.has_value()) {
-      if (!intent->file_urls.has_value()) {
-        LOG(ERROR) << "Share files failed, share intent is not valid";
-        return;
-      }
+
+    if (intent->mime_type.has_value() && intent->file_urls.has_value()) {
       file_manager::util::ConvertToContentUrls(
           apps::GetFileSystemURL(profile_, intent->file_urls.value()),
           base::BindOnce(&OnContentUrlResolved, std::move(intent),
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 189ac6ad..bec641be 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -279,12 +279,6 @@
         <include name="IDR_DRIVE_INTERNALS_HTML" file="resources\chromeos\drive_internals.html" flattenhtml="true" type="BINDATA" />
         <include name="IDR_DRIVE_INTERNALS_JS" file="resources\chromeos\drive_internals.js" type="BINDATA" />
 
-        <include name="IDR_FILE_MANAGER_CSS" file="resources\chromeos\file_manager\file_manager.css" type="BINDATA" />
-        <include name="IDR_FILE_MANAGER_HTML" file="resources\chromeos\file_manager\file_manager.html" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_FILE_MANAGER_JS" file="resources\chromeos\file_manager\file_manager.js" type="BINDATA" />
-        <include name="IDR_FILE_MANAGER_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\file_manager\file_manager.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_FILE_MANAGER_PROXY_JS" file="resources\chromeos\file_manager\browser_proxy.js" type="BINDATA" />
-
         <include name="IDR_GUEST_SESSION_TAB_HTML" file="resources\chromeos\guest_session_tab.html" flattenhtml="true" type="BINDATA" />
 
         <!-- Note: mobile_setup_ui.cc does not support compressed resources. -->
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 9b4140a..5f41a86 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -154,8 +154,6 @@
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_ui.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager.mojom.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h"
 #include "chrome/browser/ui/webui/chromeos/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
@@ -169,6 +167,8 @@
 #include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h"
 #include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h"
 #include "chromeos/components/camera_app_ui/camera_app_ui.h"
+#include "chromeos/components/file_manager/file_manager.mojom.h"
+#include "chromeos/components/file_manager/file_manager_ui.h"
 #include "chromeos/components/help_app_ui/help_app_ui.h"
 #include "chromeos/components/help_app_ui/help_app_ui.mojom.h"
 #include "chromeos/components/media_app_ui/media_app_ui.h"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d50d68cf..ab7b345 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2839,12 +2839,16 @@
 
   if (!is_official_build) {
     deps += [
+      "//chromeos/components/file_manager:file_manager_ui",
       "//chromeos/components/sample_system_web_app_ui",
       "//chromeos/components/telemetry_extension_ui",
+      "//chromeos/resources:file_manager_resources_grit",
       "//chromeos/resources:sample_system_web_app_resources_grit",
       "//chromeos/resources:telemetry_extension_resources",
     ]
     sources += [
+      "web_applications/file_manager_web_app_info.cc",
+      "web_applications/file_manager_web_app_info.h",
       "web_applications/sample_system_web_app_info.cc",
       "web_applications/sample_system_web_app_info.h",
       "web_applications/telemetry_extension_web_app_info.cc",
@@ -3873,7 +3877,9 @@
       "//chrome/browser/chromeos",
       "//chromeos/dbus:dbus",
       "//chromeos/tpm",
+      "//components:components_tests_pak",
       "//third_party/libprotobuf-mutator",
+      "//ui/resources:ui_test_pak",
     ]
   }
 
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
index 77ed102..c24fff50 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -70,7 +70,7 @@
 }
 
 bool AccessibilityNodeInfoDataWrapper::IsIgnored() const {
-  if (!tree_source_->IsScreenReaderMode())
+  if (!tree_source_->UseFullFocusMode())
     return !IsImportantInAndroid();
 
   if (!IsImportantInAndroid() || !HasImportantProperty())
@@ -294,7 +294,7 @@
 
 #undef MAP_STATE
 
-  const bool focusable = tree_source_->IsScreenReaderMode()
+  const bool focusable = tree_source_->UseFullFocusMode()
                              ? IsAccessibilityFocusableContainer()
                              : GetProperty(AXBooleanProperty::FOCUSABLE);
   if (focusable)
@@ -537,7 +537,7 @@
 
   // If a node is accessibility focusable, but has no name, the name should be
   // computed from its descendants.
-  if (names.empty() && tree_source_->IsScreenReaderMode() &&
+  if (names.empty() && tree_source_->UseFullFocusMode() &&
       IsAccessibilityFocusableContainer())
     ComputeNameFromContents(&names);
 
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
index 0429686e..1bca8b8 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
@@ -119,18 +119,16 @@
   }
 
   // AXTreeSourceArc::Delegate overrides.
-  bool IsScreenReaderEnabled() const override { return screen_reader_enabled_; }
+  bool UseFullFocusMode() const override { return full_focus_mode_; }
   void OnAction(const ui::AXActionData& data) const override {}
 
-  void set_screen_reader_mode(bool enabled) {
-    screen_reader_enabled_ = enabled;
-  }
+  void set_full_focus_mode(bool enabled) { full_focus_mode_ = enabled; }
 
   AXTreeSourceArc* tree_source() { return tree_source_.get(); }
 
  private:
   const std::unique_ptr<TestAXTreeSourceArc> tree_source_;
-  bool screen_reader_enabled_ = true;
+  bool full_focus_mode_ = true;
 };
 
 TEST_F(AccessibilityNodeInfoDataWrapperTest, Name) {
@@ -219,7 +217,7 @@
   SetProperty(&child2, AXStringProperty::TEXT, "child2 label text");
 
   // If the screen reader mode is off, do not compute from descendants.
-  set_screen_reader_mode(false);
+  set_full_focus_mode(false);
 
   ui::AXNodeData data = CallSerialize(root_wrapper);
   std::string name;
@@ -240,7 +238,7 @@
 
   // Enable screen reader.
   // Compute the name of the clickable node from descendants, and ignore them.
-  set_screen_reader_mode(true);
+  set_full_focus_mode(true);
 
   data = CallSerialize(root_wrapper);
   ASSERT_TRUE(
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 6025368..5e36c31 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -517,8 +517,8 @@
                      base::Unretained(this), data));
 }
 
-bool ArcAccessibilityHelperBridge::IsScreenReaderEnabled() const {
-  return is_screen_reader_enabled_;
+bool ArcAccessibilityHelperBridge::UseFullFocusMode() const {
+  return use_full_focus_mode_;
 }
 
 void ArcAccessibilityHelperBridge::OnTaskDestroyed(int32_t task_id) {
@@ -729,8 +729,8 @@
 
   is_focus_highlight_enabled_ =
       accessibility_manager->IsFocusHighlightEnabled();
-  is_screen_reader_enabled_ = accessibility_manager->IsSwitchAccessEnabled() ||
-                              accessibility_manager->IsSpokenFeedbackEnabled();
+  use_full_focus_mode_ = accessibility_manager->IsSwitchAccessEnabled() ||
+                         accessibility_manager->IsSpokenFeedbackEnabled();
 
   bool add_activation_observer =
       filter_type_ == arc::mojom::AccessibilityFilterType::ALL;
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 e32059f..4f7d49b 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
@@ -98,7 +98,7 @@
 
   // AXTreeSourceArc::Delegate overrides.
   void OnAction(const ui::AXActionData& data) const override;
-  bool IsScreenReaderEnabled() const override;
+  bool UseFullFocusMode() const override;
 
   // ArcAppListPrefs::Observer overrides.
   void OnTaskDestroyed(int32_t task_id) override;
@@ -178,7 +178,7 @@
 
   bool activation_observer_added_ = false;
   bool is_focus_highlight_enabled_ = false;
-  bool is_screen_reader_enabled_ = false;
+  bool use_full_focus_mode_ = false;
   Profile* const profile_;
   ArcBridgeService* const arc_bridge_service_;
   TreeMap trees_;
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
index bd9556cc..3f735624 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
@@ -121,6 +121,8 @@
     case ax::mojom::Action::kDoDefault:
       return arc::mojom::AccessibilityActionType::CLICK;
     case ax::mojom::Action::kFocus:
+      // Fallthrough
+    case ax::mojom::Action::kSetSequentialFocusNavigationStartingPoint:
       return arc::mojom::AccessibilityActionType::ACCESSIBILITY_FOCUS;
     case ax::mojom::Action::kScrollToMakeVisible:
       return arc::mojom::AccessibilityActionType::SHOW_ON_SCREEN;
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index 96831b3..f2237a6 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -81,8 +81,8 @@
   GetAutomationEventRouter()->DispatchGetTextLocationDataResult(data, rect);
 }
 
-bool AXTreeSourceArc::IsScreenReaderMode() const {
-  return delegate_->IsScreenReaderEnabled();
+bool AXTreeSourceArc::UseFullFocusMode() const {
+  return delegate_->UseFullFocusMode();
 }
 
 void AXTreeSourceArc::InvalidateTree() {
@@ -376,6 +376,7 @@
 }
 
 bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) {
+  // TODO(hirokisato): Handle CLEAR_ACCESSIBILITY_FOCUS event.
   if (event_data.event_type == AXEventType::VIEW_FOCUSED) {
     AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id);
     if (source_node && source_node->IsVisibleToUser()) {
@@ -385,6 +386,11 @@
       if (IsValid(adjusted_node))
         android_focused_id_ = adjusted_node->GetId();
     }
+  } else if (event_data.event_type == AXEventType::VIEW_ACCESSIBILITY_FOCUSED &&
+             UseFullFocusMode()) {
+    AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id);
+    if (source_node && source_node->IsVisibleToUser())
+      android_focused_id_ = source_node->GetId();
   } else if (event_data.event_type == AXEventType::VIEW_SELECTED) {
     // In Android, VIEW_SELECTED event is dispatched in the two cases below:
     // 1. Changing a value in ProgressBar or TimePicker in ARC P.
@@ -411,8 +417,23 @@
     // The event of WINDOW_STATE_CHANGED is fired only once for each window
     // change and use it as a trigger to move the a11y focus to the first node.
     AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id);
-    AccessibilityInfoDataWrapper* new_focus =
-        FindFirstFocusableNode(source_node);
+    AccessibilityInfoDataWrapper* new_focus = nullptr;
+
+    // If the current window has ever been visited in the current task, try
+    // focus on the last focus node in this window.
+    // We do it for WINDOW_STATE_CHANGED event from a window or a root node.
+    bool from_root_or_window = (source_node && !source_node->IsNode()) ||
+                               IsRootOfNodeTree(event_data.source_id);
+    auto itr = root_window_id_to_last_focus_node_id_.find(*root_id_);
+    if (from_root_or_window &&
+        itr != root_window_id_to_last_focus_node_id_.end()) {
+      new_focus = GetFromId(itr->second);
+    }
+
+    // Otherwise, try focus on the first focusable node.
+    if (!IsValid(new_focus))
+      new_focus = FindFirstFocusableNode(GetFromId(event_data.source_id));
+
     if (IsValid(new_focus))
       android_focused_id_ = new_focus->GetId();
   }
@@ -423,6 +444,12 @@
     android_focused_id_ = root_id_;
   }
 
+  if (android_focused_id_.has_value()) {
+    root_window_id_to_last_focus_node_id_[*root_id_] = *android_focused_id_;
+  } else {
+    root_window_id_to_last_focus_node_id_.erase(*root_id_);
+  }
+
   AccessibilityInfoDataWrapper* focused_node =
       android_focused_id_.has_value() ? GetFromId(*android_focused_id_)
                                       : nullptr;
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
index 61338ad..7c76dc52 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
@@ -42,7 +42,7 @@
   class Delegate {
    public:
     virtual void OnAction(const ui::AXActionData& data) const = 0;
-    virtual bool IsScreenReaderEnabled() const = 0;
+    virtual bool UseFullFocusMode() const = 0;
   };
 
   AXTreeSourceArc(Delegate* delegate, float device_scale_factor);
@@ -64,7 +64,9 @@
   // When it is enabled, this class exposes an accessibility tree optimized for
   // screen readers such as ChromeVox and SwitchAccess. This intends to have the
   // navigation order and focusabilities similar to TalkBack.
-  bool IsScreenReaderMode() const;
+  // Also, when it is enabled, the accessibility focus in Android is exposed as
+  // the focus of this tree.
+  bool UseFullFocusMode() const;
 
   // Returns true if the node id is the root of the node tree (which can have a
   // parent window).
@@ -121,10 +123,14 @@
   AccessibilityInfoDataWrapper* GetSelectedNodeInfoFromAdapterView(
       const mojom::AccessibilityEventData& event_data) const;
 
-  // Update android_focused_id_ from given AccessibilityEventData.
-  // Returns true if it is successfully updated to existing node.
-  // Returns false if we don't dispatch the processing event to chrome
-  // automation.
+  // Updates android_focused_id_ from given AccessibilityEventData.
+  // Having this method, |android_focused_id_| is one of these:
+  // - input focus in Android
+  // - accessibility focus in Android
+  // - the chrome automation client's internal focus (via set sequential focus
+  //   action and replying accessibility focus event from Android).
+  // This returns false if we don't want to dispatch the processing
+  // event to chrome automation. Otherwise, this returns true.
   bool UpdateAndroidFocusedId(const mojom::AccessibilityEventData& event_data);
 
   void UpdateAXNameCache(AccessibilityInfoDataWrapper* source_node,
@@ -173,6 +179,9 @@
   std::map<int32_t, std::string> cached_names_;
   std::map<int32_t, ax::mojom::Role> cached_roles_;
 
+  // Cache of mapping from the root window id to the last focused node id.
+  std::map<int32_t, int32_t> root_window_id_to_last_focus_node_id_;
+
   // Mapping from Chrome node ID to its cached computed bounds.
   // This simplifies bounds calculations.
   std::map<int32_t, gfx::Rect> computed_bounds_;
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index b9f9eca..6c0b73e2 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -198,11 +198,9 @@
     EXPECT_EQ(expected, tree_text.substr(first_new_line));
   }
 
-  void set_screen_reader_mode(bool enabled) {
-    screen_reader_enabled_ = enabled;
-  }
+  void set_full_focus_mode(bool enabled) { full_focus_mode_ = enabled; }
 
-  bool IsScreenReaderEnabled() const override { return screen_reader_enabled_; }
+  bool UseFullFocusMode() const override { return full_focus_mode_; }
 
  private:
   void OnAction(const ui::AXActionData& data) const override {}
@@ -210,7 +208,7 @@
   const std::unique_ptr<MockAutomationEventRouter> router_;
   const std::unique_ptr<AXTreeSourceArc> tree_source_;
 
-  bool screen_reader_enabled_ = false;
+  bool full_focus_mode_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(AXTreeSourceArcTest);
 };
@@ -715,9 +713,7 @@
 
 TEST_F(AXTreeSourceArcTest, OnWindowStateChangedEvent) {
   auto event = AXEventData::New();
-  event->source_id = 1;  // node1.
   event->task_id = 1;
-  event->event_type = AXEventType::WINDOW_STATE_CHANGED;
 
   event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
   event->window_data->push_back(AXWindowInfoData::New());
@@ -735,7 +731,8 @@
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* node1 = event->node_data.back().get();
   node1->id = 1;
-  SetProperty(node1, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({2}));
+  SetProperty(node1, AXIntListProperty::CHILD_NODE_IDS,
+              std::vector<int>({2, 3}));
   SetProperty(node1, AXBooleanProperty::IMPORTANCE, true);
   SetProperty(node1, AXBooleanProperty::VISIBLE_TO_USER, true);
 
@@ -744,16 +741,63 @@
   node2->id = 2;
   SetProperty(node2, AXBooleanProperty::IMPORTANCE, true);
   SetProperty(node2, AXBooleanProperty::VISIBLE_TO_USER, true);
-  SetProperty(node2, AXStringProperty::TEXT, "sample string.");
+  SetProperty(node2, AXStringProperty::TEXT, "sample string node2.");
 
+  event->node_data.push_back(AXNodeInfoData::New());
+  AXNodeInfoData* node3 = event->node_data.back().get();
+  node3->id = 3;
+  SetProperty(node3, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(node3, AXBooleanProperty::VISIBLE_TO_USER, true);
+  SetProperty(node3, AXStringProperty::TEXT, "sample string node3.");
+
+  // Focus will be on the first accessible node (node2).
+  event->event_type = AXEventType::WINDOW_STATE_CHANGED;
+  event->source_id = root->id;
   CallNotifyAccessibilityEvent(event.get());
   ui::AXTreeData data;
 
-  // Focus is now at the first accessible node (node2).
   EXPECT_TRUE(CallGetTreeData(&data));
   EXPECT_EQ(node2->id, data.focus_id);
 
-  EXPECT_EQ(1, GetDispatchedEventCount(ax::mojom::Event::kFocus));
+  // focus moved to node3 for some reason.
+  event->event_type = AXEventType::VIEW_FOCUSED;
+  event->source_id = node3->id;
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(node3->id, data.focus_id);
+
+  // after moved the focus on the window, keep the same focus on
+  // WINDOW_STATE_CHANGED event.
+  event->event_type = AXEventType::WINDOW_STATE_CHANGED;
+  event->source_id = root->id;
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(node3->id, data.focus_id);
+
+  // Simulate opening another window in this task.
+  // This is the same as new WINDOW_STATE_CHANGED event, so focus is at the
+  // first accessible node (node2).
+  root_window->window_id = 200;
+  event->event_type = AXEventType::WINDOW_STATE_CHANGED;
+  event->source_id = node1->id;
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(node2->id, data.focus_id);
+
+  // Simulate closing the second window and coming back to the first window.
+  // The focus back to the last focus node, which is node3.
+  root_window->window_id = 100;
+  event->event_type = AXEventType::WINDOW_STATE_CHANGED;
+  event->source_id = root->id;
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(node3->id, data.focus_id);
+
+  EXPECT_EQ(5, GetDispatchedEventCount(ax::mojom::Event::kFocus));
 }
 
 TEST_F(AXTreeSourceArcTest, OnFocusEvent) {
@@ -801,14 +845,26 @@
   EXPECT_TRUE(CallGetTreeData(&data));
   EXPECT_EQ(node2->id, data.focus_id);
 
-  // Chrome should focus to node2, even if Android sends focus on List.
+  // Chrome should focus to node1, even if Android sends focus on List.
   event->source_id = root->id;
   CallNotifyAccessibilityEvent(event.get());
 
   EXPECT_TRUE(CallGetTreeData(&data));
   EXPECT_EQ(node1->id, data.focus_id);
 
-  EXPECT_EQ(2, GetDispatchedEventCount(ax::mojom::Event::kFocus));
+  // VIEW_ACCESSIBILITY_FOCUSED event also updates the focus in screen reader
+  // mode.
+  set_full_focus_mode(true);
+  SetProperty(node1, AXBooleanProperty::ACCESSIBILITY_FOCUSED, false);
+  SetProperty(node2, AXBooleanProperty::ACCESSIBILITY_FOCUSED, true);
+  event->event_type = AXEventType::VIEW_ACCESSIBILITY_FOCUSED;
+  event->source_id = node2->id;
+  CallNotifyAccessibilityEvent(event.get());
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(node2->id, data.focus_id);
+
+  EXPECT_EQ(3, GetDispatchedEventCount(ax::mojom::Event::kFocus));
 }
 
 TEST_F(AXTreeSourceArcTest, OnDrawerOpened) {
@@ -912,7 +968,7 @@
   // |node2| is ignored by default because
   // AXBooleanProperty::IMPORTANCE has a default false value.
 
-  set_screen_reader_mode(true);
+  set_full_focus_mode(true);
 
   CallNotifyAccessibilityEvent(event.get());
   EXPECT_EQ(1, GetDispatchedEventCount(ax::mojom::Event::kFocus));
diff --git a/chrome/browser/chromeos/concierge_helper_service.cc b/chrome/browser/chromeos/concierge_helper_service.cc
index 38b98eb..8abed5d 100644
--- a/chrome/browser/chromeos/concierge_helper_service.cc
+++ b/chrome/browser/chromeos/concierge_helper_service.cc
@@ -18,13 +18,6 @@
 namespace chromeos {
 namespace {
 
-void OnStartConcierge(bool started) {
-  if (started)
-    VLOG(1) << "Concierge D-Bus service successfully started";
-  else
-    LOG(ERROR) << "Unable to start Concierge D-Bus service";
-}
-
 void OnSetVmCpuRestriction(
     base::Optional<vm_tools::concierge::SetVmCpuRestrictionResponse> response) {
   if (!response || !response->success()) {
@@ -33,18 +26,6 @@
   }
 }
 
-// Starts Concierge DBus service through debugd. If the service is already
-// running, this request will be ignored.
-void StartConcierge() {
-  auto* client = DBusThreadManager::Get()->GetDebugDaemonClient();
-  if (!client) {
-    LOG(WARNING) << "DebugDaemonClient is not available";
-    OnStartConcierge(false);
-    return;
-  }
-  client->StartConcierge(base::BindOnce(&OnStartConcierge));
-}
-
 // Adds a callback to be run when Concierge DBus service becomes available.
 // If the service is already available, runs the callback immediately.
 void WaitForConciergeToBeAvailable(
@@ -103,9 +84,7 @@
   return ConciergeHelperServiceFactory::GetForBrowserContext(context);
 }
 
-ConciergeHelperService::ConciergeHelperService() {
-  StartConcierge();
-}
+ConciergeHelperService::ConciergeHelperService() = default;
 
 void ConciergeHelperService::SetArcVmCpuRestriction(bool do_restrict) {
   MakeRestrictionRequest(vm_tools::concierge::CPU_CGROUP_ARCVM, do_restrict);
diff --git a/chrome/browser/chromeos/concierge_helper_service_unittest.cc b/chrome/browser/chromeos/concierge_helper_service_unittest.cc
index 9d5f2a9e..fac19b2e 100644
--- a/chrome/browser/chromeos/concierge_helper_service_unittest.cc
+++ b/chrome/browser/chromeos/concierge_helper_service_unittest.cc
@@ -11,7 +11,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
 #include "chromeos/dbus/fake_concierge_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "dbus/bus.h"
@@ -20,24 +19,6 @@
 
 namespace chromeos {
 
-class TestDebugDaemonClient : public FakeDebugDaemonClient {
- public:
-  TestDebugDaemonClient() = default;
-  ~TestDebugDaemonClient() override = default;
-
-  void StartConcierge(ConciergeCallback callback) override {
-    ++start_concierge_called_;
-    FakeDebugDaemonClient::StartConcierge(std::move(callback));
-  }
-
-  size_t start_concierge_called() const { return start_concierge_called_; }
-
- private:
-  size_t start_concierge_called_{0};
-
-  DISALLOW_COPY_AND_ASSIGN(TestDebugDaemonClient);
-};
-
 class TestConciergeClient : public FakeConciergeClient {
  public:
   TestConciergeClient() = default;
@@ -96,7 +77,6 @@
   // testing::Test:
   void SetUp() override {
     auto setter = DBusThreadManager::GetSetterForTesting();
-    setter->SetDebugDaemonClient(std::make_unique<TestDebugDaemonClient>());
     setter->SetConciergeClient(std::make_unique<TestConciergeClient>());
     service_ = ConciergeHelperService::GetForBrowserContext(&profile_);
   }
@@ -111,11 +91,6 @@
         DBusThreadManager::Get()->GetConciergeClient());
   }
 
-  TestDebugDaemonClient* fake_debug_client() {
-    return static_cast<TestDebugDaemonClient*>(
-        DBusThreadManager::Get()->GetDebugDaemonClient());
-  }
-
   content::BrowserTaskEnvironment* task_environment() {
     return &task_environment_;
   }
@@ -128,12 +103,6 @@
   DISALLOW_COPY_AND_ASSIGN(ConciergeHelperServiceTest);
 };
 
-// Tests that ConciergeHelperService can be constructed and destructed. Also,
-// check that ConciergeHelperService starts Concierge on construction.
-TEST_F(ConciergeHelperServiceTest, TestConstructDestruct) {
-  EXPECT_EQ(1U, fake_debug_client()->start_concierge_called());
-}
-
 // Tests that ConciergeHelperService makes cpu restriction requests correctly.
 TEST_F(ConciergeHelperServiceTest, TestSetVmCpuRestriction) {
   vm_tools::concierge::SetVmCpuRestrictionResponse response;
diff --git a/chrome/browser/chromeos/crostini/crostini_disk.cc b/chrome/browser/chromeos/crostini/crostini_disk.cc
index 225174f..8ea4a8c6 100644
--- a/chrome/browser/chromeos/crostini/crostini_disk.cc
+++ b/chrome/browser/chromeos/crostini/crostini_disk.cc
@@ -73,26 +73,15 @@
         base::BindOnce(&OnAmountOfFreeDiskSpace, std::move(callback), profile,
                        std::move(vm_name)));
   } else {
-    VLOG(1) << "Starting concierge";
     // Since we only care about the disk's current size and whether it's a
     // sparse disk, we claim there's plenty of free space available to prevent
     // error conditions in |OnCrostiniSufficientlyRunning|.
     constexpr int64_t kFakeAvailableDiskBytes =
         kDiskHeadroomBytes + kRecommendedDiskSizeBytes;
 
-    CrostiniManager::GetForProfile(profile)->StartConcierge(base::BindOnce(
-        [](OnceDiskInfoCallback callback, Profile* profile, std::string vm_name,
-           bool success) {
-          if (!success) {
-            LOG(ERROR) << "Failed to start concierge";
-            std::move(callback).Run(nullptr);
-            return;
-          }
-          OnCrostiniSufficientlyRunning(
-              std::move(callback), profile, std::move(vm_name),
-              kFakeAvailableDiskBytes, CrostiniResult::SUCCESS);
-        },
-        std::move(callback), profile, std::move(vm_name)));
+    OnCrostiniSufficientlyRunning(std::move(callback), profile,
+                                  std::move(vm_name), kFakeAvailableDiskBytes,
+                                  CrostiniResult::SUCCESS);
   }
 }
 
diff --git a/chrome/browser/chromeos/crostini/crostini_installer.cc b/chrome/browser/chromeos/crostini/crostini_installer.cc
index 8de0de66..9faad08a 100644
--- a/chrome/browser/chromeos/crostini/crostini_installer.cc
+++ b/chrome/browser/chromeos/crostini/crostini_installer.cc
@@ -113,8 +113,6 @@
       return SetupResult::kSuccess;
     case InstallerError::kErrorLoadingTermina:
       return SetupResult::kErrorLoadingTermina;
-    case InstallerError::kErrorStartingConcierge:
-      return SetupResult::kErrorStartingConcierge;
     case InstallerError::kErrorCreatingDiskImage:
       return SetupResult::kErrorCreatingDiskImage;
     case InstallerError::kErrorStartingTermina:
@@ -149,8 +147,6 @@
       return SetupResult::kUserCancelledStart;
     case InstallerState::kInstallImageLoader:
       return SetupResult::kUserCancelledInstallImageLoader;
-    case InstallerState::kStartConcierge:
-      return SetupResult::kUserCancelledStartConcierge;
     case InstallerState::kCreateDiskImage:
       return SetupResult::kUserCancelledCreateDiskImage;
     case InstallerState::kStartTerminaVm:
@@ -326,15 +322,6 @@
     }
     return;
   }
-  UpdateInstallingState(InstallerState::kStartConcierge);
-}
-
-void CrostiniInstaller::OnConciergeStarted(bool success) {
-  DCHECK_EQ(installing_state_, InstallerState::kStartConcierge);
-  if (!success) {
-    HandleError(InstallerError::kErrorStartingConcierge);
-    return;
-  }
   UpdateInstallingState(InstallerState::kCreateDiskImage);
 }
 
@@ -499,12 +486,8 @@
       state_end_mark = 0.20;
       state_max_time = base::TimeDelta::FromSeconds(30);
       break;
-    case InstallerState::kStartConcierge:
-      state_start_mark = 0.20;
-      state_end_mark = 0.21;
-      break;
     case InstallerState::kCreateDiskImage:
-      state_start_mark = 0.21;
+      state_start_mark = 0.20;
       state_end_mark = 0.22;
       break;
     case InstallerState::kStartTerminaVm:
diff --git a/chrome/browser/chromeos/crostini/crostini_installer.h b/chrome/browser/chromeos/crostini/crostini_installer.h
index ce26ed5..ce38ef5f 100644
--- a/chrome/browser/chromeos/crostini/crostini_installer.h
+++ b/chrome/browser/chromeos/crostini/crostini_installer.h
@@ -37,7 +37,7 @@
     // kUserCancelled = 1,
     kSuccess = 2,
     kErrorLoadingTermina = 3,
-    kErrorStartingConcierge = 4,
+    // kErrorStartingConcierge = 4,
     kErrorCreatingDiskImage = 5,
     kErrorStartingTermina = 6,
     kErrorStartingContainer = 7,
@@ -48,7 +48,7 @@
 
     kUserCancelledStart = 12,
     kUserCancelledInstallImageLoader = 13,
-    kUserCancelledStartConcierge = 14,
+    // kUserCancelledStartConcierge = 14,
     kUserCancelledCreateDiskImage = 15,
     kUserCancelledStartTerminaVm = 16,
     kUserCancelledCreateContainer = 17,
@@ -88,7 +88,6 @@
   // CrostiniManager::RestartObserver:
   void OnStageStarted(crostini::mojom::InstallerState stage) override;
   void OnComponentLoaded(crostini::CrostiniResult result) override;
-  void OnConciergeStarted(bool success) override;
   void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 903c98f..7a7fce0 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -333,23 +333,6 @@
     }
     // Set the pref here, after we first successfully install something
     profile_->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
-    StartStage(mojom::InstallerState::kStartConcierge);
-    crostini_manager_->StartConcierge(base::BindOnce(
-        &CrostiniRestarter::ConciergeStarted, weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void ConciergeStarted(bool is_started) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    for (auto& observer : observer_list_) {
-      observer.OnConciergeStarted(is_started);
-    }
-    if (ReturnEarlyIfAborted()) {
-      return;
-    }
-    if (!is_started) {
-      FinishRestart(CrostiniResult::CONCIERGE_START_FAILED);
-      return;
-    }
 
     // Allow concierge to choose an appropriate disk image size.
     int64_t disk_size_bytes = options_.disk_size_bytes.value_or(0);
@@ -1044,34 +1027,6 @@
   termina_installer_.Uninstall(std::move(callback));
 }
 
-void CrostiniManager::StartConcierge(BoolCallback callback) {
-  VLOG(1) << "Starting Concierge service";
-  chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartConcierge(
-      base::BindOnce(&CrostiniManager::OnStartConcierge,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void CrostiniManager::OnStartConcierge(BoolCallback callback, bool success) {
-  if (!success) {
-    LOG(ERROR) << "Failed to start Concierge service";
-    std::move(callback).Run(success);
-    return;
-  }
-  VLOG(1) << "Concierge service started";
-  VLOG(1) << "Waiting for Cicerone to announce availability.";
-
-  GetCiceroneClient()->WaitForServiceToBeAvailable(std::move(callback));
-}
-
-void CrostiniManager::OnStopConcierge(BoolCallback callback, bool success) {
-  if (!success) {
-    LOG(ERROR) << "Failed to stop Concierge service";
-  } else {
-    VLOG(1) << "Concierge service stopped";
-  }
-  std::move(callback).Run(success);
-}
-
 void CrostiniManager::CreateDiskImage(
     const base::FilePath& disk_path,
     vm_tools::concierge::StorageLocation storage_location,
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index 459f9c816..1ff400d 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -189,7 +189,6 @@
     virtual ~RestartObserver() {}
     virtual void OnStageStarted(mojom::InstallerState stage) {}
     virtual void OnComponentLoaded(CrostiniResult result) {}
-    virtual void OnConciergeStarted(bool success) {}
     virtual void OnDiskImageCreated(bool success,
                                     vm_tools::concierge::DiskImageStatus status,
                                     int64_t disk_size_bytes) {}
@@ -243,10 +242,6 @@
   // Unloads and removes termina.
   void UninstallTermina(BoolCallback callback);
 
-  // Starts the Concierge service. |callback| is called after the method call
-  // finishes.
-  void StartConcierge(BoolCallback callback);
-
   // Checks the arguments for creating a new Termina VM disk image. Creates a
   // disk image for a Termina VM via ConciergeClient::CreateDiskImage.
   // |callback| is called if the arguments are bad, or after the method call
@@ -703,14 +698,6 @@
       base::Optional<vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>
           response);
 
-  // Callback for CrostiniClient::StartConcierge. Called after the
-  // DebugDaemon service method finishes.
-  void OnStartConcierge(BoolCallback callback, bool success);
-
-  // Callback for CrostiniClient::StopConcierge. Called after the
-  // DebugDaemon service method finishes.
-  void OnStopConcierge(BoolCallback callback, bool success);
-
   // Callback for CiceroneClient::StartLxd. May indicate that LXD is still being
   // started in which case we will wait for OnStartLxdProgress events.
   void OnStartLxd(
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index c80358bec..d694b35 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -645,12 +645,6 @@
     }
   }
 
-  void OnConciergeStarted(bool success) override {
-    if (abort_on_concierge_started_) {
-      Abort();
-    }
-  }
-
   void OnDiskImageCreated(bool success,
                           vm_tools::concierge::DiskImageStatus status,
                           int64_t disk_size_available) override {
@@ -753,7 +747,6 @@
   const CrostiniManager::RestartId uninitialized_id_ =
       CrostiniManager::kUninitializedRestartId;
   bool abort_on_component_loaded_ = false;
-  bool abort_on_concierge_started_ = false;
   bool abort_on_disk_image_created_ = false;
   bool abort_on_vm_started_ = false;
   bool abort_on_container_created_ = false;
@@ -864,21 +857,6 @@
   ExpectRestarterUmaCount(1);
 }
 
-TEST_F(CrostiniManagerRestartTest, AbortOnConciergeStarted) {
-  abort_on_concierge_started_ = true;
-  restart_id_ = crostini_manager()->RestartCrostini(
-      container_id(),
-      base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
-                     base::Unretained(this), run_loop()->QuitClosure()),
-      this);
-  run_loop()->Run();
-  EXPECT_FALSE(fake_concierge_client_->create_disk_image_called());
-  EXPECT_FALSE(fake_concierge_client_->start_termina_vm_called());
-  EXPECT_FALSE(fake_concierge_client_->get_container_ssh_keys_called());
-  ExpectCrostiniRestartResult(CrostiniResult::RESTART_ABORTED);
-  ExpectRestarterUmaCount(1);
-}
-
 TEST_F(CrostiniManagerRestartTest, AbortOnDiskImageCreated) {
   abort_on_disk_image_created_ = true;
   restart_id_ = crostini_manager()->RestartCrostini(
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc
index e3133ab..ed92fb8 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.cc
+++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -34,15 +34,6 @@
 
 void CrostiniRemover::RemoveCrostini() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  CrostiniManager::GetForProfile(profile_)->StartConcierge(
-      base::BindOnce(&CrostiniRemover::OnConciergeStarted, this));
-}
-
-void CrostiniRemover::OnConciergeStarted(bool is_successful) {
-  if (!is_successful) {
-    std::move(callback_).Run(CrostiniResult::UNKNOWN_ERROR);
-    return;
-  }
   CrostiniManager::GetForProfile(profile_)->StopVm(
       vm_name_, base::BindOnce(&CrostiniRemover::StopVmFinished, this));
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.h b/chrome/browser/chromeos/crostini/crostini_remover.h
index afc43327..89c92c5 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.h
+++ b/chrome/browser/chromeos/crostini/crostini_remover.h
@@ -22,7 +22,6 @@
 
   ~CrostiniRemover();
 
-  void OnConciergeStarted(bool is_successful);
   void StopVmFinished(crostini::CrostiniResult result);
   void DestroyDiskImageFinished(bool success);
   void UninstallTerminaFinished(bool success);
diff --git a/chrome/browser/chromeos/crostini/crostini_types.mojom b/chrome/browser/chromeos/crostini/crostini_types.mojom
index 65efb5c..56420cda 100644
--- a/chrome/browser/chromeos/crostini/crostini_types.mojom
+++ b/chrome/browser/chromeos/crostini/crostini_types.mojom
@@ -7,7 +7,6 @@
 enum InstallerState {
   kStart,               // Just started installation
   kInstallImageLoader,  // Loading the Termina VM component.
-  kStartConcierge,      // Starting the Concierge D-Bus client.
   kCreateDiskImage,     // Creating the image for the Termina VM.
   kStartTerminaVm,      // Starting the Termina VM.
   kCreateContainer,     // Creating the container inside the Termina VM.
@@ -21,7 +20,6 @@
 enum InstallerError {
   kNone,
   kErrorLoadingTermina,
-  kErrorStartingConcierge,
   kErrorCreatingDiskImage,
   kErrorStartingTermina,
   kErrorStartingContainer,
diff --git a/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc b/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
index 000fc14..fab9677 100644
--- a/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
+++ b/chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.cc
@@ -4,14 +4,17 @@
 
 #include <string>
 
+#include "base/at_exit.h"
 #include "base/check_op.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/i18n/icu_util.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/path_service.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "chrome/browser/chromeos/dbus/dbus_helper.h"
 #include "chrome/browser/chromeos/policy/device_policy_decoder_chromeos.h"
 #include "chrome/browser/chromeos/policy/fuzzer/policy_fuzzer.pb.h"
@@ -31,6 +34,8 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/prefs/pref_value_map.h"
 #include "testing/libfuzzer/proto/lpm_interface.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
 
 namespace policy {
 
@@ -44,9 +49,23 @@
     CHECK(base::PathService::Override(chrome::DIR_USER_DATA,
                                       scoped_temp_dir.GetPath()));
     CHECK(base::i18n::InitializeICU());
+
+    ui::RegisterPathProvider();
+
+    base::FilePath ui_test_pak_path =
+        base::PathService::CheckedGet(ui::UI_TEST_PAK);
+    ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+
+    base::FilePath pak_path = base::PathService::CheckedGet(base::DIR_MODULE);
+    ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        pak_path.AppendASCII("components_tests_resources.pak"),
+        ui::SCALE_FACTOR_NONE);
   }
 
+  ~Environment() { ui::ResourceBundle::CleanupSharedInstance(); }
+
   base::ScopedTempDir scoped_temp_dir;
+  base::AtExitManager exit_manager;
 };
 
 struct PerInputEnvironment {
diff --git a/chrome/browser/chromeos/web_applications/file_manager_web_app_info.cc b/chrome/browser/chromeos/web_applications/file_manager_web_app_info.cc
new file mode 100644
index 0000000..a19d544
--- /dev/null
+++ b/chrome/browser/chromeos/web_applications/file_manager_web_app_info.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/web_applications/file_manager_web_app_info.h"
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/web_applications/system_web_app_install_utils.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/common/web_application_info.h"
+#include "chromeos/components/file_manager/url_constants.h"
+#include "chromeos/grit/chromeos_file_manager_resources.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
+
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForFileManager() {
+  auto info = std::make_unique<WebApplicationInfo>();
+  info->app_url = GURL(chromeos::file_manager::kChromeUIFileManagerURL);
+  info->scope = GURL(chromeos::file_manager::kChromeUIFileManagerURL);
+  // TODO(majewski): Fetch from a resource.
+  info->title = base::UTF8ToUTF16("File Manager");
+  web_app::CreateIconInfoForSystemWebApp(
+      info->app_url, {{"icon192.png", 192, IDR_FILE_MANAGER_ICON_192}}, *info);
+  info->theme_color = 0xFF4285F4;
+  info->background_color = 0xFFFFFFFF;
+  info->display_mode = blink::mojom::DisplayMode::kStandalone;
+  info->open_as_window = true;
+
+  return info;
+}
diff --git a/chrome/browser/chromeos/web_applications/file_manager_web_app_info.h b/chrome/browser/chromeos/web_applications/file_manager_web_app_info.h
new file mode 100644
index 0000000..4431b20
--- /dev/null
+++ b/chrome/browser/chromeos/web_applications/file_manager_web_app_info.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_FILE_MANAGER_WEB_APP_INFO_H_
+#define CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_FILE_MANAGER_WEB_APP_INFO_H_
+
+#include <memory>
+
+#if defined(OFFICIAL_BUILD)
+#error File Manager should only be included in unofficial builds.
+#endif
+
+struct WebApplicationInfo;
+
+// Return a WebApplicationInfo used to install the app.
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForFileManager();
+
+#endif  // CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_FILE_MANAGER_WEB_APP_INFO_H_
diff --git a/chrome/browser/chromeos/web_applications/media_app_integration_browsertest.cc b/chrome/browser/chromeos/web_applications/media_app_integration_browsertest.cc
index b88f14c..aaca4c39 100644
--- a/chrome/browser/chromeos/web_applications/media_app_integration_browsertest.cc
+++ b/chrome/browser/chromeos/web_applications/media_app_integration_browsertest.cc
@@ -21,9 +21,9 @@
 #include "chromeos/components/media_app_ui/test/media_app_ui_browsertest.h"
 #include "chromeos/components/media_app_ui/url_constants.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "extensions/browser/api/crash_report_private/mock_crash_endpoint.h"
 #include "extensions/browser/entry_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
diff --git a/chrome/browser/extensions/api/terminal/crostini_startup_status.cc b/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
index 46b9d08..ccdbe0e7 100644
--- a/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
+++ b/chrome/browser/extensions/api/terminal/crostini_startup_status.cc
@@ -100,9 +100,6 @@
           {InstallerState::kInstallImageLoader,
            l10n_util::GetStringUTF8(
                IDS_CROSTINI_TERMINAL_STATUS_INSTALL_IMAGE_LOADER)},
-          {InstallerState::kStartConcierge,
-           l10n_util::GetStringUTF8(
-               IDS_CROSTINI_TERMINAL_STATUS_START_CONCIERGE)},
           {InstallerState::kCreateDiskImage,
            l10n_util::GetStringUTF8(
                IDS_CROSTINI_TERMINAL_STATUS_CREATE_DISK_IMAGE)},
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a22459e..4b61ffa 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -200,6 +200,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "ash-enable-interactive-window-cycle-list",
+    "owners": [ "chinsenj", "tclaiborne" ],
+    "expiry_milestone": 91
+  },
+  {
     "name": "ash-enable-pip-rounded-corners",
     "owners": [ "edcourtney" ],
     "expiry_milestone": 90
@@ -1649,6 +1654,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-holding-space",
+    "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "enable-home-page-location-policy",
     "owners": [ "wenyufu", "chrome-android-app@chromium.org" ],
     "expiry_milestone": 83
@@ -2189,6 +2199,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "enable-tab-search-fixed-entrypoint",
+    "owners": [ "tluk@google.com", "robliao@google.com" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "enable-tab-switcher-on-return",
     "owners": [ "memex-team@google.com" ],
     "expiry_milestone": 90
@@ -3326,6 +3341,11 @@
     "expiry_milestone": 95
   },
   {
+    "name": "omnibox-bubble-url-suggestions",
+    "owners": [ "manukh", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "omnibox-clobber-triggers-contextual-web-zero-suggest",
     "owners": [ "tommycli", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 95
@@ -4289,6 +4309,11 @@
     "expiry_milestone": 88
   },
   {
+    "name": "sync-autofill-wallet-offer-data",
+    "owners": ["siyua", "treib"],
+    "expiry_milestone": 90
+  },
+  {
     "name": "sync-setup-friendly-settings",
     "owners": [ "msalama" ],
     "expiry_milestone": 84
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ff5c05c..7fc0c82 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1008,8 +1008,14 @@
 #if BUILDFLAG(ENABLE_TAB_SEARCH)
 const char kEnableTabSearchName[] = "Enable Tab Search";
 const char kEnableTabSearchDescription[] =
-    "Enable the Tab Search feature in Top Chrome UI, which will show a popup"
+    "Enable the Tab Search feature in Top Chrome UI, which will show a popup "
     "bubble that allows users to search over their currently open tabs.";
+const char kEnableTabSearchFixedEntrypointName[] =
+    "Enable Tab Search fixed "
+    "entrypoint";
+const char kEnableTabSearchFixedEntrypointDescription[] =
+    "Enable the Tab "
+    "Search feature in Top Chrome UI having a fixed button position.";
 #endif  // BUILDFLAG(ENABLE_TAB_SEARCH)";
 
 const char kEnableTextFragmentAnchorName[] = "Enable Text Fragment Anchor.";
@@ -1701,6 +1707,12 @@
     "Configures the maximum number of autocomplete matches displayed in the "
     "Omnibox UI dynamically based on the number of URL matches.";
 
+const char kOmniboxBubbleUrlSuggestionsName[] =
+    "Omnibox Bubble URL Suggestions";
+const char kOmniboxBubbleUrlSuggestionsDescription[] =
+    "Configures the bubbling of URL suggestions after grouping searches "
+    "above URLs.";
+
 const char kOmniboxOnDeviceHeadSuggestionsIncognitoName[] =
     "Omnibox on device head suggestions (incognito only)";
 const char kOmniboxOnDeviceHeadSuggestionsIncognitoDescription[] =
@@ -1851,12 +1863,6 @@
     "Forces wheel, and mousewheel event listeners on document level targets "
     "(which haven't requested otherwise) to be treated as passive.";
 
-const char kPassiveMixedContentWarningName[] =
-    "Warning for Passive Mixed Content";
-const char kPassiveMixedContentWarningDescription[] =
-    "Causes a 'Not Secure' chip to be shown in the omnibox if a site contains "
-    "passive (aka optionally blockable) mixed content.";
-
 const char kPasswordChangeInSettingsName[] =
     "Rework password change flow from settings";
 const char kPasswordChangeInSettingsDescription[] =
@@ -2241,6 +2247,11 @@
     "Match Autofill suggestions based on substrings (token prefixes) rather "
     "than just prefixes.";
 
+const char kSyncAutofillWalletOfferDataName[] =
+    "Enable syncing autofill offer data";
+const char kSyncAutofillWalletOfferDataDescription[] =
+    "When enabled, allows syncing autofill wallet offer data type.";
+
 const char kSyncDeviceInfoInTransportModeName[] =
     "Enable syncing DeviceInfo in transport-only sync mode.";
 const char kSyncDeviceInfoInTransportModeDescription[] =
@@ -4133,6 +4144,14 @@
     "Hides media notifications for ARC apps. Requires "
     "#enable-media-session-notifications to be enabled.";
 
+const char kHoldingSpaceName[] =
+    "Quick access to screenshots, downloads, and files test";
+const char kHoldingSpaceDescription[] =
+    "Enables quick access to screenshots, downloads, and important files which "
+    "aims to increase productivity by saving time. When enabled, access recent "
+    "screenshots and downloads from the shelf. Pin important files with the "
+    "Files App context menu to keep them one click away.";
+
 const char kImeAssistAutocorrectName[] = "Enable assistive autocorrect";
 const char kImeAssistAutocorrectDescription[] =
     "Enable assistive auto-correct features for native IME";
@@ -4175,6 +4194,12 @@
 const char kIntentPickerPWAPersistenceDescription[] =
     "Allow user to always open with PWA in intent picker.";
 
+const char kInteractiveWindowCycleList[] =
+    "Enable Alt-Tab interactivity improvements.";
+const char kInteractiveWindowCycleListDescription[] =
+    "Adds mouse behavior, three-finger touchpad swipe, left/right "
+    "arrow navigation, and space/enter confirmation to Alt-Tab.";
+
 const char kLacrosSupportName[] = "LaCrOS support";
 const char kLacrosSupportDescription[] =
     "Support for the experimental lacros-chrome browser.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7624291..a1bf42f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -585,6 +585,8 @@
 #if BUILDFLAG(ENABLE_TAB_SEARCH)
 extern const char kEnableTabSearchName[];
 extern const char kEnableTabSearchDescription[];
+extern const char kEnableTabSearchFixedEntrypointName[];
+extern const char kEnableTabSearchFixedEntrypointDescription[];
 #endif  // BUILDFLAG(ENABLE_TAB_SEARCH)
 
 extern const char kEnableTextFragmentAnchorName[];
@@ -981,6 +983,9 @@
 extern const char kOmniboxDynamicMaxAutocompleteName[];
 extern const char kOmniboxDynamicMaxAutocompleteDescription[];
 
+extern const char kOmniboxBubbleUrlSuggestionsName[];
+extern const char kOmniboxBubbleUrlSuggestionsDescription[];
+
 extern const char kOmniboxOnDeviceHeadSuggestionsIncognitoName[];
 extern const char kOmniboxOnDeviceHeadSuggestionsIncognitoDescription[];
 
@@ -1072,9 +1077,6 @@
 extern const char kPassiveDocumentWheelEventListenersName[];
 extern const char kPassiveDocumentWheelEventListenersDescription[];
 
-extern const char kPassiveMixedContentWarningName[];
-extern const char kPassiveMixedContentWarningDescription[];
-
 extern const char kPasswordImportName[];
 extern const char kPasswordImportDescription[];
 
@@ -1288,6 +1290,9 @@
 extern const char kSuggestionsWithSubStringMatchName[];
 extern const char kSuggestionsWithSubStringMatchDescription[];
 
+extern const char kSyncAutofillWalletOfferDataName[];
+extern const char kSyncAutofillWalletOfferDataDescription[];
+
 extern const char kSyncDeviceInfoInTransportModeName[];
 extern const char kSyncDeviceInfoInTransportModeDescription[];
 
@@ -2398,6 +2403,9 @@
 extern const char kHideArcMediaNotificationsName[];
 extern const char kHideArcMediaNotificationsDescription[];
 
+extern const char kHoldingSpaceName[];
+extern const char kHoldingSpaceDescription[];
+
 extern const char kImeAssistAutocorrectName[];
 extern const char kImeAssistAutocorrectDescription[];
 
@@ -2428,6 +2436,9 @@
 extern const char kIntentPickerPWAPersistenceName[];
 extern const char kIntentPickerPWAPersistenceDescription[];
 
+extern const char kInteractiveWindowCycleList[];
+extern const char kInteractiveWindowCycleListDescription[];
+
 extern const char kLacrosSupportName[];
 extern const char kLacrosSupportDescription[];
 
diff --git a/chrome/browser/metrics/family_user_metrics_provider.cc b/chrome/browser/metrics/family_user_metrics_provider.cc
index 82e2516..1804dc1c 100644
--- a/chrome/browser/metrics/family_user_metrics_provider.cc
+++ b/chrome/browser/metrics/family_user_metrics_provider.cc
@@ -31,6 +31,12 @@
       g_browser_process->platform_part()
           ->GetAccountManagerFactory()
           ->GetAccountManager(profile->GetPath().value());
+  DCHECK(account_manager);
+  if (!account_manager->IsInitialized()) {
+    base::UmaHistogramEnumeration(kFamilyUserLogSegmentHistogramName,
+                                  LogSegment::kOther);
+    return;
+  }
   // Calls the callback immediately and not asynchronously.
   account_manager->GetAccounts(base::BindOnce(
       &FamilyUserMetricsProvider::CheckSecondaryAccountsAndLogSegment,
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 7c285c8..c8813ed 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/public/cpp/session/session_controller.h"
 #include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/files/file.h"
@@ -43,7 +44,6 @@
 #include "crypto/random.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "ui/base/idle/idle.h"
 #include "url/gurl.h"
 
 namespace {
@@ -230,6 +230,14 @@
   DCHECK(profile_);
   DCHECK(nearby_connections_manager_);
 
+#if defined(OS_CHROMEOS)
+  auto* session_controller = ash::SessionController::Get();
+  if (session_controller) {
+    is_screen_locked_ = session_controller->IsScreenLocked();
+    session_controller->AddObserver(this);
+  }
+#endif  // OS_CHROMEOS
+
   nearby_process_observer_.Add(process_manager_);
 
   settings_.AddSettingsObserver(settings_receiver_.BindNewPipeAndPassRemote());
@@ -282,6 +290,12 @@
   if (bluetooth_adapter_)
     bluetooth_adapter_->RemoveObserver(this);
 
+#if defined(OS_CHROMEOS)
+  auto* session_controller = ash::SessionController::Get();
+  if (session_controller)
+    session_controller->RemoveObserver(this);
+#endif  // OS_CHROMEOS
+
   foreground_receive_callbacks_.Clear();
   background_receive_callbacks_.Clear();
   foreground_send_transfer_callbacks_.Clear();
@@ -820,6 +834,13 @@
   NS_LOG(VERBOSE) << __func__ << ": Reported onShareTargetLost";
 }
 
+void NearbySharingServiceImpl::OnLockStateChanged(bool locked) {
+  NS_LOG(VERBOSE) << __func__ << ": Screen lock state changed. (" << locked
+                  << ")";
+  is_screen_locked_ = locked;
+  InvalidateSurfaceState();
+}
+
 void NearbySharingServiceImpl::OnEnabledChanged(bool enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (enabled) {
@@ -1120,7 +1141,7 @@
   }
 
   // Screen is off. Do no work.
-  if (ui::CheckIdleStateIsLocked()) {
+  if (is_screen_locked_) {
     StopAdvertising();
     NS_LOG(VERBOSE) << __func__
                     << ": Stopping advertising because the screen is locked.";
@@ -1289,7 +1310,7 @@
   }
 
   // Screen is off. Do no work.
-  if (ui::CheckIdleStateIsLocked()) {
+  if (is_screen_locked_) {
     StopScanning();
     NS_LOG(VERBOSE) << __func__
                     << ": Stopping discovery because the screen is locked.";
@@ -1346,7 +1367,7 @@
     return;
   }
 
-  if (ui::CheckIdleStateIsLocked()) {
+  if (is_screen_locked_) {
     NS_LOG(VERBOSE) << __func__
                     << ": Failed to scan because the user's screen is locked.";
     return;
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
index be22bcb..b25b3bf 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/session/session_observer.h"
 #include "base/cancelable_callback.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
@@ -57,7 +58,8 @@
       public NearbyProcessManager::Observer,
       public device::BluetoothAdapter::Observer,
       public NearbyConnectionsManager::IncomingConnectionListener,
-      public NearbyConnectionsManager::DiscoveryListener {
+      public NearbyConnectionsManager::DiscoveryListener,
+      public ash::SessionObserver {
  public:
   explicit NearbySharingServiceImpl(
       PrefService* prefs,
@@ -132,6 +134,9 @@
   void OnEndpointLost(const std::string& endpoint_id) override;
 
  private:
+  // ash::SessionObserver:
+  void OnLockStateChanged(bool locked) override;
+
   base::ObserverList<TransferUpdateCallback>& GetReceiveCallbacksFromState(
       ReceiveSurfaceState state);
   bool IsVisibleInBackground(Visibility visibility);
@@ -316,6 +321,7 @@
   std::unique_ptr<NearbyShareCertificateManager> certificate_manager_;
   NearbyShareSettings settings_;
   NearbyFileHandler file_handler_;
+  bool is_screen_locked_ = false;
 
   // A list of service observers.
   base::ObserverList<NearbySharingService::Observer> observers_;
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index b034476..1f6f1863 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -59,9 +59,10 @@
 #include "net/base/mock_network_change_notifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/idle/scoped_set_idle_state.h"
+
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ui/ash/test_session_controller.h"
 #include "components/user_manager/scoped_user_manager.h"
 #endif  // defined(OS_CHROMEOS)
 
@@ -364,6 +365,10 @@
     device::BluetoothAdapterFactory::SetAdapterForTesting(
         mock_bluetooth_adapter_);
 
+#if defined(OS_CHROMEOS)
+    session_controller_ = std::make_unique<TestSessionController>();
+#endif  // OS_CHROMEOS
+
     service_ = CreateService();
     SetFakeFastInitiationManagerFactory(/*should_succeed_on_start=*/true);
 
@@ -617,7 +622,6 @@
     SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false);
 
     ShareTarget share_target;
-    ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
     SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
     base::RunLoop run_loop;
     EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
@@ -661,7 +665,6 @@
   ShareTarget DiscoverShareTarget(
       MockTransferUpdateCallback& transfer_callback,
       MockShareTargetDiscoveredCallback& discovery_callback) {
-    ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
     SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
     // Ensure decoder parses a valid endpoint advertisement.
@@ -859,7 +862,6 @@
   // ChromeDownloadManagerDelegate.
   std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_;
   content::BrowserTaskEnvironment task_environment_;
-  ui::ScopedSetIdleState idle_state_{ui::IDLE_STATE_IDLE};
   TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   Profile* profile_ = nullptr;
 #if defined(OS_CHROMEOS)
@@ -873,6 +875,9 @@
   FakeNearbyShareCertificateManager::Factory certificate_manager_factory_;
   std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
   NiceMock<MockNearbyProcessManager> mock_nearby_process_manager_;
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<TestSessionController> session_controller_;
+#endif  // OS_CHROMEOS
   std::unique_ptr<NearbySharingServiceImpl> service_;
   std::unique_ptr<base::ScopedDisallowBlocking> disallow_blocking_;
   std::unique_ptr<FakeFastInitiationManagerFactory>
@@ -886,40 +891,40 @@
 };
 
 struct ValidSendSurfaceTestData {
-  ui::IdleState idle_state;
   bool bluetooth_enabled;
   net::NetworkChangeNotifier::ConnectionType connection_type;
 } kValidSendSurfaceTestData[] = {
     // No network connection, only bluetooth available
-    {ui::IDLE_STATE_IDLE, true, net::NetworkChangeNotifier::CONNECTION_NONE},
+    {true, net::NetworkChangeNotifier::CONNECTION_NONE},
     // Wifi available
-    {ui::IDLE_STATE_IDLE, true, net::NetworkChangeNotifier::CONNECTION_WIFI},
+    {true, net::NetworkChangeNotifier::CONNECTION_WIFI},
     // Ethernet available
-    {ui::IDLE_STATE_IDLE, true,
-     net::NetworkChangeNotifier::CONNECTION_ETHERNET},
+    {true, net::NetworkChangeNotifier::CONNECTION_ETHERNET},
     // 3G available
-    {ui::IDLE_STATE_IDLE, true, net::NetworkChangeNotifier::CONNECTION_3G},
+    {true, net::NetworkChangeNotifier::CONNECTION_3G},
     // Wifi available and no bluetooth
-    {ui::IDLE_STATE_IDLE, false, net::NetworkChangeNotifier::CONNECTION_WIFI},
+    {false, net::NetworkChangeNotifier::CONNECTION_WIFI},
     // Ethernet available and no bluetooth
-    {ui::IDLE_STATE_IDLE, false,
-     net::NetworkChangeNotifier::CONNECTION_ETHERNET}};
+    {false, net::NetworkChangeNotifier::CONNECTION_ETHERNET}};
 
 class NearbySharingServiceImplValidSendTest
     : public NearbySharingServiceImplTest,
       public testing::WithParamInterface<ValidSendSurfaceTestData> {};
 
 struct InvalidSendSurfaceTestData {
-  ui::IdleState idle_state;
+  bool screen_locked;
   bool bluetooth_enabled;
   net::NetworkChangeNotifier::ConnectionType connection_type;
 } kInvalidSendSurfaceTestData[] = {
+#if defined(OS_CHROMEOS)
     // Screen locked
-    {ui::IDLE_STATE_LOCKED, true, net::NetworkChangeNotifier::CONNECTION_WIFI},
+    {/*screen_locked=*/true, true, net::NetworkChangeNotifier::CONNECTION_WIFI},
+#endif  // OS_CHROMEOS
     // No network connection and no bluetooth
-    {ui::IDLE_STATE_IDLE, false, net::NetworkChangeNotifier::CONNECTION_NONE},
+    {/*screen_locked=*/false, false,
+     net::NetworkChangeNotifier::CONNECTION_NONE},
     // 3G available and no bluetooth
-    {ui::IDLE_STATE_IDLE, false, net::NetworkChangeNotifier::CONNECTION_3G},
+    {/*screen_locked=*/false, false, net::NetworkChangeNotifier::CONNECTION_3G},
 };
 
 class NearbySharingServiceImplInvalidSendTest
@@ -979,7 +984,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, DisableNearbyShutdownConnections) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
   service_->FlushMojoForTesting();
@@ -987,7 +991,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, StartFastInitiationAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1007,7 +1010,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, StartFastInitiationAdvertisingError) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetFakeFastInitiationManagerFactory(/*should_succeed_on_start=*/false);
   MockTransferUpdateCallback transfer_callback;
@@ -1020,7 +1022,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundStartFastInitiationAdvertisingError) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1033,7 +1034,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        StartFastInitiationAdvertising_BluetoothNotPresent) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   is_bluetooth_present_ = false;
   MockTransferUpdateCallback transfer_callback;
@@ -1046,7 +1046,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        StartFastInitiationAdvertising_BluetoothNotPowered) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   is_bluetooth_powered_ = false;
   MockTransferUpdateCallback transfer_callback;
@@ -1058,7 +1057,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, StopFastInitiationAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1076,7 +1074,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        StopFastInitiationAdvertising_BluetoothBecomesNotPresent) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1092,7 +1089,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        StopFastInitiationAdvertising_BluetoothBecomesNotPowered) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1108,7 +1104,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        RegisterSendSurfaceNoActiveProfilesNotDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   ON_CALL(mock_nearby_process_manager_, IsActiveProfile(_))
       .WillByDefault(Return(false));
@@ -1148,7 +1143,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundRegisterSendSurfaceStartsDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1161,7 +1155,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundRegisterSendSurfaceTwiceKeepsDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1232,7 +1225,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundRegisterSendSurfaceNotDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1246,7 +1238,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        DifferentSurfaceRegisterSendSurfaceTwiceKeepsDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1265,7 +1256,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        RegisterSendSurfaceEndpointFoundDiscoveryCallbackNotified) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
   // Ensure decoder parses a valid endpoint advertisement.
@@ -1323,7 +1313,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, RegisterSendSurfaceEmptyCertificate) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
   // Ensure decoder parses a valid endpoint advertisement.
@@ -1382,7 +1371,6 @@
 
 TEST_P(NearbySharingServiceImplValidSendTest,
        RegisterSendSurfaceIsDiscovering) {
-  ui::ScopedSetIdleState idle_state(GetParam().idle_state);
   is_bluetooth_present_ = GetParam().bluetooth_enabled;
   SetConnectionType(GetParam().connection_type);
   MockTransferUpdateCallback transfer_callback;
@@ -1400,7 +1388,9 @@
 
 TEST_P(NearbySharingServiceImplInvalidSendTest,
        RegisterSendSurfaceNotDiscovering) {
-  ui::ScopedSetIdleState idle_state(GetParam().idle_state);
+#if defined(OS_CHROMEOS)
+  session_controller_->SetScreenLocked(GetParam().screen_locked);
+#endif  // OS_CHROMEOS
   is_bluetooth_present_ = GetParam().bluetooth_enabled;
   SetConnectionType(GetParam().connection_type);
   MockTransferUpdateCallback transfer_callback;
@@ -1419,7 +1409,6 @@
                          testing::ValuesIn(kInvalidSendSurfaceTestData));
 
 TEST_F(NearbySharingServiceImplTest, DisableFeatureSendSurfaceNotDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
   service_->FlushMojoForTesting();
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
@@ -1435,7 +1424,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        DisableFeatureSendSurfaceStopsDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1452,7 +1440,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, UnregisterSendSurfaceStopsDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1473,7 +1460,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        UnregisterSendSurfaceDifferentCallbackKeepDiscovering) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -1492,7 +1478,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, UnregisterSendSurfaceNeverRegistered) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
   EXPECT_CALL(mock_nearby_process_manager(), StopProcess(profile_))
@@ -1507,7 +1492,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundRegisterReceiveSurfaceIsAdvertisingAllContacts) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
   local_device_data_manager()->SetDeviceName(kDeviceName);
@@ -1534,7 +1518,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundRegisterReceiveSurfaceIsAdvertisingNoOne) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetVisibility(nearby_share::mojom::Visibility::kNoOne);
   local_device_data_manager()->SetDeviceName(kDeviceName);
@@ -1563,7 +1546,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundRegisterReceiveSurfaceIsAdvertisingSelectedContacts) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetVisibility(nearby_share::mojom::Visibility::kSelectedContacts);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
@@ -1591,7 +1573,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        RegisterReceiveSurfaceTwiceSameCallbackKeepAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1607,7 +1588,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        RegisterReceiveSurfaceTwiceKeepAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1622,9 +1602,10 @@
   EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
 }
 
+#if defined(OS_CHROMEOS)
 TEST_F(NearbySharingServiceImplTest,
        ScreenLockedRegisterReceiveSurfaceNotAdvertising) {
-  ui::ScopedSetIdleState locked(ui::IDLE_STATE_LOCKED);
+  session_controller_->SetScreenLocked(true);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1634,9 +1615,25 @@
   EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
 }
 
+TEST_F(NearbySharingServiceImplTest, ScreenLocksDuringAdvertising) {
+  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
+  MockTransferUpdateCallback transfer_callback;
+  MockShareTargetDiscoveredCallback discovery_callback;
+  EXPECT_EQ(
+      NearbySharingService::StatusCodes::kOk,
+      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
+                                    SendSurfaceState::kForeground));
+  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
+
+  session_controller_->SetScreenLocked(true);
+  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
+  session_controller_->SetScreenLocked(false);
+  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
+}
+#endif  // OS_CHROMEOS
+
 TEST_F(NearbySharingServiceImplTest,
        DataUsageChangedRegisterReceiveSurfaceRestartsAdvertising) {
-  ui::ScopedSetIdleState locked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
   prefs_.SetInteger(prefs::kNearbySharingDataUsageName,
@@ -1660,7 +1657,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        NoNetworkRegisterReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
@@ -1671,7 +1667,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        NoBluetoothNoNetworkRegisterReceiveSurfaceNotAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1682,7 +1677,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, WifiRegisterReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1693,7 +1687,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        EthernetRegisterReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1704,7 +1697,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ThreeGRegisterReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1716,7 +1708,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        NoBluetoothWifiReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
@@ -1728,7 +1719,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        NoBluetoothEthernetReceiveSurfaceIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
   MockTransferUpdateCallback callback;
@@ -1740,7 +1730,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        NoBluetoothThreeGReceiveSurfaceNotAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
   MockTransferUpdateCallback callback;
@@ -1753,7 +1742,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        DisableFeatureReceiveSurfaceNotAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
   service_->FlushMojoForTesting();
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
@@ -1767,7 +1755,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        DisableFeatureReceiveSurfaceStopsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1783,7 +1770,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundReceiveSurfaceNoOneVisibilityIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
@@ -1796,7 +1782,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundReceiveSurfaceNoOneVisibilityNotAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
@@ -1811,7 +1796,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundReceiveSurfaceVisibilityToNoOneStopsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
@@ -1831,7 +1815,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundReceiveSurfaceVisibilityToSelectedStartsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
@@ -1851,7 +1834,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundReceiveSurfaceSelectedContactsVisibilityIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
@@ -1864,7 +1846,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundReceiveSurfaceSelectedContactsVisibilityIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
@@ -1877,7 +1858,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        ForegroundReceiveSurfaceAllContactsVisibilityIsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kAllContacts));
@@ -1890,7 +1870,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        BackgroundReceiveSurfaceAllContactsVisibilityNotAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kAllContacts));
@@ -1902,7 +1881,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, UnregisterReceiveSurfaceStopsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1921,7 +1899,6 @@
 
 TEST_F(NearbySharingServiceImplTest,
        UnregisterReceiveSurfaceDifferentCallbackKeepAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -1937,7 +1914,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, UnregisterReceiveSurfaceNeverRegistered) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
   EXPECT_CALL(mock_nearby_process_manager(), StopProcess(profile_))
@@ -1957,7 +1933,6 @@
   SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                             /*return_empty_advertisement=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_)).Times(0);
@@ -1985,7 +1960,6 @@
                             /*return_empty_advertisement=*/false);
   SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/true);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2034,7 +2008,6 @@
                             /*return_empty_advertisement=*/false);
   SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2145,7 +2118,6 @@
           }));
   connection_.AppendReadableData(std::move(bytes));
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2221,7 +2193,6 @@
           }));
   connection_.AppendReadableData(std::move(bytes));
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2262,7 +2233,6 @@
                             /*return_empty_advertisement=*/false);
   SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2844,7 +2814,6 @@
                             /*return_empty_advertisement=*/false);
   SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2897,7 +2866,6 @@
                             /*return_empty_advertisement=*/false);
   SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
   base::RunLoop run_loop;
@@ -2953,7 +2921,6 @@
   SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                             /*return_empty_advertisement=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
 
@@ -2984,7 +2951,6 @@
   SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                             /*return_empty_advertisement=*/false);
 
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   NiceMock<MockTransferUpdateCallback> callback;
 
@@ -3423,7 +3389,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, ProfileChangedControlsDiscovery) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback transfer_callback;
   MockShareTargetDiscoveredCallback discovery_callback;
@@ -3446,7 +3411,6 @@
 }
 
 TEST_F(NearbySharingServiceImplTest, ProfileChangedControlsAdvertising) {
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@@ -3470,7 +3434,6 @@
        RegisterForegroundReceiveSurfaceEntersHighVisibility) {
   TestObserver observer(service_.get());
   NiceMock<MockTransferUpdateCallback> callback;
-  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
 
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc
index c181d3fd..28103ac 100644
--- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc
@@ -396,10 +396,7 @@
   CloseAllTabs();
 
   security_state::SecurityLevel mixed_content_security_level =
-      base::FeatureList::IsEnabled(
-          security_state::features::kPassiveMixedContentWarning)
-          ? security_state::WARNING
-          : security_state::NONE;
+      security_state::WARNING;
 
   histogram_tester()->ExpectTotalCount(
       SecurityStatePageLoadMetricsObserver::
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java
index 8884526..a1b6233 100644
--- a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java
@@ -9,7 +9,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.policy.PolicyService;
+import org.chromium.components.policy.PolicyService;
 
 /**
  * Get the PolicyService instance. Note that the associated C++ instance won't
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.cc b/chrome/browser/policy/chrome_browser_policy_connector.cc
index f359971c..bc07603c 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.cc
+++ b/chrome/browser/policy/chrome_browser_policy_connector.cc
@@ -48,7 +48,7 @@
 #elif defined(OS_POSIX) && !defined(OS_ANDROID)
 #include "components/policy/core/common/config_dir_policy_loader.h"
 #elif defined(OS_ANDROID)
-#include "components/policy/core/browser/android/android_combined_policy_provider.h"
+#include "components/policy/core/common/android/android_combined_policy_provider.h"
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_queue.cc b/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
index 9b254f1..1caf13a 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
@@ -920,9 +920,7 @@
     }
     scoped_refptr<SingleFile> last_file = assign_result.ValueOrDie();
 
-    // Prepare and initiate writing generation into to a new file.
-    // Pick up when both WriteMetadata and WriteHeaderAndBlock
-    // have finished.
+    // Writing metadata ahead of the data write.
     Status write_result = storage_queue_->WriteMetadata();
     if (!write_result.ok()) {
       Response(write_result);
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc b/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
index 0dbfc0d..512e99c7 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
@@ -76,7 +76,16 @@
 
 class MockUploadClient : public StorageQueue::UploaderInterface {
  public:
-  MockUploadClient() = default;
+  // Mapping of (generation, seq number) to matching record digest. Whenever a
+  // record is uploaded and includes last record digest, this map should have
+  // that digest already recorded. Only the first record in a generation is
+  // uploaded without last record digest.
+  using LastRecordDigestMap = std::map<
+      std::pair<uint64_t /*generation */, uint64_t /*sequencing number*/>,
+      std::string /*digest*/>;
+
+  explicit MockUploadClient(LastRecordDigestMap* last_record_digest_map)
+      : last_record_digest_map_(last_record_digest_map) {}
 
   void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record,
                      base::OnceCallback<void(bool)> processed_cb) override {
@@ -89,25 +98,22 @@
     ASSERT_TRUE(wrapped_record.ParseFromString(
         encrypted_record.ValueOrDie().encrypted_wrapped_record()));
     // Verify generation match.
+    const auto& sequencing_information =
+        encrypted_record.ValueOrDie().sequencing_information();
     if (generation_id_.has_value() &&
-        generation_id_.value() != encrypted_record.ValueOrDie()
-                                      .sequencing_information()
-                                      .generation_id()) {
+        generation_id_.value() != sequencing_information.generation_id()) {
       std::move(processed_cb)
           .Run(UploadRecordFailure(Status(
               error::DATA_LOSS,
               base::StrCat({"Generation id mismatch, expected=",
                             base::NumberToString(generation_id_.value()),
                             " actual=",
-                            base::NumberToString(encrypted_record.ValueOrDie()
-                                                     .sequencing_information()
-                                                     .generation_id())}))));
+                            base::NumberToString(
+                                sequencing_information.generation_id())}))));
       return;
     }
     if (!generation_id_.has_value()) {
-      generation_id_ = encrypted_record.ValueOrDie()
-                           .sequencing_information()
-                           .generation_id();
+      generation_id_ = sequencing_information.generation_id();
     }
 
     // Verify digest and its match.
@@ -124,12 +130,26 @@
                 Status(error::DATA_LOSS, "Record digest mismatch")));
         return;
       }
+      if (wrapped_record.has_last_record_digest()) {
+        auto it = last_record_digest_map_->find(
+            std::make_pair(sequencing_information.sequencing_id() - 1,
+                           sequencing_information.generation_id()));
+        if (it == last_record_digest_map_->end() ||
+            it->second != wrapped_record.last_record_digest()) {
+          std::move(processed_cb)
+              .Run(UploadRecordFailure(
+                  Status(error::DATA_LOSS, "Last record digest mismatch")));
+          return;
+        }
+      }
+      last_record_digest_map_->emplace(
+          std::make_pair(sequencing_information.sequencing_id(),
+                         sequencing_information.generation_id()),
+          record_digest);
     }
 
     std::move(processed_cb)
-        .Run(UploadRecord(encrypted_record.ValueOrDie()
-                              .sequencing_information()
-                              .sequencing_id(),
+        .Run(UploadRecord(sequencing_information.sequencing_id(),
                           wrapped_record.record().data()));
   }
 
@@ -176,6 +196,8 @@
 
  private:
   base::Optional<uint64_t> generation_id_;
+  LastRecordDigestMap* const last_record_digest_map_;
+
   Sequence test_upload_sequence_;
 };
 
@@ -218,7 +240,8 @@
 
   StatusOr<std::unique_ptr<StorageQueue::UploaderInterface>>
   BuildMockUploader() {
-    auto uploader = std::make_unique<MockUploadClient>();
+    auto uploader =
+        std::make_unique<MockUploadClient>(&last_record_digest_map_);
     set_mock_uploader_expectations_.Call(uploader.get());
     return uploader;
   }
@@ -250,6 +273,10 @@
   scoped_refptr<test::TestEncryptionModule> test_encryption_module_;
   scoped_refptr<StorageQueue> storage_queue_;
 
+  // Test-wide global mapping of seq number to record digest.
+  // Serves all MockUploadClients created by test fixture.
+  MockUploadClient::LastRecordDigestMap last_record_digest_map_;
+
   ::testing::MockFunction<void(MockUploadClient*)>
       set_mock_uploader_expectations_;
 
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
index e63c4b7..f4033abd 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/policy/messaging_layer/storage/storage.h"
 
 #include <cstdint>
+#include <tuple>
 #include <utility>
-#include <vector>
 
 #include "base/files/scoped_temp_dir.h"
 #include "base/optional.h"
@@ -78,7 +78,18 @@
 
 class MockUploadClient : public Storage::UploaderInterface {
  public:
-  MockUploadClient() = default;
+  // Mapping of (generation, seq number) to matching record digest. Whenever a
+  // record is uploaded and includes last record digest, this map should have
+  // that digest already recorded. Only the first record in a generation is
+  // uploaded without last record digest.
+  using LastRecordDigestMap =
+      std::map<std::tuple<Priority,
+                          uint64_t /*generation */,
+                          uint64_t /*sequencing number*/>,
+               std::string /*digest*/>;
+
+  explicit MockUploadClient(LastRecordDigestMap* last_record_digest_map)
+      : last_record_digest_map_(last_record_digest_map) {}
 
   void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record,
                      base::OnceCallback<void(bool)> processed_cb) override {
@@ -91,25 +102,22 @@
     ASSERT_TRUE(wrapped_record.ParseFromString(
         encrypted_record.ValueOrDie().encrypted_wrapped_record()));
     // Verify generation match.
+    const auto& sequencing_information =
+        encrypted_record.ValueOrDie().sequencing_information();
     if (generation_id_.has_value() &&
-        generation_id_.value() != encrypted_record.ValueOrDie()
-                                      .sequencing_information()
-                                      .generation_id()) {
+        generation_id_.value() != sequencing_information.generation_id()) {
       std::move(processed_cb)
           .Run(UploadRecordFailure(Status(
               error::DATA_LOSS,
               base::StrCat({"Generation id mismatch, expected=",
                             base::NumberToString(generation_id_.value()),
                             " actual=",
-                            base::NumberToString(encrypted_record.ValueOrDie()
-                                                     .sequencing_information()
-                                                     .generation_id())}))));
+                            base::NumberToString(
+                                sequencing_information.generation_id())}))));
       return;
     }
     if (!generation_id_.has_value()) {
-      generation_id_ = encrypted_record.ValueOrDie()
-                           .sequencing_information()
-                           .generation_id();
+      generation_id_ = sequencing_information.generation_id();
     }
 
     // Verify digest and its match.
@@ -126,15 +134,30 @@
                 Status(error::DATA_LOSS, "Record digest mismatch")));
         return;
       }
+      if (wrapped_record.has_last_record_digest()) {
+        auto it = last_record_digest_map_->find(
+            std::make_tuple(sequencing_information.priority(),
+                            sequencing_information.sequencing_id() - 1,
+                            sequencing_information.generation_id()));
+        if (it == last_record_digest_map_->end() ||
+            it->second != wrapped_record.last_record_digest()) {
+          std::move(processed_cb)
+              .Run(UploadRecordFailure(
+                  Status(error::DATA_LOSS, "Last record digest mismatch")));
+          return;
+        }
+      }
+      last_record_digest_map_->emplace(
+          std::make_tuple(sequencing_information.priority(),
+                          sequencing_information.sequencing_id(),
+                          sequencing_information.generation_id()),
+          record_digest);
     }
 
     std::move(processed_cb)
-        .Run(UploadRecord(
-            encrypted_record.ValueOrDie().sequencing_information().priority(),
-            encrypted_record.ValueOrDie()
-                .sequencing_information()
-                .sequencing_id(),
-            wrapped_record.record().data()));
+        .Run(UploadRecord(sequencing_information.priority(),
+                          sequencing_information.sequencing_id(),
+                          wrapped_record.record().data()));
   }
 
   void Completed(Status status) override { UploadComplete(status); }
@@ -205,6 +228,8 @@
 
  private:
   base::Optional<uint64_t> generation_id_;
+  LastRecordDigestMap* const last_record_digest_map_;
+
   Sequence test_upload_sequence_;
 };
 
@@ -234,14 +259,15 @@
 
   StatusOr<std::unique_ptr<Storage::UploaderInterface>> BuildMockUploader(
       Priority priority) {
-    auto uploader = std::make_unique<MockUploadClient>();
+    auto uploader =
+        std::make_unique<MockUploadClient>(&last_record_digest_map_);
     set_mock_uploader_expectations_.Call(priority, uploader.get());
     return uploader;
   }
 
   Status WriteString(Priority priority, base::StringPiece data) {
-    TestEvent<Status> w;
     EXPECT_TRUE(storage_) << "Storage not created yet";
+    TestEvent<Status> w;
     Record record;
     record.set_data(std::string(data));
     record.set_destination(UPLOAD_EVENTS);
@@ -266,6 +292,10 @@
   scoped_refptr<test::TestEncryptionModule> test_encryption_module_;
   scoped_refptr<Storage> storage_;
 
+  // Test-wide global mapping of seq number to record digest.
+  // Serves all MockUploadClients created by test fixture.
+  MockUploadClient::LastRecordDigestMap last_record_digest_map_;
+
   ::testing::MockFunction<void(Priority, MockUploadClient*)>
       set_mock_uploader_expectations_;
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index ea76986..5477a32b 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -737,6 +737,8 @@
             new KeyPrefix("zero_suggest_description*");
     public static final KeyPrefix KEY_ZERO_SUGGEST_NATIVE_TYPE_PREFIX =
             new KeyPrefix("zero_suggest_native_type*");
+    public static final KeyPrefix KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX =
+            new KeyPrefix("zero_suggest_native_subtypes*");
     public static final KeyPrefix KEY_ZERO_SUGGEST_IS_SEARCH_TYPE_PREFIX =
             new KeyPrefix("zero_suggest_is_search*");
     public static final KeyPrefix KEY_ZERO_SUGGEST_ANSWER_TEXT_PREFIX =
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
index eef9918a..90fc777 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
@@ -209,6 +209,7 @@
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_DISPLAY_TEXT_PREFIX,
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_DESCRIPTION_PREFIX,
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_TYPE_PREFIX,
+                ChromePreferenceKeys.KEY_ZERO_SUGGEST_NATIVE_SUBTYPES_PREFIX,
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_IS_SEARCH_TYPE_PREFIX,
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_ANSWER_TEXT_PREFIX,
                 ChromePreferenceKeys.KEY_ZERO_SUGGEST_GROUP_ID_PREFIX,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 016d4c9..c5734a3 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -76,6 +76,7 @@
 #include "chrome/browser/tracing/chrome_tracing_delegate.h"
 #include "chrome/browser/ui/browser_ui_prefs.h"
 #include "chrome/browser/ui/hats/hats_service.h"
+#include "chrome/browser/ui/in_product_help/feature_promo_snooze_service.h"
 #include "chrome/browser/ui/network_profile_bubble.h"
 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
 #include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
@@ -893,6 +894,7 @@
   enterprise_reporting::RegisterProfilePrefs(registry);
   extensions::CommandService::RegisterProfilePrefs(registry);
   extensions::TabsCaptureVisibleTabFunction::RegisterProfilePrefs(registry);
+  FeaturePromoSnoozeService::RegisterProfilePrefs(registry);
   first_run::RegisterProfilePrefs(registry);
   gcm::RegisterProfilePrefs(registry);
   HatsService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 5e46e55..4b9842b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -474,6 +474,7 @@
       "options/options_test.js",
       "panel/i_search_test.js",
       "panel/panel_test.js",
+      "panel/tutorial_test.js",
     ]
     gen_include_files = [
       "../common/testing/assert_additions.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
new file mode 100644
index 0000000..47b67630
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
@@ -0,0 +1,119 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Include test fixture.
+GEN_INCLUDE(['../testing/chromevox_next_e2e_test_base.js']);
+GEN_INCLUDE(['../testing/mock_feedback.js']);
+
+/**
+ * Test fixture for the interactive tutorial.
+ */
+ChromeVoxTutorialTest = class extends ChromeVoxNextE2ETest {
+  /** @override */
+  testGenCppIncludes() {
+    super.testGenCppIncludes();
+    GEN(`
+  #include "base/command_line.h"
+  #include "ui/accessibility/accessibility_switches.h"
+      `);
+  }
+
+  /** @override */
+  testGenPreamble() {
+    GEN(`
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kEnableExperimentalAccessibilityChromeVoxTutorial);
+      `);
+    super.testGenPreamble();
+  }
+
+  /** @override */
+  setUp() {
+    window.doCmd = this.doCmd;
+  }
+
+  /**
+   * @return {!MockFeedback}
+   */
+  createMockFeedback() {
+    const mockFeedback =
+        new MockFeedback(this.newCallback(), this.newCallback.bind(this));
+    mockFeedback.install();
+    return mockFeedback;
+  }
+
+  getPanelWindow() {
+    let panelWindow = null;
+    while (!panelWindow) {
+      panelWindow = chrome.extension.getViews().find(function(view) {
+        return view.location.href.indexOf('chromevox/panel/panel.html') > 0;
+      });
+    }
+    return panelWindow;
+  }
+
+  getPanel() {
+    return this.getPanelWindow().Panel;
+  }
+
+  /**
+   * Waits for the interactive tutorial to load.
+   */
+  async waitForTutorial() {
+    return new Promise(resolve => {
+      const doc = this.getPanelWindow().document;
+      if (doc.getElementById('i-tutorial-container')) {
+        resolve();
+      } else {
+        /**
+         * @param {Array<MutationRecord>} mutationsList
+         * @param {MutationObserver} observer
+         */
+        const onMutation = (mutationsList, observer) => {
+          for (const mutation of mutationsList) {
+            if (mutation.type === 'childList') {
+              for (const node of mutation.addedNodes) {
+                if (node.id === 'i-tutorial-container') {
+                  // Resolve once the tutorial has been added to the document.
+                  resolve();
+                  observer.disconnect();
+                }
+              }
+            }
+          }
+        };
+
+        const observer = new MutationObserver(onMutation);
+        observer.observe(
+            doc.body /* target */, {childList: true} /* options */);
+      }
+    });
+  }
+
+  get simpleDoc() {
+    return `
+      <p>Simple</p>
+    `;
+  }
+};
+
+TEST_F('ChromeVoxTutorialTest', 'BasicTest', function() {
+  const mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.simpleDoc, async function(root) {
+    const Panel = this.getPanel();
+    assertTrue(Panel.iTutorialEnabled_);
+    new PanelCommand(PanelCommandType.TUTORIAL).send();
+    await this.waitForTutorial();
+    mockFeedback.expectSpeech('Choose your tutorial experience')
+        .call(doCmd('nextObject'))
+        .expectSpeech('New user', 'Button')
+        .call(doCmd('nextObject'))
+        .expectSpeech('Experienced user', 'Button')
+        .call(doCmd('nextObject'))
+        .expectSpeech('Developer', 'Button')
+        .call(doCmd('nextObject'))
+        .expectSpeech('Exit tutorial', 'Button')
+        .replay();
+  });
+});
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
index da3c78e..59d61e3 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
@@ -105,6 +105,7 @@
     "navigation_manager_test.js",
     "nodes/basic_node_test.js",
     "nodes/desktop_node_test.js",
+    "nodes/group_node_test.js",
     "nodes/tab_node_test.js",
     "switch_access_predicate_test.js",
     "text_navigation_manager_test.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
index 2e82b479..cd1c62d 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
@@ -303,3 +303,32 @@
         'button1 should come before button2');
   });
 });
+
+TEST_F(
+    'SwitchAccessNavigationManagerTest', 'NodeUndefinedBeforeTreeChangeRemoved',
+    function() {
+      const website = `<div>
+                     <button id="button1"></button>
+                   </div>`;
+      this.runWithLoadedTree(website, (desktop) => {
+        this.navigator.moveTo_(this.findNodeById('button1'));
+        const button1 = this.navigator.node_;
+        assertFalse(
+            button1 instanceof BackButtonNode,
+            'button1 should not be a BackButtonNode');
+        assertEquals(
+            'button1', button1.automationNode.htmlAttributes.id,
+            'Current node is not button1');
+
+        // Simulate the underlying node's deletion. Note that this is different
+        // than an orphaned node (which can have a valid AutomationNode
+        // instance, but no backing C++ object, so attributes returned like role
+        // are undefined).
+        NavigationManager.instance.node_.baseNode_ = undefined;
+
+        // Tree change removed gets sent by C++ after the tree has already
+        // applied changes (so this comes after the above clearing).
+        NavigationManager.instance.onTreeChange_(
+            {type: chrome.automation.TreeChangeType.NODE_REMOVED});
+      });
+    });
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
index 84a58494..3c2aedb 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
@@ -108,8 +108,8 @@
 
   /** @override */
   isValidAndVisible() {
-    // Nodes without a role are not valid.
-    if (!this.baseNode_.role) {
+    // Nodes may have been deleted or orphaned.
+    if (!this.baseNode_ || !this.baseNode_.role) {
       return false;
     }
     return SwitchAccessPredicate.isVisible(this.baseNode_) &&
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
index a9126ae..98a4d54 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
@@ -35,7 +35,8 @@
 
   /** @override */
   get location() {
-    const childLocations = this.children_.map(c => c.location);
+    const childLocations =
+        this.children_.filter(c => c.isValidAndVisible()).map(c => c.location);
     return RectUtil.unionAll(childLocations);
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
new file mode 100644
index 0000000..76fa76f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+GEN_INCLUDE(['../switch_access_e2e_test_base.js']);
+
+/** Test fixture for the node wrapper type. */
+SwitchAccessGroupNodeTest = class extends SwitchAccessE2ETest {};
+
+TEST_F('SwitchAccessGroupNodeTest', 'NodesRemoved', function() {
+  const website = `<button></button>`;
+  this.runWithLoadedTree(website, (desktop) => {
+    const button = desktop.find({role: chrome.automation.RoleType.BUTTON});
+    assertNotEquals(undefined, button);
+
+    const root = new BasicRootNode(desktop);
+    assertEquals(0, root.children_.length);
+
+    // Add a group child which has two buttons (same underlying automation
+    // node).
+    const buttonNode = new BasicNode(button, root);
+    const otherButtonNode = new BasicNode(button, root);
+    const groupNode = new GroupNode([buttonNode, otherButtonNode]);
+    root.children_ = [groupNode];
+
+    // Try asking for the location of the group.
+    assertTrue(!!groupNode.location);
+
+    // Try again after clearing one of the button's underlying node.
+    buttonNode.baseNode_ = undefined;
+    assertTrue(!!groupNode.location);
+  });
+});
diff --git a/chrome/browser/resources/chromeos/crostini_installer/app.js b/chrome/browser/resources/chromeos/crostini_installer/app.js
index ff3c5108..c92b178f 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/app.js
+++ b/chrome/browser/resources/chromeos/crostini_installer/app.js
@@ -412,9 +412,6 @@
       case InstallerState.kInstallImageLoader:
         messageId = 'loadTerminaMessage';
         break;
-      case InstallerState.kStartConcierge:
-        messageId = 'startConciergeMessage';
-        break;
       case InstallerState.kCreateDiskImage:
         messageId = 'createDiskImageMessage';
         break;
@@ -461,9 +458,6 @@
       case InstallerError.kErrorLoadingTermina:
         messageId = 'loadTerminaError';
         break;
-      case InstallerError.kErrorStartingConcierge:
-        messageId = 'startConciergeError';
-        break;
       case InstallerError.kErrorCreatingDiskImage:
         messageId = 'createDiskImageError';
         break;
diff --git a/chrome/browser/resources/chromeos/file_manager/OWNERS b/chrome/browser/resources/chromeos/file_manager/OWNERS
deleted file mode 100644
index 7d236cb..0000000
--- a/chrome/browser/resources/chromeos/file_manager/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-file://ui/file_manager/OWNERS
-# COMPONENT: Platform>Apps>FileManager
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index fc569cf..48fab7e 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -279,7 +279,8 @@
       <ntp-middle-slot-promo
           on-ntp-middle-slot-promo-loaded="onMiddleSlotPromoLoaded_">
       </ntp-middle-slot-promo>
-      <template is="dom-repeat" items="[[moduleDescriptors_]]" id="modules">
+      <template is="dom-repeat" items="[[moduleDescriptors_]]" id="modules"
+          on-dom-change="onModulesRendered_">
         <ntp-module-wrapper descriptor="[[item]]"></ntp-module-wrapper>
       </template>
       <a id="backgroundImageAttribution"
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index 68ea6ff..cb34994 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -742,6 +742,11 @@
     onResize();
   }
 
+  /** @private */
+  onModulesRendered_() {
+    this.pageHandler_.onModulesRendered(BrowserProxy.getInstance().now());
+  }
+
   /**
    * During a shortcut drag, an iframe behind ntp-most-visited will prevent
    * 'dragover' events from firing. To workaround this, 'pointer-events: none'
diff --git a/chrome/browser/resources/optimize_webui.py b/chrome/browser/resources/optimize_webui.py
index 5b3b9aa..094b7b3 100755
--- a/chrome/browser/resources/optimize_webui.py
+++ b/chrome/browser/resources/optimize_webui.py
@@ -73,6 +73,7 @@
   'resources/css/text_defaults_md.css',
   'resources/mojo/mojo/public/js/mojo_bindings_lite.html',
   'resources/mojo/mojo/public/mojom/base/time.mojom.html',
+  'resources/mojo/mojo/public/mojom/base/time.mojom-lite.js',
   'resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom.html',
   'resources/mojo/services/network/public/mojom/ip_address.mojom.html',
 
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 1cbb07a1..19b8a43 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -163,6 +163,7 @@
     ":toolbar_manager",
     "elements:viewer-error-screen",
     "elements:viewer-password-screen",
+    "elements:viewer-pdf-sidenav",
     "elements:viewer-pdf-toolbar",
     "elements:viewer-pdf-toolbar-new",
     "elements:viewer-zoom-toolbar",
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
index ad0db9f..b81eeb0 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
@@ -1,10 +1,13 @@
 <style include="pdf-shared">
   :host {
+    --viewer-pdf-toolbar-height: 56px;
     box-shadow:
         0 -2px 8px rgba(0, 0, 0, 0.09),
         0 4px 8px rgba(0, 0, 0, 0.06),
         0 1px 2px rgba(0, 0, 0, 0.3),
         0 2px 6px rgba(0, 0, 0, 0.15);
+    height: var(--viewer-pdf-toolbar-height);
+    position: relative;
   }
 
   #toolbar {
@@ -12,7 +15,7 @@
     background-color: var(--viewer-pdf-toolbar-background-color);
     color: white;
     display: flex;
-    height: 56px;
+    height: var(--viewer-pdf-toolbar-height);
     padding: 0 16px;
   }
 
@@ -164,7 +167,7 @@
 <div id="toolbar">
   <div id="start">
     <cr-icon-button id="sidenavToggle" iron-icon="cr20:menu"
-        on-click="onSidenavToggleClick_">
+        disabled="[[annotationMode]]" on-click="onSidenavToggleClick_">
     </cr-icon-button>
     <span id="title">[[docTitle]]</span>
   </div>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
index 28d2c71..20ffefc 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js
@@ -40,7 +40,6 @@
       // </if>
       annotationMode: {
         type: Boolean,
-        notify: true,
         value: false,
         reflectToAttribute: true,
       },
@@ -324,11 +323,11 @@
 
   // <if expr="chromeos">
   toggleAnnotation() {
-    this.annotationMode = !this.annotationMode;
+    const newAnnotationMode = !this.annotationMode;
     this.dispatchEvent(new CustomEvent(
-        'annotation-mode-toggled', {detail: this.annotationMode}));
+        'annotation-mode-toggled', {detail: newAnnotationMode}));
 
-    if (this.annotationMode && !this.displayAnnotations_) {
+    if (newAnnotationMode && !this.displayAnnotations_) {
       this.toggleDisplayAnnotations_();
     }
   }
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index 95044f9..f051b41 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -142,6 +142,7 @@
 </template>
 <template is="dom-if" if="[[pdfViewerUpdateEnabled_]]">
   <viewer-pdf-toolbar-new id="toolbar"
+    annotation-mode="[[annotationMode_]]"
     doc-title="[[title_]]" doc-length="[[docLength_]]" page-no="[[pageNo_]]"
     load-progress="[[loadProgress_]]" has-edits="[[hasEdits_]]"
     has-entered-annotation-mode="[[hasEnteredAnnotationMode_]]"
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index a003c97c..67d1d27c 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -4,7 +4,6 @@
 
 import './elements/viewer-error-screen.js';
 import './elements/viewer-password-screen.js';
-import './elements/viewer-pdf-sidenav.js';
 import './elements/viewer-pdf-toolbar.js';
 import './elements/viewer-zoom-toolbar.js';
 import './elements/shared-vars.js';
@@ -18,12 +17,13 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {hasKeyModifiers} from 'chrome://resources/js/util.m.js';
+import {hasKeyModifiers, listenOnce} from 'chrome://resources/js/util.m.js';
 import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Bookmark} from './bookmark_type.js';
 import {BrowserApi} from './browser_api.js';
 import {Attachment, FittingType, Point, SaveRequestType} from './constants.js';
+import {ViewerPdfSidenavElement} from './elements/viewer-pdf-sidenav.js';
 import {ViewerPdfToolbarNewElement} from './elements/viewer-pdf-toolbar-new.js';
 // <if expr="chromeos">
 import {InkController} from './ink_controller.js';
@@ -259,6 +259,12 @@
     /** @private {boolean} */
     this.sidenavCollapsed_ = false;
 
+    /**
+     * The state to restore sidenavCollapsed_ to after exiting annotation mode.
+     * @private {boolean}
+     */
+    this.sidenavRestoreState_ = false;
+
     if (this.pdfViewerUpdateEnabled_) {
       // TODO(dpapad): Add tests after crbug.com/1111459 is fixed.
       this.sidenavCollapsed_ = Boolean(Number.parseInt(
@@ -459,18 +465,51 @@
 
   // <if expr="chromeos">
   /**
+   * @return {!Promise} Resolves when the sidenav animation is complete.
+   * @private
+   */
+  waitForSidenavTransition_() {
+    return new Promise(resolve => {
+      listenOnce(
+          /** @type {!ViewerPdfSidenavElement} */ (
+              this.shadowRoot.querySelector('#sidenav-container')),
+          'transitionend', e => resolve());
+    });
+  }
+
+  /**
+   * @return {!Promise} Resolves when the sidenav is restored to
+   *     |sidenavRestoreState_|, after having been closed for annotation mode.
+   * @private
+   */
+  restoreSidenav_() {
+    this.sidenavCollapsed_ = this.sidenavRestoreState_;
+    return this.sidenavCollapsed_ ? Promise.resolve() :
+                                    this.waitForSidenavTransition_();
+  }
+
+  /**
    * Handles the annotation mode being toggled on or off.
    * @param {!CustomEvent<boolean>} e
    * @private
    */
   async onAnnotationModeToggled_(e) {
     const annotationMode = e.detail;
-    this.annotationMode_ = annotationMode;
     if (annotationMode) {
       // Enter annotation mode.
       assert(this.currentController === this.pluginController);
       // TODO(dstockwell): set plugin read-only, begin transition
       this.updateProgress(0);
+
+      if (this.pdfViewerUpdateEnabled_) {
+        this.sidenavRestoreState_ = this.sidenavCollapsed_;
+        this.sidenavCollapsed_ = true;
+        if (!this.sidenavRestoreState_) {
+          // Wait for the animation before proceeding.
+          await this.waitForSidenavTransition_();
+        }
+      }
+
       // TODO(dstockwell): handle save failure
       const saveResult =
           await this.pluginController.save(SaveRequestType.ANNOTATION);
@@ -483,12 +522,12 @@
         } catch (e) {
           // The user aborted entering annotation mode. Revert to the plugin.
           this.getToolbar_().annotationMode = false;
-          this.annotationMode_ = false;
           this.updateProgress(100);
           return;
         }
       }
       PDFMetrics.record(PDFMetrics.UserAction.ENTER_ANNOTATION_MODE);
+      this.annotationMode_ = true;
       this.hasEnteredAnnotationMode_ = true;
       // TODO(dstockwell): feed real progress data from the Ink component
       this.updateProgress(50);
@@ -502,6 +541,7 @@
       assert(this.currentController === this.inkController_);
       // TODO(dstockwell): set ink read-only, begin transition
       this.updateProgress(0);
+      this.annotationMode_ = false;
       // This runs separately to allow other consumers of `loaded` to queue
       // up after this task.
       this.loaded.then(() => {
@@ -513,6 +553,9 @@
           await this.inkController_.save(SaveRequestType.ANNOTATION);
       // Data always exists when save is called with requestType = ANNOTATION.
       const result = /** @type {!RequiredSaveResult} */ (saveResult);
+      if (this.pdfViewerUpdateEnabled_) {
+        await this.restoreSidenav_();
+      }
       await this.pluginController.load(result.fileName, result.dataToSave);
       // Ensure the plugin gets the initial viewport.
       this.pluginController.afterZoom();
@@ -529,6 +572,9 @@
     }
     this.getToolbar_().toggleAnnotation();
     this.annotationMode_ = false;
+    if (this.pdfViewerUpdateEnabled_) {
+      await this.restoreSidenav_();
+    }
     await this.loaded;
   }
   // </if>
@@ -546,7 +592,8 @@
    * @private
    */
   onScroll_(e) {
-    if (this.currentController === this.pluginController) {
+    if (this.currentController === this.pluginController &&
+        !this.annotationMode_) {
       this.pluginController.updateScroll(
           e.target.scrollLeft, e.target.scrollTop);
     }
diff --git a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
index 5c572d9..907cf23 100644
--- a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
+++ b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SecuritySettingsFragmentTest.java
@@ -37,8 +37,8 @@
 import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionAndAuxButton;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 /**
  * Tests for {@link SecuritySettingsFragment}.
diff --git a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java
index 8640841a..3a2f2a8 100644
--- a/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java
+++ b/chrome/browser/safe_browsing/android/javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java
@@ -25,10 +25,10 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 /**
  * Tests for {@link StandardProtectionSettingsFragment}.
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index 875baa9d..5765b20 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -134,6 +134,23 @@
     return;
   }
 
+  // When navigating a newly created portal contents, establish an association
+  // with its creator, so we can track the referrer chain across portal
+  // activations.
+  if (web_contents()->IsPortal() &&
+      !web_contents()->GetController().GetLastCommittedEntry()) {
+    content::RenderFrameHost* initiator_frame_host =
+        content::RenderFrameHost::FromID(
+            navigation_handle->GetInitiatorRoutingId());
+    content::WebContents* initiator_contents =
+        content::WebContents::FromRenderFrameHost(initiator_frame_host);
+    manager_->RecordNewWebContents(
+        initiator_contents, initiator_frame_host->GetProcess()->GetID(),
+        initiator_frame_host->GetRoutingID(), navigation_handle->GetURL(),
+        navigation_handle->GetPageTransition(), web_contents(),
+        navigation_handle->IsRendererInitiated());
+  }
+
   std::unique_ptr<NavigationEvent> nav_event =
       std::make_unique<NavigationEvent>();
   auto it = navigation_handle_map_.find(navigation_handle);
@@ -164,8 +181,7 @@
 
   // If there was a URL previously committed in the current RenderFrameHost,
   // set it as the source url of this navigation. Otherwise, this is the
-  // first url going to commit in this frame. We set navigation_handle's URL as
-  // the source url.
+  // first url going to commit in this frame.
   int current_process_id =
       navigation_handle->GetStartingSiteInstance()->GetProcess()->GetID();
   content::RenderFrameHost* current_frame_host =
@@ -270,44 +286,6 @@
       renderer_initiated);
 }
 
-void SafeBrowsingNavigationObserver::DidActivatePortal(
-    content::WebContents* predecessor_web_contents,
-    base::TimeTicks activation_time) {
-  content::RenderFrameHost* predecessor_frame =
-      predecessor_web_contents->GetMainFrame();
-  content::RenderFrameHost* successor_frame = web_contents()->GetMainFrame();
-
-  // Portal activation swaps contents in a tab, so to the user it looks like a
-  // navigation, so we treat activation as navigation event, with the
-  // predecessor as the source and the successor as the "navigation."
-  std::unique_ptr<NavigationEvent> nav_event =
-      std::make_unique<NavigationEvent>();
-  nav_event->navigation_initiation =
-      predecessor_frame->HasTransientUserActivation()
-          ? ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE
-          : ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE;
-  nav_event->frame_id = successor_frame->GetFrameTreeNodeId();
-  DCHECK(predecessor_frame->GetLastCommittedURL().is_valid());
-  DCHECK(successor_frame->GetLastCommittedURL().is_valid());
-  nav_event->source_url = SafeBrowsingNavigationObserverManager::ClearURLRef(
-      predecessor_frame->GetLastCommittedURL());
-  nav_event->source_main_frame_url = nav_event->source_url;
-  // TODO(mcnee): Ensure that redirects within a portal before it is activated
-  // are reflected in the referrer chain. See https://crbug.com/1096115
-  nav_event->original_request_url =
-      SafeBrowsingNavigationObserverManager::ClearURLRef(
-          successor_frame->GetLastCommittedURL());
-  nav_event->source_tab_id =
-      sessions::SessionTabHelper::IdForTab(predecessor_web_contents);
-  nav_event->target_tab_id =
-      sessions::SessionTabHelper::IdForTab(web_contents());
-  nav_event->maybe_launched_by_external_application = false;
-  nav_event->has_committed = true;
-  nav_event->last_updated = base::Time::Now();
-
-  manager_->RecordNavigationEvent(std::move(nav_event));
-}
-
 void SafeBrowsingNavigationObserver::OnContentSettingChanged(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
index fd79bf4..02b6788 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
@@ -136,8 +136,6 @@
                            ui::PageTransition transition,
                            bool started_from_context_menu,
                            bool renderer_initiated) override;
-  void DidActivatePortal(content::WebContents* predecessor_web_contents,
-                         base::TimeTicks activation_time) override;
 
   // content_settings::Observer overrides.
   void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index 9d5a75a..66ee1a6 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -2704,6 +2704,33 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+namespace {
+class PortalActivationWaiter : public content::WebContentsObserver {
+ public:
+  explicit PortalActivationWaiter(content::WebContents* portal_contents)
+      : content::WebContentsObserver(portal_contents) {}
+
+  void Wait() {
+    if (!web_contents()->IsPortal())
+      return;
+
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  // content::WebContentsObserver:
+  void DidActivatePortal(content::WebContents* predecessor_contents,
+                         base::TimeTicks activation_time) override {
+    if (quit_closure_)
+      std::move(quit_closure_).Run();
+  }
+
+ private:
+  base::OnceClosure quit_closure_;
+};
+}  // namespace
+
 // Click a link which activates a portal to the landing page, and then click on
 // the landing page to trigger the download.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverPortalBrowserTest,
@@ -2713,6 +2740,7 @@
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
   ui_test_utils::NavigateToURL(browser(), initial_url);
 
+  SimulateUserGesture();
   ASSERT_EQ(true, content::EvalJs(
                       browser()->tab_strip_model()->GetActiveWebContents(),
                       content::JsReplace(
@@ -2724,11 +2752,19 @@
                           "});",
                           landing_url)));
 
-  // Note that this runs with a user gesture.
-  ASSERT_EQ(true, content::EvalJs(
-                      browser()->tab_strip_model()->GetActiveWebContents(),
-                      "let portal = document.querySelector('portal');"
-                      "portal.activate().then(() => { return true; });"));
+  std::vector<content::WebContents*> inner_web_contents =
+      browser()
+          ->tab_strip_model()
+          ->GetActiveWebContents()
+          ->GetInnerWebContents();
+  ASSERT_EQ(1u, inner_web_contents.size());
+  content::WebContents* portal_contents = inner_web_contents[0];
+
+  PortalActivationWaiter activation_waiter(portal_contents);
+  ASSERT_TRUE(
+      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "document.querySelector('portal').activate();"));
+  activation_waiter.Wait();
 
   ClickTestLink("download_on_landing_page", 1, landing_url);
 
@@ -2744,19 +2780,19 @@
                         true,         // has_committed
                         false,        // has_server_redirect
                         nav_list->Get(0));
-  VerifyNavigationEvent(GURL(),       // source_url
-                        GURL(),       // source_main_frame_url
-                        landing_url,  // original_request_url
-                        landing_url,  // destination_url
-                        false,        // is_user_initiated,
-                        true,         // has_committed
-                        false,        // has_server_redirect
-                        nav_list->Get(1));
   VerifyNavigationEvent(initial_url,  // source_url
                         initial_url,  // source_main_frame_url
                         landing_url,  // original_request_url
                         landing_url,  // destination_url
                         true,         // is_user_initiated,
+                        false,        // has_committed
+                        false,        // has_server_redirect
+                        nav_list->Get(1));
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        landing_url,  // original_request_url
+                        landing_url,  // destination_url
+                        false,        // is_user_initiated,
                         true,         // has_committed
                         false,        // has_server_redirect
                         nav_list->Get(2));
@@ -2808,4 +2844,144 @@
       referrer_chain.Get(2));
 }
 
+// Click a link which creates a portal which redirects to the landing page and
+// is then activated, and then click on the landing page to trigger the
+// download. The redirect within the portal before it was activated should be
+// reflected in the referrer chain.
+IN_PROC_BROWSER_TEST_F(SBNavigationObserverPortalBrowserTest,
+                       RedirectInPortalThenActivate) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  GURL redirect_to_landing_url =
+      embedded_test_server()->GetURL(kRedirectToLandingURL);
+  GURL landing_url = embedded_test_server()->GetURL(kLandingURL);
+  GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  ui_test_utils::NavigateToURL(browser(), initial_url);
+
+  content::TestNavigationObserver redirect_observer(landing_url);
+  redirect_observer.StartWatchingNewWebContents();
+
+  SimulateUserGesture();
+  ASSERT_EQ(true, content::EvalJs(
+                      browser()->tab_strip_model()->GetActiveWebContents(),
+                      content::JsReplace(
+                          "new Promise((resolve) => {"
+                          "  let portal = document.createElement('portal');"
+                          "  portal.src = $1;"
+                          "  portal.onload = () => { resolve(true); };"
+                          "  document.body.appendChild(portal);"
+                          "});",
+                          redirect_to_landing_url)));
+
+  redirect_observer.Wait();
+
+  std::vector<content::WebContents*> inner_web_contents =
+      browser()
+          ->tab_strip_model()
+          ->GetActiveWebContents()
+          ->GetInnerWebContents();
+  ASSERT_EQ(1u, inner_web_contents.size());
+  content::WebContents* portal_contents = inner_web_contents[0];
+
+  PortalActivationWaiter activation_waiter(portal_contents);
+  ASSERT_TRUE(
+      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "document.querySelector('portal').activate();"));
+  activation_waiter.Wait();
+
+  ClickTestLink("download_on_landing_page", 1, landing_url);
+
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
+  auto* nav_list = navigation_event_list();
+  ASSERT_TRUE(nav_list);
+  ASSERT_EQ(5U, nav_list->Size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_list->Get(0));
+  VerifyNavigationEvent(initial_url,              // source_url
+                        initial_url,              // source_main_frame_url
+                        redirect_to_landing_url,  // original_request_url
+                        redirect_to_landing_url,  // destination_url
+                        true,                     // is_user_initiated,
+                        false,                    // has_committed
+                        false,                    // has_server_redirect
+                        nav_list->Get(1));
+  VerifyNavigationEvent(GURL(),                   // source_url
+                        GURL(),                   // source_main_frame_url
+                        redirect_to_landing_url,  // original_request_url
+                        redirect_to_landing_url,  // destination_url
+                        false,                    // is_user_initiated,
+                        true,                     // has_committed
+                        false,                    // has_server_redirect
+                        nav_list->Get(2));
+  VerifyNavigationEvent(redirect_to_landing_url,  // source_url
+                        redirect_to_landing_url,  // source_main_frame_url
+                        landing_url,              // original_request_url
+                        landing_url,              // destination_url
+                        false,                    // is_user_initiated,
+                        true,                     // has_committed
+                        false,                    // has_server_redirect
+                        nav_list->Get(3));
+  VerifyNavigationEvent(landing_url,   // source_url
+                        landing_url,   // source_main_frame_url
+                        download_url,  // original_request_url
+                        download_url,  // destination_url
+                        true,          // is_user_initiated,
+                        false,         // has_committed
+                        false,         // has_server_redirect
+                        nav_list->Get(4));
+  VerifyHostToIpMap();
+
+  ReferrerChain referrer_chain;
+  IdentifyReferrerChainForDownload(GetDownload(), &referrer_chain);
+  EXPECT_EQ(4, referrer_chain.size());
+  VerifyReferrerChainEntry(
+      download_url,                   // url
+      GURL(),                         // main_frame_url
+      ReferrerChainEntry::EVENT_URL,  // type
+      test_server_ip,                 // ip_address
+      landing_url,                    // referrer_url
+      GURL(),                         // referrer_main_frame_url
+      false,                          // is_retargeting
+      std::vector<GURL>(),            // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
+      referrer_chain.Get(0));
+  VerifyReferrerChainEntry(
+      landing_url,                       // url
+      GURL(),                            // main_frame_url
+      ReferrerChainEntry::LANDING_PAGE,  // type
+      test_server_ip,                    // ip_address
+      redirect_to_landing_url,           // referrer_url
+      GURL(),                            // referrer_main_frame_url
+      false,                             // is_retargeting
+      std::vector<GURL>(),               // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
+      referrer_chain.Get(1));
+  VerifyReferrerChainEntry(
+      redirect_to_landing_url,              // url
+      GURL(),                               // main_frame_url
+      ReferrerChainEntry::CLIENT_REDIRECT,  // type
+      test_server_ip,                       // ip_address
+      initial_url,                          // referrer_url
+      GURL(),                               // referrer_main_frame_url
+      true,                                 // is_retargeting
+      std::vector<GURL>(),                  // server redirects
+      ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE,
+      referrer_chain.Get(2));
+  VerifyReferrerChainEntry(initial_url,  // url
+                           GURL(),       // main_frame_url
+                           ReferrerChainEntry::LANDING_REFERRER,  // type
+                           test_server_ip,                        // ip_address
+                           GURL(),               // referrer_url
+                           GURL(),               // referrer_main_frame_url
+                           false,                // is_retargeting
+                           std::vector<GURL>(),  // server redirects
+                           ReferrerChainEntry::BROWSER_INITIATED,
+                           referrer_chain.Get(3));
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/share/DEPS b/chrome/browser/share/DEPS
index c99df3bfc..d7aba3d 100644
--- a/chrome/browser/share/DEPS
+++ b/chrome/browser/share/DEPS
@@ -12,6 +12,7 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/modules/ModuleInstallUi.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/notifications",
   "+chrome/android/java/src/org/chromium/chrome/browser/screenshot/EditorScreenshotTask.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/share",
   "+chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java",
@@ -19,9 +20,7 @@
   "+components/browser_ui/notifications/android",
   "+components/browser_ui/share/android",
   "+components/browser_ui/android/bottomsheet",
+  "+components/infobars/android",
   "+content/public/android/java/src/org/chromium/content_public/browser/RenderWidgetHostView.java",
   "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
-
-  # TODO(crbug/1090917): Remove this dependency once the STTS java code is moved.
-  "+chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinator.java",
 ]
diff --git a/chrome/browser/share/android/BUILD.gn b/chrome/browser/share/android/BUILD.gn
index dd31391..524196e 100644
--- a/chrome/browser/share/android/BUILD.gn
+++ b/chrome/browser/share/android/BUILD.gn
@@ -31,5 +31,11 @@
   sources = [
     "java/src/org/chromium/chrome/browser/share/BitmapDownloadRequest.java",
     "java/src/org/chromium/chrome/browser/share/qrcode/QRCodeGenerationRequest.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java",
   ]
 }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
index 50e3e1a..acfae3d5 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
@@ -7,39 +7,49 @@
 import android.content.Context;
 import android.net.Uri;
 
+import org.chromium.blink.mojom.TextFragmentSelectorProducer;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.components.browser_ui.share.ShareParams;
-import org.chromium.ui.base.WindowAndroid;
+import org.chromium.services.service_manager.InterfaceProvider;
 
 /**
  * Handles the Link To Text action in the Sharing Hub.
  */
-public class LinkToTextCoordinator {
+public class LinkToTextCoordinator extends EmptyTabObserver {
     private static final String SHARE_TEXT_TEMPLATE = "\"%s\"\n%s";
     private static final String TEXT_FRAGMENT_PREFIX = ":~:text=";
+    private static final String INVALID_SELECTOR = "";
     private final Context mContext;
-    private final WindowAndroid mWindow;
     private final ChromeOptionShareCallback mChromeOptionShareCallback;
     private final String mVisibleUrl;
     private final String mSelectedText;
+    private final Tab mTab;
 
-    public LinkToTextCoordinator(Context context, WindowAndroid window,
+    private TextFragmentSelectorProducer mProducer;
+    private boolean mCancelRequest;
+
+    public LinkToTextCoordinator(Context context, Tab tab,
             ChromeOptionShareCallback chromeOptionShareCallback, String visibleUrl,
             String selectedText) {
         mContext = context;
-        mWindow = window;
         mChromeOptionShareCallback = chromeOptionShareCallback;
         mVisibleUrl = visibleUrl;
         mSelectedText = selectedText;
+        mTab = tab;
+        mTab.addObserver(this);
+        mCancelRequest = false;
 
-        // TODO(1102382): Replace following line with a request to create text fragment selector and
-        // pass |OnSelectorReady| as callback.
-        onSelectorReady("");
+        requestSelector();
     }
 
     public void onSelectorReady(String selector) {
+        if (mCancelRequest) return;
+
         String successMessage =
                 mContext.getResources().getString(R.string.link_to_text_success_message);
         String failureMessage =
@@ -48,9 +58,10 @@
         // TODO(1102382): Consider creating SharedParams on sharesheet side. In that case there will
         // be no need to keep the WindowAndroid in this class.
         String textToShare = getTextToShare(selector);
-        ShareParams params = new ShareParams.Builder(mWindow, /*title=*/"", /*url=*/"")
-                                     .setText(textToShare)
-                                     .build();
+        ShareParams params =
+                new ShareParams.Builder(mTab.getWindowAndroid(), /*title=*/"", /*url=*/"")
+                        .setText(textToShare)
+                        .build();
 
         ChromeShareExtras chromeShareExtras = new ChromeShareExtras.Builder().build();
         mChromeOptionShareCallback.showThirdPartyShareSheetWithMessage(
@@ -58,6 +69,23 @@
                 System.currentTimeMillis());
     }
 
+    public void requestSelector() {
+        if (mTab.getWebContents().getMainFrame() != mTab.getWebContents().getFocusedFrame()) {
+            onSelectorReady(INVALID_SELECTOR);
+            return;
+        }
+
+        InterfaceProvider interfaces = mTab.getWebContents().getMainFrame().getRemoteInterfaces();
+        mProducer = interfaces.getInterface(TextFragmentSelectorProducer.MANAGER);
+        mProducer.generateSelector(new TextFragmentSelectorProducer.GenerateSelectorResponse() {
+            @Override
+            public void call(String selector) {
+                onSelectorReady(selector);
+                cleanup();
+            }
+        });
+    }
+
     public String getTextToShare(String selector) {
         String url = mVisibleUrl;
         if (!selector.isEmpty()) {
@@ -67,4 +95,29 @@
         }
         return String.format(SHARE_TEXT_TEMPLATE, mSelectedText, url);
     }
+
+    // Discard results if tab is not on foreground anymore.
+    @Override
+    public void onHidden(Tab tab, @TabHidingType int type) {
+        cleanup();
+    }
+
+    // Discard results if tab content is changed by typing new URL in omnibox.
+    @Override
+    public void onUpdateUrl(Tab tab, String url) {
+        cleanup();
+    }
+
+    // Discard results if tab content crashes.
+    @Override
+    public void onCrash(Tab tab) {
+        cleanup();
+    }
+
+    private void cleanup() {
+        // TODO(gayane): Consider canceling request in renderer.
+        if (mProducer != null) mProducer.close();
+        mCancelRequest = true;
+        mTab.removeObserver(this);
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetAdapter.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetAdapter.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
index bb0019f3..428e90e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetAdapter.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import android.content.Context;
 import android.content.res.Resources;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
index 779f085..ef6e7f4e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -18,8 +18,8 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfMetrics.SendTabToSelfShareClickResult;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfMetrics.SendTabToSelfShareClickResult;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.widget.ButtonCompat;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java
index c650df6..9b19f3f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationManager.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import android.app.AlarmManager;
 import android.app.Notification;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManager.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManager.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java
index 1e1db15..5f9be1645 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManager.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -23,7 +23,6 @@
  * with all previous versions.
  */
 public class NotificationSharedPrefManager {
-
     // Any time the serialization of the ActiveNotification needs to change, increment this version.
     private static final int VERSION = 1;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/OWNERS b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/OWNERS
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/OWNERS
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
index 7ea49f77..4455714 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridge.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import androidx.annotation.Nullable;
 
@@ -164,7 +164,7 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         SendTabToSelfEntry addEntry(Profile profile, String url, String title, long navigationTime,
                 String targetDeviceSyncCacheGuid);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
similarity index 90%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinator.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
index 863e1a7..fb1c8fc7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import android.content.Context;
 
@@ -47,8 +47,8 @@
         mController.expandSheet();
     }
 
-    static BottomSheetContent createBottomSheetContent(Context context, String url, String title,
-            long navigationTime, BottomSheetController controller,
+    public static BottomSheetContent createBottomSheetContent(Context context, String url,
+            String title, long navigationTime, BottomSheetController controller,
             SettingsLauncher settingsLauncher, boolean isSyncEnabled) {
         if (sBottomSheetContentForTesting != null) {
             return sBottomSheetContentForTesting;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java
index 66d5795..286788c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfEntry.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import org.chromium.base.annotations.CalledByNative;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java
index e32f8c59..75e8e2f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBar.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBarController.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBarController.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
index a07b3dea..6e80fac5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfInfoBarController.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java
index 3fb3e656..e528b06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfMetrics.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfModelObserverBridge.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfModelObserverBridge.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java
index 951009d..4e315b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfModelObserverBridge.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java
rename to chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java
index d410ed1..e3d7c7f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/TargetDeviceInfo.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
index c2da26f..014cef2 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -21,12 +21,12 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfCoordinator;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator;
 import org.chromium.chrome.browser.share.qrcode.QrCodeCoordinator;
 import org.chromium.chrome.browser.share.screenshot.ScreenshotCoordinator;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
 import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -340,10 +340,9 @@
                 .setIcon(R.drawable.link, R.string.sharing_highlights)
                 .setFeatureNameForMetrics("SharingHubAndroid.LinkToTextSelected")
                 .setOnClickCallback((view) -> {
-                    LinkToTextCoordinator linkToTextCoordinator = new LinkToTextCoordinator(
-                            mActivity, mTabProvider.get().getWindowAndroid(),
-                            mChromeOptionShareCallback, mShareParams.getUrl(),
-                            mShareParams.getText());
+                    LinkToTextCoordinator linkToTextCoordinator =
+                            new LinkToTextCoordinator(mActivity, mTabProvider.get(),
+                                    mChromeOptionShareCallback, mUrl, mShareParams.getText());
                 })
                 .build();
     }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java
index 279f068..39750e8 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java
@@ -5,7 +5,10 @@
 package org.chromium.chrome.browser.share.share_sheet;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.net.Uri;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -14,21 +17,32 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType;
+import org.chromium.chrome.browser.ui.favicon.IconType;
+import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
+import org.chromium.url.GURL;
 
+import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Bottom sheet content to display a 2-row custom share sheet.
@@ -36,20 +50,24 @@
 class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickListener {
     private static final int SHARE_SHEET_ITEM = 0;
     private final Context mContext;
+    private final LargeIconBridge mIconBridge;
     private final ShareSheetCoordinator mShareSheetCoordinator;
-    private ViewGroup mToolbarView;
     private ViewGroup mContentView;
     private ShareParams mParams;
+    private String mUrl;
 
     /**
      * Creates a ShareSheetBottomSheetContent (custom share sheet) opened from the given activity.
      *
      * @param context The context the share sheet was launched from.
-     * @param shareSheetCoordinator The Cooredinator that instatiated this BottomSheetContent.
+     * @param iconBridge The {@link LargeIconBridge} to generate the icon in the preview.
+     * @param shareSheetCoordinator The Coordinator that instantiated this BottomSheetContent.
+     * @param params The {@link ShareParams} for the current share.
      */
-    ShareSheetBottomSheetContent(
-            Context context, ShareSheetCoordinator shareSheetCoordinator, ShareParams params) {
+    ShareSheetBottomSheetContent(Context context, LargeIconBridge iconBridge,
+            ShareSheetCoordinator shareSheetCoordinator, ShareParams params) {
         mContext = context;
+        mIconBridge = iconBridge;
         mShareSheetCoordinator = shareSheetCoordinator;
         mParams = params;
         createContentView();
@@ -66,10 +84,11 @@
      * @param activity The activity the share sheet belongs to.
      * @param firstPartyModels The PropertyModels used to build the top row.
      * @param thirdPartyModels The PropertyModels used to build the bottom row.
+     * @param contentTypes The {@link Set} of {@link ContentType}s to build the preview.
      * @param message The message to show on top of the share sheet.
      */
     void createRecyclerViews(List<PropertyModel> firstPartyModels,
-            List<PropertyModel> thirdPartyModels, String message) {
+            List<PropertyModel> thirdPartyModels, Set<Integer> contentTypes, String message) {
         // A success/failure message can be shown for features such as LinkToText.
         if (!message.isEmpty()) {
             TextView messageView = this.getContentView().findViewById(R.id.message);
@@ -80,10 +99,7 @@
         }
         // If there's no message to be shown, show a preview of the content to be shared.
         else {
-            TextView titleView = this.getContentView().findViewById(R.id.title_preview);
-            titleView.setText(mParams.getTitle());
-            TextView urlView = this.getContentView().findViewById(R.id.url_preview);
-            urlView.setText(mParams.getUrl());
+            createPreview(contentTypes);
         }
 
         createFirstPartyRecyclerViews(firstPartyModels);
@@ -135,12 +151,130 @@
         }
     }
 
-    void setFaviconForPreview(Bitmap icon) {
+    private void createPreview(Set<Integer> contentTypes) {
+        // Default preview is to show title + url.
+        String title = mParams.getTitle();
+        String subtitle = mParams.getUrl();
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_SHARING_HUB_V15)) {
+            fetchFavicon(mParams.getUrl());
+            setTitleStyle(R.style.TextAppearance_TextMediumThick_Primary);
+            setTextForPreview(title, subtitle);
+            return;
+        }
+
+        if (contentTypes.contains(ContentType.IMAGE)) {
+            setImageForPreviewFromUri(mParams.getFileUris().get(0));
+            if (TextUtils.isEmpty(subtitle)) {
+                subtitle = mContext.getResources().getString(
+                        R.string.sharing_hub_image_preview_subtitle);
+            }
+        } else if (contentTypes.contains(ContentType.OTHER_FILE_TYPE)) {
+            // TODO(1120093): Set file icon.
+        } else if (contentTypes.size() == 1
+                && (contentTypes.contains(ContentType.HIGHLIGHTED_TEXT)
+                        || contentTypes.contains(ContentType.TEXT))) {
+            // TODO(1120093): Set text monogram icon.
+            title = "";
+            subtitle = mParams.getText();
+            setSubtitleMaxLines(2);
+        } else {
+            fetchFavicon(mParams.getUrl());
+        }
+
+        if (contentTypes.contains(ContentType.TEXT)
+                && contentTypes.contains(ContentType.LINK_PAGE_NOT_VISIBLE)) {
+            title = mParams.getText();
+            setTitleStyle(R.style.TextAppearance_TextMedium_Primary);
+        } else {
+            setTitleStyle(R.style.TextAppearance_TextMediumThick_Primary);
+        }
+
+        setTextForPreview(title, subtitle);
+    }
+
+    private void setImageForPreviewFromUri(Uri imageUri) {
+        try {
+            setImagePreview(
+                    ApiCompatibilityUtils.getBitmapByUri(mContext.getContentResolver(), imageUri));
+        } catch (IOException e) {
+            // If no image preview available, don't show a preview.
+        }
+    }
+
+    private void setTitleStyle(int resId) {
+        TextView titleView = this.getContentView().findViewById(R.id.title_preview);
+        ApiCompatibilityUtils.setTextAppearance(titleView, resId);
+    }
+
+    private void setTextForPreview(String title, String subtitle) {
+        TextView titleView = this.getContentView().findViewById(R.id.title_preview);
+        titleView.setText(title);
+        TextView subtitleView = this.getContentView().findViewById(R.id.subtitle_preview);
+        subtitleView.setText(subtitle);
+
+        // If there is no title, have subtitleView take up the whole area.
+        if (TextUtils.isEmpty(title)) {
+            titleView.setVisibility(View.GONE);
+        }
+    }
+
+    private void setSubtitleMaxLines(int maxLines) {
+        TextView subtitleView = this.getContentView().findViewById(R.id.subtitle_preview);
+        subtitleView.setMaxLines(maxLines);
+    }
+
+    private void setImagePreview(Bitmap icon) {
         ImageView imageView = this.getContentView().findViewById(R.id.image_preview);
         imageView.setImageBitmap(icon);
     }
 
     /**
+     * Fetches the favicon for the given url.
+     **/
+    private void fetchFavicon(String url) {
+        if (!url.isEmpty()) {
+            mUrl = url;
+            mIconBridge.getLargeIconForUrl(new GURL(url),
+                    mContext.getResources().getDimensionPixelSize(R.dimen.default_favicon_min_size),
+                    this::onFaviconAvailable);
+        }
+    }
+
+    /**
+     * Passed as the callback to {@link LargeIconBridge#getLargeIconForStringUrl}
+     * by showShareSheetWithMessage.
+     */
+    private void onFaviconAvailable(@Nullable Bitmap icon, @ColorInt int fallbackColor,
+            boolean isColorDefault, @IconType int iconType) {
+        // If we didn't get a favicon, generate a monogram instead
+        if (icon == null) {
+            RoundedIconGenerator iconGenerator = createRoundedIconGenerator(fallbackColor);
+            icon = iconGenerator.generateIconForUrl(mUrl);
+            // generateIconForUrl might return null if the URL is empty or the domain cannot be
+            // resolved. See https://crbug.com/987101
+            // TODO(1120093): Handle the case where generating an icon fails.
+            if (icon == null) {
+                return;
+            }
+        }
+
+        int size = mContext.getResources().getDimensionPixelSize(
+                R.dimen.sharing_hub_preview_monogram_size);
+
+        setImagePreview(Bitmap.createScaledBitmap(icon, size, size, true));
+    }
+
+    private RoundedIconGenerator createRoundedIconGenerator(@ColorInt int iconColor) {
+        Resources resources = mContext.getResources();
+        int iconSize = resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_size);
+        int cornerRadius = iconSize / 2;
+        int textSize =
+                resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_text_size);
+
+        return new RoundedIconGenerator(iconSize, iconSize, cornerRadius, iconColor, textSize);
+    }
+
+    /**
      * One-shot reporter that records the first time the user scrolls a {@link RecyclerView}.
      */
     private static class ScrollEventReporter extends RecyclerView.OnScrollListener {
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index 99292e5..6689f69 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -6,12 +6,9 @@
 
 import android.app.Activity;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.view.View;
 
-import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 
@@ -27,14 +24,12 @@
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.ui.favicon.IconType;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.components.browser_ui.share.ShareParams;
-import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -140,8 +135,7 @@
             }
         }
 
-        mBottomSheet = new ShareSheetBottomSheetContent(mActivity, this, params);
-        fetchFavicon(mActivity, params.getUrl());
+        mBottomSheet = new ShareSheetBottomSheetContent(mActivity, mIconBridge, this, params);
 
         mShareStartTime = shareStartTime;
         mContentTypes = ShareSheetPropertyModelBuilder.getContentTypes(params, chromeShareExtras);
@@ -150,7 +144,7 @@
         List<PropertyModel> thirdPartyApps = createThirdPartyPropertyModels(
                 mActivity, params, mContentTypes, chromeShareExtras.saveLastUsed());
 
-        mBottomSheet.createRecyclerViews(firstPartyApps, thirdPartyApps, message);
+        mBottomSheet.createRecyclerViews(firstPartyApps, thirdPartyApps, mContentTypes, message);
 
         boolean shown = mBottomSheetController.requestShowContent(mBottomSheet, true);
         if (shown) {
@@ -258,58 +252,4 @@
         mBottomSheet.getThirdPartyView().requestLayout();
     }
 
-    /** Fetches the favicon for the given url. **/
-    void fetchFavicon(Activity activity, String url) {
-        if (!url.isEmpty()) {
-            // Update mActivity so it's non-null in onFaviconAvailable in tests.
-            mActivity = activity;
-            mUrl = url;
-            mIconBridge.getLargeIconForStringUrl(url,
-                    activity.getResources().getDimensionPixelSize(R.dimen.default_favicon_min_size),
-                    this::onFaviconAvailable);
-        }
-    }
-
-    /**
-     * Passed as the callback to {@link LargeIconBridge#getLargeIconForStringUrl}
-     * by showShareSheetWithMessage.
-     */
-    void onFaviconAvailable(@Nullable Bitmap icon, @ColorInt int fallbackColor,
-            boolean isColorDefault, @IconType int iconType) {
-        // If we didn't get a favicon, generate a monogram instead
-        if (icon == null) {
-            RoundedIconGenerator iconGenerator = createRoundedIconGenerator(fallbackColor);
-            icon = iconGenerator.generateIconForUrl(mUrl);
-            // generateIconForUrl might return null if the URL is empty or the domain cannot be
-            // resolved. See https://crbug.com/987101
-            // TODO(1120093): Handle the case where generating an icon fails.
-            if (icon == null) {
-                return;
-            }
-        }
-
-        int size = mActivity.getResources().getDimensionPixelSize(
-                R.dimen.sharing_hub_preview_monogram_size);
-
-        mIconForPreview = Bitmap.createScaledBitmap(icon, size, size, true);
-
-        if (mBottomSheet != null) {
-            mBottomSheet.setFaviconForPreview(mIconForPreview);
-        }
-    }
-
-    private RoundedIconGenerator createRoundedIconGenerator(@ColorInt int iconColor) {
-        Resources resources = mActivity.getResources();
-        int iconSize = resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_size);
-        int cornerRadius = iconSize / 2;
-        int textSize =
-                resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_text_size);
-
-        return new RoundedIconGenerator(iconSize, iconSize, cornerRadius, iconColor, textSize);
-    }
-
-    @VisibleForTesting
-    Bitmap getIconForPreview() {
-        return mIconForPreview;
-    }
 }
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index 3786a33..00f7a02b 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -35,6 +35,18 @@
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetView.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewBinder.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java",
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
index f768a0cfd..2a5b5d1 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java
@@ -11,6 +11,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
+import android.content.Context;
 
 import androidx.test.filters.SmallTest;
 
@@ -19,11 +20,14 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -31,10 +35,26 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class LinkToTextCoordinatorTest {
+    // Mock class for |LinkToTextCoordinator| that disables |requestSelector| call.
+    private class MockLinkToTextCoordinator extends LinkToTextCoordinator {
+        public MockLinkToTextCoordinator(Context context, Tab tab,
+                ChromeOptionShareCallback chromeOptionShareCallback, String visibleUrl,
+                String selectedText) {
+            super(context, tab, chromeOptionShareCallback, visibleUrl, selectedText);
+        }
+
+        @Override
+        public void requestSelector() {}
+    };
+
     @Mock
     private ChromeOptionShareCallback mShareCallback;
     @Mock
     private WindowAndroid mWindow;
+    @Mock
+    private Tab mTab;
+    @Mock
+    private WebContents mWebContents;
 
     private Activity mAcivity;
     private static final String SELECTED_TEXT = "selection";
@@ -47,6 +67,8 @@
         doNothing()
                 .when(mShareCallback)
                 .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
+        Mockito.when(mTab.getWebContents()).thenReturn(mWebContents);
+        Mockito.when(mTab.getWindowAndroid()).thenReturn(mWindow);
     }
 
     @Test
@@ -54,8 +76,8 @@
     public void getTextToShareTest() {
         String selector = "selector";
         String expectedTextToShare = "\"selection\"\nwww.example.com#:~:text=selector";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
@@ -64,8 +86,8 @@
     public void getTextToShareTest_URLWithFragment() {
         String selector = "selector";
         String expectedTextToShare = "\"selection\"\nwww.example.com#:~:text=selector";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL + "#elementid", SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL + "#elementid", SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
@@ -74,17 +96,29 @@
     public void getTextToShareTest_EmptySelector() {
         String selector = "";
         String expectedTextToShare = "\"selection\"\nwww.example.com";
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         Assert.assertEquals(expectedTextToShare, coordinator.getTextToShare(selector));
     }
 
     @Test
     @SmallTest
     public void onSelectorReadyTest() {
-        LinkToTextCoordinator coordinator = new LinkToTextCoordinator(
-                mAcivity, mWindow, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
         // OnSelectorReady should call back the share sheet.
+        coordinator.onSelectorReady("selector");
+        verify(mShareCallback)
+                .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
+    }
+
+    @Test
+    @SmallTest
+    public void onSelectorReadyTest_EmptySelector() {
+        MockLinkToTextCoordinator coordinator = new MockLinkToTextCoordinator(
+                mAcivity, mTab, mShareCallback, VISIBLE_URL, SELECTED_TEXT);
+        // OnSelectorReady should call back the share sheet.
+        coordinator.onSelectorReady("");
         verify(mShareCallback)
                 .showThirdPartyShareSheetWithMessage(anyString(), any(), any(), anyLong());
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManagerTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManagerTest.java
similarity index 96%
rename from chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManagerTest.java
rename to chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManagerTest.java
index 695f3fc4..9f6381b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/NotificationSharedPrefManagerTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManagerTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import android.support.test.filters.SmallTest;
 
@@ -16,7 +16,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.send_tab_to_self.NotificationSharedPrefManager.ActiveNotification;
+import org.chromium.chrome.browser.share.send_tab_to_self.NotificationSharedPrefManager.ActiveNotification;
 
 /** Tests for NotificationSharedPrefManagerTest */
 @RunWith(BaseRobolectricTestRunner.class)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
similarity index 97%
rename from chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
rename to chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
index f18ab32..b39c1e7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import static org.mockito.AdditionalAnswers.answerVoid;
 import static org.mockito.Mockito.any;
@@ -27,7 +27,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.send_tab_to_self.TargetDeviceInfo.DeviceType;
+import org.chromium.chrome.browser.share.send_tab_to_self.TargetDeviceInfo.DeviceType;
 import org.chromium.content_public.browser.WebContents;
 
 import java.util.Arrays;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java
similarity index 96%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinatorTest.java
rename to chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java
index 94e112ee..9507e44 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.send_tab_to_self;
+package org.chromium.chrome.browser.share.send_tab_to_self;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.eq;
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
index 71ed137d..2c3a5fa 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
@@ -307,16 +307,17 @@
         Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled);
 
         ShareParams shareParams = new ShareParams.Builder(null, /*title=*/"", /*url=*/"").build();
-        mChromeProvidedSharingOptionsProvider = new ChromeProvidedSharingOptionsProvider(mActivity,
-                mTabProvider,
-                /*bottomSheetController=*/null,
-                new ShareSheetBottomSheetContent(mActivity, mShareSheetCoordinator, shareParams),
-                new ShareParams.Builder(null, "", "").build(),
-                new ChromeShareExtras.Builder().build(),
-                /*TabPrinterDelegate=*/null,
-                /*settingsLauncher=*/null,
-                /*syncState=*/false,
-                /*shareStartTime=*/0, mShareSheetCoordinator);
+        mChromeProvidedSharingOptionsProvider =
+                new ChromeProvidedSharingOptionsProvider(mActivity, mTabProvider,
+                        /*bottomSheetController=*/null,
+                        new ShareSheetBottomSheetContent(
+                                mActivity, null, mShareSheetCoordinator, shareParams),
+                        new ShareParams.Builder(null, "", "").build(),
+                        new ChromeShareExtras.Builder().build(),
+                        /*TabPrinterDelegate=*/null,
+                        /*settingsLauncher=*/null,
+                        /*syncState=*/false,
+                        /*shareStartTime=*/0, mShareSheetCoordinator);
     }
 
     private void assertCorrectModelsAreInTheRightOrder(
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java
new file mode 100644
index 0000000..f8e5bcc
--- /dev/null
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java
@@ -0,0 +1,226 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share.share_sheet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.support.test.rule.ActivityTestRule;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.test.filters.MediumTest;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType;
+import org.chromium.chrome.browser.ui.favicon.IconType;
+import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
+import org.chromium.chrome.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.ui.test.util.DummyUiActivity;
+import org.chromium.url.GURL;
+
+import java.util.ArrayList;
+
+/**
+ * Tests {@link ShareSheetBottomSheetContent}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@Features.EnableFeatures({ChromeFeatureList.CHROME_SHARING_HUB_V15})
+public final class ShareSheetBottomSheetContentTest {
+    @Rule
+    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+
+    @Rule
+    public ActivityTestRule<DummyUiActivity> mActivityTestRule =
+            new ActivityTestRule<>(DummyUiActivity.class);
+
+    private static final Bitmap.Config sConfig = Bitmap.Config.ALPHA_8;
+    private static final Uri sImageUri = Uri.parse("content://testImage.png");
+    private static final String sText = "Text";
+    private static final String sTitle = "Title";
+    private static final String sUrl = "https://www.example.com";
+
+    private Activity mActivity;
+    private ShareParams mShareParams;
+    private ShareSheetBottomSheetContent mShareSheetBottomSheetContent;
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityTestRule.getActivity();
+        mShareParams = new ShareParams.Builder(/*window=*/null, sTitle, sUrl)
+                               .setText(sText)
+                               .setFileUris(new ArrayList<>(ImmutableList.of(sImageUri)))
+                               .build();
+
+        mShareSheetBottomSheetContent = new ShareSheetBottomSheetContent(
+                mActivity, new MockLargeIconBridge(), null, mShareParams);
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_hasMessage_doesNotShowPreview() {
+        mShareSheetBottomSheetContent.createRecyclerViews(
+                ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(), "message");
+
+        TextView titleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        ImageView imageView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
+        assertEquals("", titleView.getText());
+        assertEquals("", subtitleView.getText());
+        assertNull(imageView.getDrawable());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_imageOnlyShare() {
+        ShareSheetBottomSheetContent shareSheetBottomSheetContent =
+                new ShareSheetBottomSheetContent(mActivity, new MockLargeIconBridge(), null,
+                        new ShareParams.Builder(/*window=*/null, /*title=*/"", /*url=*/"")
+                                .setFileUris(new ArrayList<>(ImmutableList.of(sImageUri)))
+                                .build());
+
+        shareSheetBottomSheetContent.createRecyclerViews(
+                ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(ContentType.IMAGE), "");
+
+        TextView titleView =
+                shareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                shareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        assertEquals("", titleView.getText());
+        assertEquals(mActivity.getString(R.string.sharing_hub_image_preview_subtitle),
+                subtitleView.getText());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_highlightedTextShare() {
+        mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
+                ImmutableSet.of(ContentType.HIGHLIGHTED_TEXT), "");
+
+        TextView titleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        assertEquals(View.GONE, titleView.getVisibility());
+        assertEquals(mShareParams.getText(), subtitleView.getText());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_textOnlyShare() {
+        mShareSheetBottomSheetContent.createRecyclerViews(
+                ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(ContentType.TEXT), "");
+
+        TextView titleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        assertEquals(View.GONE, titleView.getVisibility());
+        assertEquals(mShareParams.getText(), subtitleView.getText());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_producesCorrectFavicon() {
+        mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
+                ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), "");
+
+        ImageView imageView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
+        assertNotNull(imageView.getDrawable());
+        Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
+        int size = mActivity.getResources().getDimensionPixelSize(
+                R.dimen.sharing_hub_preview_monogram_size);
+        assertEquals(size, bitmap.getWidth());
+        assertEquals(size, bitmap.getHeight());
+        assertEquals(sConfig, bitmap.getConfig());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_tabShare() {
+        mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
+                ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), "");
+
+        TextView titleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        ImageView imageView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
+        assertEquals(mShareParams.getTitle(), titleView.getText());
+        assertEquals(mShareParams.getUrl(), subtitleView.getText());
+        assertNotNull(imageView.getDrawable());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_webShareTextAndUrl() {
+        mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
+                ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.TEXT), "");
+
+        TextView titleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        ImageView imageView =
+                mShareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
+        assertEquals(mShareParams.getText(), titleView.getText());
+        assertEquals(mShareParams.getUrl(), subtitleView.getText());
+        assertNotNull(imageView.getDrawable());
+    }
+
+    @Test
+    @MediumTest
+    public void createRecyclerViews_webShareUrl() {
+        ShareSheetBottomSheetContent shareSheetBottomSheetContent =
+                new ShareSheetBottomSheetContent(mActivity, new MockLargeIconBridge(), null,
+                        new ShareParams.Builder(/*window=*/null, /*title=*/"", sUrl).build());
+
+        shareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
+                ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE), "");
+
+        TextView titleView =
+                shareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
+        TextView subtitleView =
+                shareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
+        ImageView imageView =
+                shareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
+        assertEquals(View.GONE, titleView.getVisibility());
+        assertEquals(mShareParams.getUrl(), subtitleView.getText());
+        assertNotNull(imageView.getDrawable());
+    }
+
+    private static class MockLargeIconBridge extends LargeIconBridge {
+        @Override
+        public boolean getLargeIconForUrl(
+                GURL pageUrl, int desiredSizePx, final LargeIconBridge.LargeIconCallback callback) {
+            callback.onLargeIconAvailable(
+                    Bitmap.createBitmap(48, 84, sConfig), 0, false, IconType.INVALID);
+            return true;
+        }
+    }
+}
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
index 8ea6f4b..1c699e36 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
@@ -12,7 +12,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
-import android.graphics.Bitmap;
 import android.support.test.rule.ActivityTestRule;
 
 import androidx.test.filters.MediumTest;
@@ -29,8 +28,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.ui.favicon.IconType;
-import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
@@ -74,8 +71,6 @@
     private Activity mActivity;
     private ShareSheetCoordinator mShareSheetCoordinator;
 
-    private static Bitmap.Config sConfig = Bitmap.Config.ALPHA_8;
-
     @Before
     public void setUp() {
         mActivity = mActivityTestRule.getActivity();
@@ -99,7 +94,7 @@
                 .thenReturn(thirdPartyPropertyModels);
 
         mShareSheetCoordinator = new ShareSheetCoordinator(mController, mLifecycleDispatcher, null,
-                mPropertyModelBuilder, null, new MockLargeIconBridge(), null, false);
+                mPropertyModelBuilder, null, null, null, false);
     }
 
     @Test
@@ -129,28 +124,4 @@
                 mActivity.getResources().getString(R.string.sharing_more_icon_label),
                 propertyModels.get(2).get(ShareSheetItemViewProperties.LABEL));
     }
-
-    @Test
-    @MediumTest
-    public void testFetchFavicon() {
-        Activity activity = mActivityTestRule.getActivity();
-        mShareSheetCoordinator.fetchFavicon(activity, "https://www.example.com");
-
-        Bitmap bitmap = mShareSheetCoordinator.getIconForPreview();
-        int size = activity.getResources().getDimensionPixelSize(
-                R.dimen.sharing_hub_preview_monogram_size);
-        assertEquals(size, bitmap.getWidth());
-        assertEquals(size, bitmap.getHeight());
-        assertEquals(sConfig, bitmap.getConfig());
-    }
-
-    private static class MockLargeIconBridge extends LargeIconBridge {
-        @Override
-        public boolean getLargeIconForStringUrl(String pageUrl, int desiredSizePx,
-                final LargeIconBridge.LargeIconCallback callback) {
-            callback.onLargeIconAvailable(
-                    Bitmap.createBitmap(48, 84, sConfig), 0, false, IconType.INVALID);
-            return true;
-        }
-    }
 }
diff --git a/chrome/browser/share/android/test_java_sources.gni b/chrome/browser/share/android/test_java_sources.gni
index 3f5c335..a881a58 100644
--- a/chrome/browser/share/android/test_java_sources.gni
+++ b/chrome/browser/share/android/test_java_sources.gni
@@ -7,7 +7,9 @@
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/clipboard/ClipboardImageFileProviderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetSaveDelegateTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java",
 ]
@@ -15,4 +17,6 @@
 share_junit_test_java_sources = [
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediatorUnitTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManagerTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java",
 ]
diff --git a/chrome/browser/sharesheet/drive_share_action.cc b/chrome/browser/sharesheet/drive_share_action.cc
index 018c641..ceeff8b 100644
--- a/chrome/browser/sharesheet/drive_share_action.cc
+++ b/chrome/browser/sharesheet/drive_share_action.cc
@@ -28,7 +28,8 @@
 }
 
 const gfx::ImageSkia DriveShareAction::GetActionIcon() {
-  // TODO(crbug.com/1097623): Get the icon.
+  // TODO(crbug.com/1127750): Update to create the Icon at the
+  // Sharesheet bubble view. Only get the VectorIcon here.
   return gfx::ImageSkia();
 }
 
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 82a40f8..00a0bfc 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -180,13 +180,6 @@
   return security_state::InsecureInputEventData();
 }
 
-security_state::SecurityLevel GetLevelForPassiveMixedContent() {
-  return base::FeatureList::IsEnabled(
-             security_state::features::kPassiveMixedContentWarning)
-             ? security_state::WARNING
-             : security_state::NONE;
-}
-
 // A delegate class that allows emulating selection of a file for an
 // INPUT TYPE=FILE form field.
 class FileChooserDelegate : public content::WebContentsDelegate {
@@ -778,7 +771,7 @@
                                https_server_.GetURL(replacement_path));
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      GetLevelForPassiveMixedContent(), false, true, false,
+      security_state::WARNING, false, true, false,
       false /* expect cert status error */);
 
   // Navigate to an HTTPS page that displays mixed content dynamically.
@@ -798,7 +791,7 @@
   EXPECT_TRUE(js_result);
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      GetLevelForPassiveMixedContent(), false, true, false,
+      security_state::WARNING, false, true, false,
       false /* expect cert status error */);
 
   // Navigate to an HTTPS page that runs mixed content.
@@ -842,7 +835,7 @@
   SecurityStateTabHelperTestWithAutoupgradesDisabledAndMixedContentWarningEnabled() {
     feature_list.InitWithFeatures(
         /* enabled_features */
-        {security_state::features::kPassiveMixedContentWarning},
+        {},
         /* disabled_features */
         {blink::features::kMixedContentAutoupgrade});
   }
@@ -871,7 +864,7 @@
                                https_server_.GetURL(replacement_path));
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      GetLevelForPassiveMixedContent(), false, true, false,
+      security_state::WARNING, false, true, false,
       false /* expect cert status error */);
 
   // Navigate to an HTTPS page that displays mixed content dynamically.
@@ -891,7 +884,7 @@
   EXPECT_TRUE(js_result);
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      GetLevelForPassiveMixedContent(), false, true, false,
+      security_state::WARNING, false, true, false,
       false /* expect cert status error */);
 }
 
@@ -1634,9 +1627,7 @@
   ui_test_utils::NavigateToURL(browser(), mixed_content_url);
 
   blink::SecurityStyle expected_mixed_content_security_style =
-      base::FeatureList::IsEnabled(
-          security_state::features::kPassiveMixedContentWarning) &&
-              security_state::ShouldShowDangerTriangleForWarningLevel()
+      security_state::ShouldShowDangerTriangleForWarningLevel()
           ? blink::SecurityStyle::kInsecure
           : blink::SecurityStyle::kNeutral;
   EXPECT_EQ(expected_mixed_content_security_style,
@@ -2231,8 +2222,7 @@
                                        "i.src = 'http://example.test';"
                                        "document.body.appendChild(i);"));
     observer.WaitForDidChangeVisibleSecurityState();
-    histograms.ExpectUniqueSample(kHistogramName,
-                                  GetLevelForPassiveMixedContent(), 1);
+    histograms.ExpectUniqueSample(kHistogramName, security_state::WARNING, 1);
   }
 
   // Navigate away and the histogram should be recorded exactly once again, when
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index fbccb35e..21400af8 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -234,14 +234,6 @@
   SSL_INTERSTITIAL_DO_NOT_PROCEED
 };
 
-security_state::SecurityLevel GetPassiveMixedContentSecurityLevel() {
-  if (base::FeatureList::IsEnabled(
-          security_state::features::kPassiveMixedContentWarning)) {
-    return security_state::WARNING;
-  }
-  return security_state::NONE;
-}
-
 // This observer waits for the SSLErrorHandler to start an interstitial timer
 // for the given web contents.
 class SSLInterstitialTimerObserver {
@@ -1178,7 +1170,7 @@
                                      "document.body.appendChild(i);"));
   observer.WaitForDidChangeVisibleSecurityState();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 
   // Now navigate somewhere else, and then back to the page that dynamically
@@ -1209,7 +1201,7 @@
                                      "document.body.appendChild(i);"));
   security_state_observer.WaitForDidChangeVisibleSecurityState();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 
   // Initiate a same-document navigation and check that the page is still
@@ -1219,7 +1211,7 @@
                                https_server_.GetURL("/ssl/google.html#foo"));
   navigation_observer.WaitForSameDocumentNavigation();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
@@ -2387,8 +2379,7 @@
 
   ssl_test_util::CheckSecurityState(
       browser()->tab_strip_model()->GetActiveWebContents(), CertError::NONE,
-      GetPassiveMixedContentSecurityLevel(),
-      AuthState::DISPLAYED_INSECURE_CONTENT);
+      security_state::WARNING, AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
 // Visits a page that displays an insecure form.
@@ -2718,7 +2709,7 @@
 
   // We should now have insecure content.
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
@@ -2756,7 +2747,7 @@
 
   // The new tab has insecure content.
   ssl_test_util::CheckSecurityState(tab2, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 
   // The original tab should not be contaminated.
@@ -2833,7 +2824,7 @@
   const GURL url_https = https_server_.GetURL(replacement_path);
   ui_test_utils::NavigateToURL(browser(), url_https);
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
@@ -3158,7 +3149,7 @@
 
   run_loop.Run();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
@@ -5680,7 +5671,7 @@
   EXPECT_TRUE(content::WaitForLoadStop(tab));
 
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
-                                    GetPassiveMixedContentSecurityLevel(),
+                                    security_state::WARNING,
                                     AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
diff --git a/chrome/browser/subresource_filter/ads_intervention_manager.cc b/chrome/browser/subresource_filter/ads_intervention_manager.cc
new file mode 100644
index 0000000..8b78ab1
--- /dev/null
+++ b/chrome/browser/subresource_filter/ads_intervention_manager.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 The Chromium Authors. 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/subresource_filter/ads_intervention_manager.h"
+
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
+#include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Key into the website settings dict for last active ads violation.
+const char kLastAdsViolationTimeKey[] = "LastAdsViolationTime";
+const char kLastAdsViolationKey[] = "LastAdsViolation";
+
+}  // namespace
+
+using subresource_filter::mojom::AdsViolation;
+
+AdsInterventionManager::AdsInterventionManager(
+    SubresourceFilterContentSettingsManager* settings_manager)
+    : settings_manager_(settings_manager),
+      clock_(base::DefaultClock::GetInstance()) {}
+
+AdsInterventionManager::~AdsInterventionManager() = default;
+
+void AdsInterventionManager::TriggerAdsInterventionForUrlOnSubsequentLoads(
+    const GURL& url,
+    AdsViolation ads_violation) {
+  std::unique_ptr<base::DictionaryValue> additional_metadata =
+      std::make_unique<base::DictionaryValue>();
+
+  double now = clock_->Now().ToDoubleT();
+  additional_metadata->SetDouble(kLastAdsViolationTimeKey, now);
+  additional_metadata->SetInteger(kLastAdsViolationKey,
+                                  static_cast<int>(ads_violation));
+
+  bool activated = base::FeatureList::IsEnabled(
+      subresource_filter::kAdsInterventionsEnforced);
+  // This is a no-op if the metadata already exists for an active
+  // ads intervention.
+  settings_manager_->SetSiteMetadataBasedOnActivation(
+      url, activated,
+      SubresourceFilterContentSettingsManager::ActivationSource::
+          kAdsIntervention,
+      std::move(additional_metadata));
+}
+
+base::Optional<AdsInterventionManager::LastAdsIntervention>
+AdsInterventionManager::GetLastAdsIntervention(const GURL& url) const {
+  int ads_violation;
+  double last_violation_time;
+  // The last active ads intervention is stored in the site metadata.
+  std::unique_ptr<base::DictionaryValue> dict =
+      settings_manager_->GetSiteMetadata(url);
+
+  if (dict && dict->GetInteger(kLastAdsViolationKey, &ads_violation) &&
+      dict->GetDouble(kLastAdsViolationTimeKey, &last_violation_time)) {
+    base::TimeDelta diff =
+        clock_->Now() - base::Time::FromDoubleT(last_violation_time);
+
+    return LastAdsIntervention(
+        {diff, static_cast<AdsViolation>(ads_violation)});
+  }
+
+  return base::nullopt;
+}
diff --git a/chrome/browser/subresource_filter/ads_intervention_manager.h b/chrome/browser/subresource_filter/ads_intervention_manager.h
new file mode 100644
index 0000000..9ad9d126
--- /dev/null
+++ b/chrome/browser/subresource_filter/ads_intervention_manager.h
@@ -0,0 +1,80 @@
+// Copyright 2020 The Chromium 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_SUBRESOURCE_FILTER_ADS_INTERVENTION_MANAGER_H_
+#define CHROME_BROWSER_SUBRESOURCE_FILTER_ADS_INTERVENTION_MANAGER_H_
+
+#include <memory>
+
+#include "base/optional.h"
+#include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
+#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
+
+class GURL;
+
+namespace base {
+class Clock;
+}
+
+// This class tracks ads interventions that have occurred on origins and is
+// bound to the user's profile. The ads intervention manager operates in two
+// modes set by the feature flag kAdsInterventionsEnforced:
+// 1. Dry run: Ads are not blocked on sites with ad interventions, however,
+//    the ads intervention manager records metrics as if ads were blocked.
+//    If the ads intervention manager is asked to intervene on the same URL
+//    in the period where we would block ads during enforcement, it will only
+//    record the first seen intervention.
+// 2. Enforced: Ads are blocked on sites with ad interventions.
+//
+// The duration of an ad intervention is set by the feature flag
+// kAdsInterventionDuration.
+//
+// This class maintain's metadata for ads interventions in the user's website
+// settings. This is persisted to disk and cleared with browsing history. The
+// content subresource filter manager expires ads intervention metadata after
+// 7 days. As a result, kAdsInterventionDuration should be less than 7 days
+// to prevent expiry from impacting metrics. The metadata is scoped to each
+// url's origin. This API would ideally work with Origins insead of GURLs,
+// however, downstream APIs use GURL's.
+class AdsInterventionManager {
+ public:
+  // Struct representing the last triggered ads intervention.
+  struct LastAdsIntervention {
+    base::TimeDelta duration_since;
+    subresource_filter::mojom::AdsViolation ads_violation;
+  };
+
+  // The content_settings_manager should outlive the ads intervention manager.
+  // This is satisfied as the SubresourceFilterContentSettingsManager and the
+  // AdsInterventionManager are both bound to the profile.
+  explicit AdsInterventionManager(
+      SubresourceFilterContentSettingsManager* content_settings_manager);
+  ~AdsInterventionManager();
+  AdsInterventionManager(const AdsInterventionManager&) = delete;
+  AdsInterventionManager& operator=(const AdsInterventionManager&) = delete;
+
+  // The ads intervention manager should trigger an ads intervention on each
+  // subsequent page load to |url| for kAdsInterventionDuration. The active
+  // intervention is recorded in the user's website settings and updates
+  // |url| site metadata with the last active intervention.
+  void TriggerAdsInterventionForUrlOnSubsequentLoads(
+      const GURL& url,
+      subresource_filter::mojom::AdsViolation ads_violation);
+
+  // Returns the last active ads intervention written to metadata,
+  // otherwise base::nullopt is returned.
+  base::Optional<LastAdsIntervention> GetLastAdsIntervention(
+      const GURL& url) const;
+
+  void set_clock_for_testing(base::Clock* clock) { clock_ = clock; }
+
+ private:
+  // The SubresourceFilterContentSettingsManager is guaranteed to outlive the
+  // AdsInterventionManager. Both are bound to the profile.
+  SubresourceFilterContentSettingsManager* settings_manager_ = nullptr;
+
+  base::Clock* clock_;
+};
+
+#endif  // CHROME_BROWSER_SUBRESOURCE_FILTER_ADS_INTERVENTION_MANAGER_H_
diff --git a/chrome/browser/subresource_filter/ads_intervention_manager_browsertest.cc b/chrome/browser/subresource_filter/ads_intervention_manager_browsertest.cc
new file mode 100644
index 0000000..37bae42
--- /dev/null
+++ b/chrome/browser/subresource_filter/ads_intervention_manager_browsertest.cc
@@ -0,0 +1,166 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
+#include "chrome/browser/subresource_filter/ads_intervention_manager.h"
+#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
+#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace subresource_filter {
+
+namespace {
+
+const char kSubresourceFilterActionsHistogram[] = "SubresourceFilter.Actions2";
+
+}  // namespace
+
+class AdsInterventionManagerTestWithEnforcement
+    : public SubresourceFilterBrowserTest {
+ public:
+  AdsInterventionManagerTestWithEnforcement() {
+    feature_list_.InitAndEnableFeature(
+        subresource_filter::kAdsInterventionsEnforced);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(AdsInterventionManagerTestWithEnforcement,
+                       AdsInterventionEnforced_PageActivated) {
+  base::HistogramTester histogram_tester;
+  ChromeSubresourceFilterClient* client =
+      ChromeSubresourceFilterClient::FromWebContents(web_contents());
+  auto test_clock = std::make_unique<base::SimpleTestClock>();
+  ads_intervention_manager()->set_clock_for_testing(test_clock.get());
+
+  const GURL url(
+      GetTestUrl("subresource_filter/frame_with_included_script.html"));
+  ASSERT_NO_FATAL_FAILURE(
+      SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
+
+  // Should not trigger activation as the URL is not on the blocklist and
+  // has no active ads interventions.
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 0);
+
+  // Trigger an ads violation and renavigate the page. Should trigger
+  // subresource filter activation.
+  client->OnAdsViolationTriggered(
+      web_contents()->GetMainFrame(),
+      mojom::AdsViolation::kMobileAdDensityByHeightAbove30);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 1);
+
+  // Advance the clock to clear the intervention.
+  test_clock->Advance(subresource_filter::kAdsInterventionDuration.Get());
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    AdsInterventionManagerTestWithEnforcement,
+    MultipleAdsInterventions_PageActivationClearedAfterFirst) {
+  base::HistogramTester histogram_tester;
+  ChromeSubresourceFilterClient* client =
+      ChromeSubresourceFilterClient::FromWebContents(web_contents());
+  auto test_clock = std::make_unique<base::SimpleTestClock>();
+  ads_intervention_manager()->set_clock_for_testing(test_clock.get());
+
+  const GURL url(
+      GetTestUrl("subresource_filter/frame_with_included_script.html"));
+  ASSERT_NO_FATAL_FAILURE(
+      SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
+
+  // Should not trigger activation as the URL is not on the blocklist and
+  // has no active ads interventions.
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 0);
+
+  // Trigger an ads violation and renavigate the page. Should trigger
+  // subresource filter activation.
+  client->OnAdsViolationTriggered(
+      web_contents()->GetMainFrame(),
+      mojom::AdsViolation::kMobileAdDensityByHeightAbove30);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 1);
+
+  // Advance the clock by less than kAdsInterventionDuration and trigger another
+  // intervention. This intervention is a no-op.
+  test_clock->Advance(subresource_filter::kAdsInterventionDuration.Get() -
+                      base::TimeDelta::FromMinutes(30));
+  client->OnAdsViolationTriggered(
+      web_contents()->GetMainFrame(),
+      mojom::AdsViolation::kMobileAdDensityByHeightAbove30);
+
+  // Advance the clock to to kAdsInterventionDuration from the first
+  // intervention, this clear the intervention.
+  test_clock->Advance(base::TimeDelta::FromMinutes(30));
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+}
+
+class AdsInterventionManagerTestWithoutEnforcement
+    : public SubresourceFilterBrowserTest {
+ public:
+  AdsInterventionManagerTestWithoutEnforcement() {
+    feature_list_.InitAndDisableFeature(
+        subresource_filter::kAdsInterventionsEnforced);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(AdsInterventionManagerTestWithoutEnforcement,
+                       AdsInterventionNotEnforced_NoPageActivation) {
+  base::HistogramTester histogram_tester;
+  ChromeSubresourceFilterClient* client =
+      ChromeSubresourceFilterClient::FromWebContents(web_contents());
+
+  const GURL url(
+      GetTestUrl("subresource_filter/frame_with_included_script.html"));
+  ASSERT_NO_FATAL_FAILURE(
+      SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
+
+  // Should not trigger activation as the URL is not on the blocklist and
+  // has no active ads interventions.
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 0);
+
+  // Trigger an ads violation and renavigate to the page. Interventions are not
+  // enforced so no activation should occur.
+  client->OnAdsViolationTriggered(
+      web_contents()->GetMainFrame(),
+      mojom::AdsViolation::kMobileAdDensityByHeightAbove30);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+  histogram_tester.ExpectBucketCount(kSubresourceFilterActionsHistogram,
+                                     SubresourceFilterAction::kUIShown, 0);
+}
+
+}  // namespace subresource_filter
diff --git a/chrome/browser/subresource_filter/ads_intervention_manager_unittest.cc b/chrome/browser/subresource_filter/ads_intervention_manager_unittest.cc
new file mode 100644
index 0000000..1d99ae3
--- /dev/null
+++ b/chrome/browser/subresource_filter/ads_intervention_manager_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 The Chromium Authors. 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/subresource_filter/ads_intervention_manager.h"
+
+#include <memory>
+
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "chrome/browser/subresource_filter/subresource_filter_profile_context.h"
+#include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using subresource_filter::mojom::AdsViolation;
+
+class AdsInterventionManagerTest : public testing::Test {
+ public:
+  AdsInterventionManagerTest() = default;
+  AdsInterventionManagerTest(const AdsInterventionManagerTest&) = delete;
+  AdsInterventionManagerTest& operator=(const AdsInterventionManagerTest&) =
+      delete;
+
+  void SetUp() override {
+    ads_intervention_manager_ =
+        SubresourceFilterProfileContextFactory::GetForProfile(&testing_profile_)
+            ->ads_intervention_manager();
+
+    test_clock_ = std::make_unique<base::SimpleTestClock>();
+    ads_intervention_manager_->set_clock_for_testing(test_clock_.get());
+  }
+
+  base::SimpleTestClock* test_clock() { return test_clock_.get(); }
+
+ protected:
+  // Owned by the testing_profile_.
+  AdsInterventionManager* ads_intervention_manager_ = nullptr;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  TestingProfile testing_profile_;
+
+  std::unique_ptr<base::SimpleTestClock> test_clock_;
+};
+
+TEST_F(AdsInterventionManagerTest,
+       NoIntervention_NoActiveInterventionReturned) {
+  GURL url("https://example.test/");
+
+  base::Optional<AdsInterventionManager::LastAdsIntervention> ads_intervention =
+      ads_intervention_manager_->GetLastAdsIntervention(url);
+  EXPECT_FALSE(ads_intervention.has_value());
+}
+
+TEST_F(AdsInterventionManagerTest, SingleIntervention_TimeSinceMatchesClock) {
+  GURL url("https://example.test/");
+
+  ads_intervention_manager_->TriggerAdsInterventionForUrlOnSubsequentLoads(
+      url, AdsViolation::kMobileAdDensityByHeightAbove30);
+  test_clock()->Advance(base::TimeDelta::FromHours(1));
+
+  base::Optional<AdsInterventionManager::LastAdsIntervention> ads_intervention =
+      ads_intervention_manager_->GetLastAdsIntervention(url);
+  EXPECT_TRUE(ads_intervention.has_value());
+  EXPECT_EQ(ads_intervention->ads_violation,
+            AdsViolation::kMobileAdDensityByHeightAbove30);
+  EXPECT_EQ(ads_intervention->duration_since, base::TimeDelta::FromHours(1));
+
+  // Advance the clock by two hours, duration since should now be 3 hours.
+  test_clock()->Advance(base::TimeDelta::FromHours(2));
+  ads_intervention = ads_intervention_manager_->GetLastAdsIntervention(url);
+  EXPECT_TRUE(ads_intervention.has_value());
+  EXPECT_EQ(ads_intervention->ads_violation,
+            AdsViolation::kMobileAdDensityByHeightAbove30);
+  EXPECT_EQ(ads_intervention->duration_since, base::TimeDelta::FromHours(3));
+}
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
index 86555fbc..ec11f2c4f 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/subresource_filter/ads_intervention_manager.h"
 #include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context.h"
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
@@ -31,6 +32,8 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h"
@@ -40,10 +43,8 @@
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents) {
   DCHECK(web_contents);
-  SubresourceFilterProfileContext* context =
-      SubresourceFilterProfileContextFactory::GetForProfile(
-          Profile::FromBrowserContext(web_contents->GetBrowserContext()));
-  settings_manager_ = context->settings_manager();
+  profile_context_ = SubresourceFilterProfileContextFactory::GetForProfile(
+      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
 
   subresource_filter::RulesetService* ruleset_service =
       g_browser_process->subresource_filter_ruleset_service();
@@ -94,7 +95,8 @@
     return;
 
   const GURL& top_level_url = web_contents()->GetLastCommittedURL();
-  if (settings_manager_->ShouldShowUIForSite(top_level_url)) {
+  if (profile_context_->settings_manager()->ShouldShowUIForSite(
+          top_level_url)) {
     ShowUI(top_level_url);
   } else {
     LogAction(SubresourceFilterAction::kUISuppressed);
@@ -117,8 +119,26 @@
   }
 
   const GURL& url(navigation_handle->GetURL());
+
+  base::Optional<AdsInterventionManager::LastAdsIntervention>
+      last_intervention =
+          profile_context_->ads_intervention_manager()->GetLastAdsIntervention(
+              url);
+
+  // Only activate the subresource filter if we are intervening on
+  // ads
+  if (profile_context_->settings_manager()->GetSiteActivationFromMetadata(
+          url) &&
+      last_intervention &&
+      last_intervention->duration_since <
+          subresource_filter::kAdsInterventionDuration.Get()) {
+    effective_activation_level =
+        subresource_filter::mojom::ActivationLevel::kEnabled;
+    *decision = subresource_filter::ActivationDecision::ACTIVATED;
+  }
+
   if (url.SchemeIsHTTPOrHTTPS()) {
-    settings_manager_->SetSiteMetadataBasedOnActivation(
+    profile_context_->settings_manager()->SetSiteMetadataBasedOnActivation(
         url,
         effective_activation_level ==
             subresource_filter::mojom::ActivationLevel::kEnabled,
@@ -126,19 +146,43 @@
             kSafeBrowsing);
   }
 
-  if (settings_manager_->GetSitePermission(url) == CONTENT_SETTING_ALLOW) {
+  if (profile_context_->settings_manager()->GetSitePermission(url) ==
+      CONTENT_SETTING_ALLOW) {
     if (effective_activation_level ==
         subresource_filter::mojom::ActivationLevel::kEnabled) {
       *decision = subresource_filter::ActivationDecision::URL_ALLOWLISTED;
     }
     return subresource_filter::mojom::ActivationLevel::kDisabled;
   }
+
   return effective_activation_level;
 }
 
+void ChromeSubresourceFilterClient::OnAdsViolationTriggered(
+    content::RenderFrameHost* rfh,
+    subresource_filter::mojom::AdsViolation triggered_violation) {
+  // If the feature is disabled, simulate ads interventions as if we were
+  // enforcing on ads: do not record new interventions if we would be enforcing
+  // an intervention on ads already.
+  // TODO(https://crbug/1107998): Verify this behavior when violation signals
+  // and histograms are added.
+  const GURL& url = rfh->GetLastCommittedURL();
+  base::Optional<AdsInterventionManager::LastAdsIntervention>
+      last_intervention =
+          profile_context_->ads_intervention_manager()->GetLastAdsIntervention(
+              url);
+  if (last_intervention &&
+      last_intervention->duration_since <
+          subresource_filter::kAdsInterventionDuration.Get())
+    return;
+
+  profile_context_->ads_intervention_manager()
+      ->TriggerAdsInterventionForUrlOnSubsequentLoads(url, triggered_violation);
+}
+
 void ChromeSubresourceFilterClient::AllowlistByContentSettings(
     const GURL& top_level_url) {
-  settings_manager_->AllowlistSite(top_level_url);
+  profile_context_->settings_manager()->AllowlistSite(top_level_url);
 }
 
 void ChromeSubresourceFilterClient::ToggleForceActivationInCurrentWebContents(
@@ -175,7 +219,7 @@
 
   LogAction(SubresourceFilterAction::kUIShown);
   did_show_ui_for_navigation_ = true;
-  settings_manager_->OnDidShowUI(url);
+  profile_context_->settings_manager()->OnDidShowUI(url);
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ChromeSubresourceFilterClient)
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
index 3c4336a..cc150ae4 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
@@ -15,7 +15,7 @@
 #include "content/public/browser/web_contents_user_data.h"
 
 class GURL;
-class SubresourceFilterContentSettingsManager;
+class SubresourceFilterProfileContext;
 
 namespace content {
 class NavigationHandle;
@@ -80,6 +80,9 @@
       content::NavigationHandle* navigation_handle,
       subresource_filter::mojom::ActivationLevel initial_activation_level,
       subresource_filter::ActivationDecision* decision) override;
+  void OnAdsViolationTriggered(
+      content::RenderFrameHost* rfh,
+      subresource_filter::mojom::AdsViolation triggered_violation) override;
 
   // Should be called by devtools in response to a protocol command to enable ad
   // blocking in this WebContents. Should only persist while devtools is
@@ -104,7 +107,7 @@
       throttle_manager_;
 
   // Owned by the profile.
-  SubresourceFilterContentSettingsManager* settings_manager_ = nullptr;
+  SubresourceFilterProfileContext* profile_context_ = nullptr;
 
   bool did_show_ui_for_navigation_ = false;
 
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
index ca8515c..7c1c2d7 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
@@ -72,9 +72,8 @@
 
   ASSERT_TRUE(embedded_test_server()->Start());
 
-  auto* factory = SubresourceFilterProfileContextFactory::GetForProfile(
+  profile_context_ = SubresourceFilterProfileContextFactory::GetForProfile(
       browser()->profile());
-  settings_manager_ = factory->settings_manager();
 }
 
 std::unique_ptr<TestSafeBrowsingDatabaseHelper>
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
index b923a0b..c24b8d1e 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/subresource_filter/subresource_filter_profile_context.h"
 #include "chrome/browser/subresource_filter/test_ruleset_publisher.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/safe_browsing/core/db/util.h"
@@ -32,6 +33,7 @@
 }  // namespace content
 
 class SubresourceFilterContentSettingsManager;
+class AdsInterventionManager;
 class TestSafeBrowsingDatabaseHelper;
 
 namespace subresource_filter {
@@ -64,7 +66,11 @@
   content::WebContents* web_contents() const;
 
   SubresourceFilterContentSettingsManager* settings_manager() const {
-    return settings_manager_;
+    return profile_context_->settings_manager();
+  }
+
+  AdsInterventionManager* ads_intervention_manager() {
+    return profile_context_->ads_intervention_manager();
   }
 
   content::RenderFrameHost* FindFrameByName(const std::string& name) const;
@@ -114,7 +120,7 @@
   std::unique_ptr<TestSafeBrowsingDatabaseHelper> database_helper_;
 
   // Owned by the profile.
-  SubresourceFilterContentSettingsManager* settings_manager_;
+  SubresourceFilterProfileContext* profile_context_;
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterBrowserTest);
 };
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index b198d154..4a1914eb 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -22,6 +22,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_profile_context.cc b/chrome/browser/subresource_filter/subresource_filter_profile_context.cc
index 756caf4..9d348ed 100644
--- a/chrome/browser/subresource_filter/subresource_filter_profile_context.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_profile_context.cc
@@ -4,15 +4,19 @@
 
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context.h"
 
+#include "chrome/browser/subresource_filter/ads_intervention_manager.h"
 #include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
 
 SubresourceFilterProfileContext::SubresourceFilterProfileContext(
     Profile* profile)
     : settings_manager_(
-          std::make_unique<SubresourceFilterContentSettingsManager>(profile)) {}
+          std::make_unique<SubresourceFilterContentSettingsManager>(profile)),
+      ads_intervention_manager_(
+          std::make_unique<AdsInterventionManager>(settings_manager_.get())) {}
 
 SubresourceFilterProfileContext::~SubresourceFilterProfileContext() {}
 
 void SubresourceFilterProfileContext::Shutdown() {
   settings_manager_.reset();
+  ads_intervention_manager_.reset();
 }
diff --git a/chrome/browser/subresource_filter/subresource_filter_profile_context.h b/chrome/browser/subresource_filter/subresource_filter_profile_context.h
index 572da530c..c8836d7 100644
--- a/chrome/browser/subresource_filter/subresource_filter_profile_context.h
+++ b/chrome/browser/subresource_filter/subresource_filter_profile_context.h
@@ -12,6 +12,7 @@
 
 class Profile;
 class SubresourceFilterContentSettingsManager;
+class AdsInterventionManager;
 
 // This class holds profile scoped context for subresource filtering.
 class SubresourceFilterProfileContext : public KeyedService {
@@ -23,12 +24,20 @@
     return settings_manager_.get();
   }
 
+  AdsInterventionManager* ads_intervention_manager() {
+    return ads_intervention_manager_.get();
+  }
+
  private:
   // KeyedService:
   void Shutdown() override;
 
   std::unique_ptr<SubresourceFilterContentSettingsManager> settings_manager_;
 
+  // Manages ads interventions that have been triggered on previous
+  // navigations.
+  std::unique_ptr<AdsInterventionManager> ads_intervention_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterProfileContext);
 };
 
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index b16aac6..2d9f9e1 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -39,7 +39,6 @@
 // Constants for the credit card.
 const int kDefaultCardExpMonth = 8;
 const int kDefaultCardExpYear = 2087;
-const int kDefaultInstrumentId = 123;
 const char kDefaultCardLastFour[] = "1234";
 const char kDefaultCardName[] = "Patrick Valenzuela";
 const sync_pb::WalletMaskedCreditCard_WalletCardType kDefaultCardType =
@@ -369,7 +368,6 @@
   credit_card->set_name_on_card(kDefaultCardName);
   credit_card->set_status(sync_pb::WalletMaskedCreditCard::VALID);
   credit_card->set_type(kDefaultCardType);
-  credit_card->set_instrument_id(kDefaultInstrumentId);
   if (!billing_address_id.empty()) {
     credit_card->set_billing_address_id(billing_address_id);
   }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5336e8b..1d57541b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1017,6 +1017,8 @@
       "hung_renderer/hung_renderer_core.h",
       "in_product_help/active_tab_tracker.cc",
       "in_product_help/active_tab_tracker.h",
+      "in_product_help/feature_promo_snooze_service.cc",
+      "in_product_help/feature_promo_snooze_service.h",
       "in_product_help/global_media_controls_in_product_help.cc",
       "in_product_help/global_media_controls_in_product_help.h",
       "in_product_help/global_media_controls_in_product_help_factory.cc",
@@ -2177,10 +2179,6 @@
       "webui/chromeos/drive_internals_ui.h",
       "webui/chromeos/edu_account_login_handler_chromeos.cc",
       "webui/chromeos/edu_account_login_handler_chromeos.h",
-      "webui/chromeos/file_manager/file_manager_page_handler.cc",
-      "webui/chromeos/file_manager/file_manager_page_handler.h",
-      "webui/chromeos/file_manager/file_manager_ui.cc",
-      "webui/chromeos/file_manager/file_manager_ui.h",
       "webui/chromeos/first_run/first_run_actor.cc",
       "webui/chromeos/first_run/first_run_actor.h",
       "webui/chromeos/first_run/first_run_handler.cc",
@@ -2549,7 +2547,6 @@
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/file_manager:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/machine_learning:mojo_bindings",
       "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
       "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings",
@@ -2655,6 +2652,7 @@
         ]
       }
       deps += [
+        "//chromeos/components/file_manager:file_manager_ui",
         "//chromeos/components/sample_system_web_app_ui",
         "//chromeos/components/telemetry_extension_ui",
       ]
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 5c2cb05b..717eac2 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3590,6 +3590,9 @@
       <message name="IDS_SHARING_HUB_OPEN_SETTINGS_LABEL" desc="Label for the open settings button.">
         Open Settings
       </message>
+      <message name="IDS_SHARING_HUB_IMAGE_PREVIEW_SUBTITLE" desc="Subtitle shown in the preview of Sharing Hub image shares.">
+        image
+      </message>
 
       <!-- ClickToCall -->
       <message name="IDS_CLICK_TO_CALL_NOTIFICATION_TEXT" desc="Text displayed in a click to call notification to call on a number.">
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_HUB_IMAGE_PREVIEW_SUBTITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_HUB_IMAGE_PREVIEW_SUBTITLE.png.sha1
new file mode 100644
index 0000000..4385b497
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_HUB_IMAGE_PREVIEW_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+18be3a695d8acb4116f4a896fe7b72f4f0d6b533
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
index ecf71ad..a166ca50 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -8,6 +8,7 @@
 #include <set>
 #include <string>
 
+#include "ash/public/cpp/app_list/app_list_config.h"
 #include "base/files/file_path.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_features.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h"
+#include "chrome/browser/extensions/chrome_app_icon.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/install_tracker.h"
@@ -31,10 +33,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
+#include "chrome/browser/ui/app_list/icon_standardizer.h"
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/components/app_icon_manager.h"
 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -45,9 +49,13 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/image_loader.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/image/image_unittest_util.h"
 
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -139,6 +147,31 @@
       });
 }
 
+void WaitForIconUpdates(ChromeAppListItem* item) {
+  ASSERT_TRUE(item);
+  do {
+    content::RunAllTasksUntilIdle();
+  } while (item->icon().isNull());
+}
+
+void VerifyIcon(const gfx::ImageSkia& src, const gfx::ImageSkia& dst) {
+  ASSERT_FALSE(src.isNull());
+  ASSERT_FALSE(dst.isNull());
+
+  const std::vector<ui::ScaleFactor>& scale_factors =
+      ui::GetSupportedScaleFactors();
+  ASSERT_EQ(2U, scale_factors.size());
+
+  for (auto& scale_factor : scale_factors) {
+    const float scale = ui::GetScaleForScaleFactor(scale_factor);
+    ASSERT_TRUE(src.HasRepresentation(scale));
+    ASSERT_TRUE(dst.HasRepresentation(scale));
+    ASSERT_TRUE(
+        gfx::test::AreBitmapsEqual(src.GetRepresentation(scale).GetBitmap(),
+                                   dst.GetRepresentation(scale).GetBitmap()));
+  }
+}
+
 }  // namespace
 
 class AppServiceAppModelBuilderTest
@@ -224,6 +257,33 @@
                model_updater_.get());
   }
 
+  void GenerateExtensionAppIcon(const std::string app_id,
+                                gfx::ImageSkia& output_image_skia) {
+    extensions::ExtensionRegistry* registry =
+        extensions::ExtensionRegistry::Get(profile());
+    ASSERT_TRUE(registry);
+    const extensions::Extension* extension =
+        registry->GetInstalledExtension(app_id);
+    ASSERT_TRUE(extension);
+
+    base::RunLoop run_loop;
+    int size_in_dip = ash::AppListConfig::instance().grid_icon_dimension();
+    extensions::ImageLoader::Get(profile())->LoadImageAtEveryScaleFactorAsync(
+        extension, gfx::Size(size_in_dip, size_in_dip),
+        base::BindOnce(
+            [](gfx::ImageSkia* image_skia,
+               base::OnceClosure load_app_icon_callback,
+               const gfx::Image& image) {
+              *image_skia = image.AsImageSkia();
+              std::move(load_app_icon_callback).Run();
+            },
+            &output_image_skia, run_loop.QuitClosure()));
+    run_loop.Run();
+
+    if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+      output_image_skia = app_list::CreateStandardIconImage(output_image_skia);
+  }
+
   std::vector<std::string> default_apps_;
 };
 
@@ -250,6 +310,71 @@
     RemoveApps(apps::mojom::AppType::kWeb, testing_profile(),
                model_updater_.get());
   }
+
+  std::string CreateWebApp(const std::string& app_name) {
+    const GURL kAppUrl("https://example.com/");
+
+    auto web_app_info = std::make_unique<WebApplicationInfo>();
+    web_app_info->title = base::UTF8ToUTF16(app_name);
+    web_app_info->app_url = kAppUrl;
+    web_app_info->scope = kAppUrl;
+    web_app_info->open_as_window = true;
+
+    return web_app::InstallWebApp(profile(), std::move(web_app_info));
+  }
+
+  void GenerateWebAppIcon(const std::string& app_id,
+                          gfx::ImageSkia& output_image_skia) {
+    std::vector<int> icon_sizes_in_px;
+    apps::ScaleToSize scale_to_size_in_px;
+    int size_in_dip = ash::AppListConfig::instance().grid_icon_dimension();
+    for (auto scale_factor : ui::GetSupportedScaleFactors()) {
+      int size_in_px =
+          gfx::ScaleToFlooredSize(gfx::Size(size_in_dip, size_in_dip),
+                                  ui::GetScaleForScaleFactor(scale_factor))
+              .width();
+      scale_to_size_in_px[ui::GetScaleForScaleFactor(scale_factor)] =
+          size_in_px;
+      icon_sizes_in_px.emplace_back(size_in_px);
+    }
+
+    web_app::WebAppProvider* web_app_provider =
+        web_app::WebAppProvider::Get(profile());
+    ASSERT_TRUE(web_app_provider);
+
+    base::RunLoop run_loop;
+    IconPurpose icon_purpose = IconPurpose::ANY;
+    web_app_provider->icon_manager().ReadIcons(
+        app_id, icon_purpose, icon_sizes_in_px,
+        base::BindOnce(
+            [](gfx::ImageSkia* image_skia,
+               std::map<float, int> scale_to_size_in_px,
+               base::OnceClosure load_app_icon_callback,
+               std::map<SquareSizePx, SkBitmap> icon_bitmaps) {
+              for (auto it : scale_to_size_in_px) {
+                image_skia->AddRepresentation(
+                    gfx::ImageSkiaRep(icon_bitmaps[it.second], it.first));
+              }
+              std::move(load_app_icon_callback).Run();
+            },
+            &output_image_skia, scale_to_size_in_px, run_loop.QuitClosure()));
+    run_loop.Run();
+
+    if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
+      output_image_skia = gfx::ImageSkiaOperations::CreateMaskedImage(
+          output_image_skia, apps::LoadMaskImage(scale_to_size_in_px));
+    }
+
+    extensions::ChromeAppIcon::ApplyEffects(
+        size_in_dip, extensions::ChromeAppIcon::ResizeFunction(),
+        true /* app_launchable */, true /* from_bookmark */,
+        extensions::ChromeAppIcon::Badge::kNone, &output_image_skia);
+    for (auto scale_factor : ui::GetSupportedScaleFactors()) {
+      // Force the icon to be loaded.
+      output_image_skia.GetRepresentation(
+          ui::GetScaleForScaleFactor(scale_factor));
+    }
+  }
 };
 
 TEST_P(BuiltInAppTest, Build) {
@@ -441,6 +566,17 @@
   CreateBuilder();
 }
 
+TEST_P(ExtensionAppTest, LoadIcon) {
+  // Generate the source icon for comparing.
+  gfx::ImageSkia src_image_skia;
+  GenerateExtensionAppIcon(kPackagedApp1Id, src_image_skia);
+
+  auto* item = model_updater_->FindItem(kPackagedApp1Id);
+  WaitForIconUpdates(item);
+
+  VerifyIcon(src_image_skia, item->icon());
+}
+
 // This test adds a bookmark app to the app list.
 TEST_P(BookmarkAppBuilderTest, BookmarkAppList) {
   const std::string kAppName = "Bookmark App";
@@ -470,27 +606,32 @@
 
 // This test adds a web app to the app list.
 TEST_P(WebAppBuilderTest, WebAppList) {
-  Profile* const profile = profile_.get();
-
   const std::string kAppName = "Web App";
-  const GURL kAppUrl("https://example.com/");
-
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->title = base::UTF8ToUTF16(kAppName);
-  web_app_info->app_url = kAppUrl;
-  web_app_info->scope = kAppUrl;
-  web_app_info->open_as_window = true;
-
-  const web_app::AppId app_id =
-      web_app::InstallWebApp(profile, std::move(web_app_info));
+  CreateWebApp(kAppName);
 
   app_service_test_.SetUp(profile_.get());
-  RemoveApps(apps::mojom::AppType::kWeb, profile, model_updater_.get());
+  RemoveApps(apps::mojom::AppType::kWeb, profile(), model_updater_.get());
   EXPECT_EQ(1u, model_updater_->ItemCount());
   EXPECT_EQ((std::vector<std::string>{kAppName}),
             GetModelContent(model_updater_.get()));
 }
 
+TEST_P(WebAppBuilderTest, LoadGeneratedIcon) {
+  const std::string kAppName = "Web App";
+  const std::string app_id = CreateWebApp(kAppName);
+
+  app_service_test_.FlushMojoCalls();
+
+  // Generate the source icon for comparing.
+  gfx::ImageSkia src_image_skia;
+  GenerateWebAppIcon(app_id, src_image_skia);
+
+  auto* item = model_updater_->FindItem(app_id);
+  WaitForIconUpdates(item);
+
+  VerifyIcon(src_image_skia, item->icon());
+}
+
 class CrostiniAppTest : public AppServiceAppModelBuilderTest {
  public:
   CrostiniAppTest() {
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index 78ce9f1..93393d0 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -18,9 +18,11 @@
 HoldingSpaceDownloadsDelegate::HoldingSpaceDownloadsDelegate(
     Profile* profile,
     HoldingSpaceModel* model,
-    ItemDownloadedCallback item_downloaded_callback)
+    ItemDownloadedCallback item_downloaded_callback,
+    DownloadsRestoredCallback downloads_restored_callback)
     : HoldingSpaceKeyedServiceDelegate(profile, model),
-      item_downloaded_callback_(item_downloaded_callback) {}
+      item_downloaded_callback_(item_downloaded_callback),
+      downloads_restored_callback_(std::move(downloads_restored_callback)) {}
 
 HoldingSpaceDownloadsDelegate::~HoldingSpaceDownloadsDelegate() = default;
 
@@ -41,19 +43,34 @@
   RemoveObservers();
 }
 
-void HoldingSpaceDownloadsDelegate::OnHoldingSpaceModelRestored() {
+void HoldingSpaceDownloadsDelegate::OnPersistenceRestored() {
   content::DownloadManager* download_manager =
       download_manager_for_testing
           ? download_manager_for_testing
           : content::BrowserContext::GetDownloadManager(profile());
 
+  if (download_manager->IsManagerInitialized())
+    OnManagerInitialized();
+}
+
+void HoldingSpaceDownloadsDelegate::OnManagerInitialized() {
+  if (is_restoring_persistence())
+    return;
+
+  content::DownloadManager* download_manager =
+      download_manager_for_testing
+          ? download_manager_for_testing
+          : content::BrowserContext::GetDownloadManager(profile());
+
+  DCHECK(download_manager->IsManagerInitialized());
+
   download::SimpleDownloadManager::DownloadVector downloads;
   download_manager->GetAllDownloads(&downloads);
 
   for (auto* download : downloads) {
     switch (download->GetState()) {
       case download::DownloadItem::COMPLETE:
-        item_downloaded_callback_.Run(download->GetFullPath());
+        OnDownloadCompleted(download);
         break;
       case download::DownloadItem::IN_PROGRESS:
         download_item_observer_.Add(download);
@@ -64,6 +81,9 @@
         break;
     }
   }
+
+  // Notify completion of downloads restoration.
+  std::move(downloads_restored_callback_).Run();
 }
 
 void HoldingSpaceDownloadsDelegate::ManagerGoingDown(
@@ -95,7 +115,7 @@
 
 void HoldingSpaceDownloadsDelegate::OnDownloadCompleted(
     download::DownloadItem* item) {
-  if (!is_restoring())
+  if (!is_restoring_persistence())
     item_downloaded_callback_.Run(item->GetFullPath());
 }
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
index ad6b071..268c050 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
@@ -26,14 +26,18 @@
                                       public download::DownloadItem::Observer {
  public:
   // Callback to be invoked when a download is completed. Note that this
-  // callback will only be invoked after the holding space model is restored.
+  // callback will only be invoked after holding space persistence is restored.
   using ItemDownloadedCallback =
       base::RepeatingCallback<void(const base::FilePath&)>;
 
+  // Callback to invoke when all downloads have been restored to holding space.
+  using DownloadsRestoredCallback = base::OnceClosure;
+
   HoldingSpaceDownloadsDelegate(
       Profile* profile,
       HoldingSpaceModel* model,
-      ItemDownloadedCallback item_downloaded_callback);
+      ItemDownloadedCallback item_downloaded_callback,
+      DownloadsRestoredCallback downloads_restored_callback);
   HoldingSpaceDownloadsDelegate(const HoldingSpaceDownloadsDelegate&) = delete;
   HoldingSpaceDownloadsDelegate& operator=(
       const HoldingSpaceDownloadsDelegate&) = delete;
@@ -48,9 +52,10 @@
   // HoldingSpaceKeyedServiceDelegate:
   void Init() override;
   void Shutdown() override;
-  void OnHoldingSpaceModelRestored() override;
+  void OnPersistenceRestored() override;
 
   // content::DownloadManager::Observer:
+  void OnManagerInitialized() override;
   void ManagerGoingDown(content::DownloadManager* manager) override;
   void OnDownloadCreated(content::DownloadManager* manager,
                          download::DownloadItem* item) override;
@@ -67,6 +72,9 @@
   // Callback to invoke when a download is completed.
   ItemDownloadedCallback item_downloaded_callback_;
 
+  // Callback to invoke when all downloads have been restored to holding space.
+  DownloadsRestoredCallback downloads_restored_callback_;
+
   ScopedObserver<content::DownloadManager, content::DownloadManager::Observer>
       download_manager_observer_{this};
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
index 0fb15a44..cb39ec8 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
 #include "ash/public/cpp/holding_space/holding_space_image.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "base/barrier_closure.h"
 #include "base/files/file_path.h"
 #include "base/guid.h"
 #include "chrome/browser/browser_process.h"
@@ -36,6 +37,14 @@
       account_id_(account_id),
       holding_space_client_(profile),
       thumbnail_loader_(profile) {
+  // Model restoration is a multi-step process, currently consisting of a
+  // restoration from persistence followed by a restoration of downloads. Once
+  // all steps have indicated completion, `OnModelFullyRestored()` is invoked.
+  on_model_partially_restored_callback_ = base::BarrierClosure(
+      /*number_of_steps_before_fully_restored=*/2,
+      base::BindOnce(&HoldingSpaceKeyedService::OnModelFullyRestored,
+                     base::Unretained(this)));
+
   // The associated profile may not be ready yet. If it is, we can immediately
   // proceed with profile dependent initialization.
   ProfileManager* const profile_manager = GetProfileManager();
@@ -43,6 +52,7 @@
     OnProfileReady();
     return;
   }
+
   // Otherwise we need to wait for the profile to be added.
   profile_manager_observer_.Add(profile_manager);
 }
@@ -102,6 +112,12 @@
 
 void HoldingSpaceKeyedService::AddDownload(
     const base::FilePath& download_file) {
+  const bool already_exists =
+      holding_space_model_.GetItem(HoldingSpaceItem::GetFileBackedItemId(
+          HoldingSpaceItem::Type::kDownload, download_file));
+  if (already_exists)
+    return;
+
   GURL file_system_url =
       holding_space_util::ResolveFileSystemUrl(profile_, download_file);
   if (file_system_url.is_empty())
@@ -133,9 +149,13 @@
 void HoldingSpaceKeyedService::OnProfileReady() {
   // The `HoldingSpaceDownloadsDelegate` monitors the status of downloads.
   delegates_.push_back(std::make_unique<HoldingSpaceDownloadsDelegate>(
-      profile_, &holding_space_model_, /*item_downloaded_callback=*/
+      profile_, &holding_space_model_,
+      /*item_downloaded_callback=*/
       base::BindRepeating(&HoldingSpaceKeyedService::AddDownload,
-                          weak_factory_.GetWeakPtr())));
+                          weak_factory_.GetWeakPtr()),
+      /*downloads_restored_callback=*/
+      base::BindOnce(&HoldingSpaceKeyedService::OnDownloadsRestored,
+                     weak_factory_.GetWeakPtr())));
 
   // The `HoldingSpaceFileSystemDelegate` monitors the file system for changes.
   delegates_.push_back(std::make_unique<HoldingSpaceFileSystemDelegate>(
@@ -150,8 +170,8 @@
       /*item_restored_callback=*/
       base::BindRepeating(&HoldingSpaceKeyedService::AddItem,
                           weak_factory_.GetWeakPtr()),
-      /*model_restored_callback=*/
-      base::BindOnce(&HoldingSpaceKeyedService::OnModelRestored,
+      /*persistence_restored_callback=*/
+      base::BindOnce(&HoldingSpaceKeyedService::OnPersistenceRestored,
                      weak_factory_.GetWeakPtr())));
 
   // Initialize all delegates only after they have been added to our collection.
@@ -170,10 +190,19 @@
       std::cref(file_path)));
 }
 
-void HoldingSpaceKeyedService::OnModelRestored() {
+void HoldingSpaceKeyedService::OnDownloadsRestored() {
   for (auto& delegate : delegates_)
-    delegate->NotifyHoldingSpaceModelRestored();
+    delegate->NotifyDownloadsRestored();
+  on_model_partially_restored_callback_.Run();
+}
 
+void HoldingSpaceKeyedService::OnPersistenceRestored() {
+  for (auto& delegate : delegates_)
+    delegate->NotifyPersistenceRestored();
+  on_model_partially_restored_callback_.Run();
+}
+
+void HoldingSpaceKeyedService::OnModelFullyRestored() {
   HoldingSpaceController::Get()->RegisterClientAndModelForUser(
       account_id_, &holding_space_client_, &holding_space_model_);
 }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
index 9390a4c..d13e4ff 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
@@ -102,8 +102,15 @@
   // Invoked when the specified `file_path` is removed.
   void OnFileRemoved(const base::FilePath& file_path);
 
-  // Invoked when the holding space model has been restored from persistence.
-  void OnModelRestored();
+  // Invoked when all downloads have been restored to holding space.
+  void OnDownloadsRestored();
+
+  // Invoked when holding space persistence has been restored.
+  void OnPersistenceRestored();
+
+  // Invoked when the holding space model has been fully restored. This includes
+  // both holding space items restored from persistence as well as downloads.
+  void OnModelFullyRestored();
 
   Profile* const profile_;
   const AccountId account_id_;
@@ -118,6 +125,12 @@
   // service. They operate autonomously of one another.
   std::vector<std::unique_ptr<HoldingSpaceKeyedServiceDelegate>> delegates_;
 
+  // A `base::BarrierClosure` that is run when the holding space model has been
+  // partially restored. This will occur multiple times, after restoration from
+  // persistence and after restoration of downloads. Once all restoration steps
+  // have indicated completion, `OnModelFullyRestored()` is invoked.
+  base::RepeatingClosure on_model_partially_restored_callback_;
+
   ScopedObserver<ProfileManager, ProfileManagerObserver>
       profile_manager_observer_{this};
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
index 02cf558..978e02c 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
@@ -21,10 +21,16 @@
 
 void HoldingSpaceKeyedServiceDelegate::Shutdown() {}
 
-void HoldingSpaceKeyedServiceDelegate::NotifyHoldingSpaceModelRestored() {
-  DCHECK(is_restoring_);
-  is_restoring_ = false;
-  OnHoldingSpaceModelRestored();
+void HoldingSpaceKeyedServiceDelegate::NotifyDownloadsRestored() {
+  DCHECK(is_restoring_downloads_);
+  is_restoring_downloads_ = false;
+  OnDownloadsRestored();
+}
+
+void HoldingSpaceKeyedServiceDelegate::NotifyPersistenceRestored() {
+  DCHECK(is_restoring_persistence_);
+  is_restoring_persistence_ = false;
+  OnPersistenceRestored();
 }
 
 HoldingSpaceKeyedServiceDelegate::HoldingSpaceKeyedServiceDelegate(
@@ -42,6 +48,8 @@
 void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceItemRemoved(
     const HoldingSpaceItem* item) {}
 
-void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceModelRestored() {}
+void HoldingSpaceKeyedServiceDelegate::OnDownloadsRestored() {}
+
+void HoldingSpaceKeyedServiceDelegate::OnPersistenceRestored() {}
 
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
index d846f7ec..64c9c96 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
@@ -29,9 +29,13 @@
   // Delegates should perform any necessary clean up.
   virtual void Shutdown();
 
-  // Invoked by `HoldingSpaceKeyedService` to notify delegates when the holding
-  // space model has been restored from persistence.
-  void NotifyHoldingSpaceModelRestored();
+  // Invoked by `HoldingSpaceKeyedService` to notify delegates when all
+  // downloads have been restored to holding space.
+  void NotifyDownloadsRestored();
+
+  // Invoked by `HoldingSpaceKeyedService` to notify delegates when holding
+  // space persistence has been restored.
+  void NotifyPersistenceRestored();
 
  protected:
   HoldingSpaceKeyedServiceDelegate(Profile* profile, HoldingSpaceModel* model);
@@ -42,22 +46,31 @@
   // Returns the holding space model owned by `HoldingSpaceKeyedService`.
   const HoldingSpaceModel* model() const { return model_; }
 
-  // Returns if the holding space model is being restored from persistence.
-  bool is_restoring() const { return is_restoring_; }
+  // Returns if downloads are being restored.
+  bool is_restoring_downloads() const { return is_restoring_downloads_; }
+
+  // Returns if persistence is being restored.
+  bool is_restoring_persistence() const { return is_restoring_persistence_; }
 
  private:
   // HoldingSpaceModelObserver:
   void OnHoldingSpaceItemAdded(const HoldingSpaceItem* item) override;
   void OnHoldingSpaceItemRemoved(const HoldingSpaceItem* item) override;
 
-  // Invoked when the holding space model has been restored from persistence.
-  virtual void OnHoldingSpaceModelRestored();
+  // Invoked when all downloads have been restored to holding space.
+  virtual void OnDownloadsRestored();
+
+  // Invoked when holding space persistence has been restored.
+  virtual void OnPersistenceRestored();
 
   Profile* const profile_;
   const HoldingSpaceModel* const model_;
 
-  // If the holding space model is being restored from persistence.
-  bool is_restoring_ = true;
+  // If downloads are being restored.
+  bool is_restoring_downloads_ = true;
+
+  // If persistence is being restored.
+  bool is_restoring_persistence_ = true;
 
   ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver>
       holding_space_model_observer_{this};
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index a92ee25e..2ebf97d 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -161,9 +161,12 @@
  public:
   HoldingSpaceKeyedServiceTest()
       : fake_user_manager_(new chromeos::FakeChromeUserManager),
-        user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {
+        user_manager_enabler_(base::WrapUnique(fake_user_manager_)),
+        download_manager_(
+            std::make_unique<testing::NiceMock<MockDownloadManager>>()) {
     scoped_feature_list_.InitAndEnableFeature(features::kTemporaryHoldingSpace);
   }
+
   HoldingSpaceKeyedServiceTest(const HoldingSpaceKeyedServiceTest& other) =
       delete;
   HoldingSpaceKeyedServiceTest& operator=(
@@ -295,9 +298,48 @@
     return result;
   }
 
+  std::unique_ptr<download::MockDownloadItem> CreateMockDownloadItem(
+      base::FilePath full_file_path) {
+    auto item =
+        std::make_unique<testing::NiceMock<download::MockDownloadItem>>();
+    ON_CALL(*item, GetId()).WillByDefault(testing::Return(1));
+    ON_CALL(*item, GetGuid())
+        .WillByDefault(testing::ReturnRefOfCopy(
+            std::string("14CA04AF-ECEC-4B13-8829-817477EFAB83")));
+    ON_CALL(*item, GetFullPath())
+        .WillByDefault(testing::ReturnRefOfCopy(full_file_path));
+    ON_CALL(*item, GetURL())
+        .WillByDefault(testing::ReturnRefOfCopy(GURL("foo/bar")));
+    ON_CALL(*item, GetMimeType()).WillByDefault(testing::Return(std::string()));
+    content::DownloadItemUtils::AttachInfo(item.get(), GetProfile(), nullptr);
+    return item;
+  }
+
+  MockDownloadManager* download_manager() { return download_manager_.get(); }
+
  private:
+  // BrowserWithTestWindowTest:
+  void SetUp() override {
+    SetUpDownloadManager();
+    BrowserWithTestWindowTest::SetUp();
+  }
+
+  void SetUpDownloadManager() {
+    // The `content::DownloadManager` needs to be set prior to initialization
+    // of the `HoldingSpaceDownloadsDelegate`. This must happen before the
+    // `HoldingSpaceKeyedService` is created for the profile under test.
+    MockDownloadManager* mock_download_manager = download_manager();
+    HoldingSpaceDownloadsDelegate::SetDownloadManagerForTesting(
+        mock_download_manager);
+
+    // Spoof initialization of the `mock_download_manager`.
+    ON_CALL(*mock_download_manager, IsManagerInitialized)
+        .WillByDefault(testing::Return(true));
+  }
+
   chromeos::FakeChromeUserManager* fake_user_manager_;
   user_manager::ScopedUserManager user_manager_enabler_;
+  std::unique_ptr<MockDownloadManager> download_manager_;
 
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -310,6 +352,9 @@
   ScopedDownloadsMountPoint downloads_mount(GetProfile());
   ASSERT_TRUE(downloads_mount.IsValid());
 
+  // Wait for the holding space model.
+  HoldingSpaceModelAttachedWaiter(GetProfile()).Wait();
+
   // Verify that the holding space model gets set even if the holding space
   // keyed service is not explicitly created.
   HoldingSpaceModel* const initial_model =
@@ -560,52 +605,7 @@
             persisted_holding_space_items_after_restoration);
 }
 
-class HoldingSpaceKeyedServiceDownloadsTest
-    : public HoldingSpaceKeyedServiceTest {
- public:
-  HoldingSpaceKeyedServiceDownloadsTest()
-      : download_manager_(
-            std::make_unique<testing::NiceMock<MockDownloadManager>>()) {}
-
-  std::unique_ptr<download::MockDownloadItem> CreateMockDownloadItem(
-      base::FilePath full_file_path) {
-    auto item =
-        std::make_unique<testing::NiceMock<download::MockDownloadItem>>();
-    ON_CALL(*item, GetId()).WillByDefault(testing::Return(1));
-    ON_CALL(*item, GetGuid())
-        .WillByDefault(testing::ReturnRefOfCopy(
-            std::string("14CA04AF-ECEC-4B13-8829-817477EFAB83")));
-    ON_CALL(*item, GetFullPath())
-        .WillByDefault(testing::ReturnRefOfCopy(full_file_path));
-    ON_CALL(*item, GetURL())
-        .WillByDefault(testing::ReturnRefOfCopy(GURL("foo/bar")));
-    ON_CALL(*item, GetMimeType()).WillByDefault(testing::Return(std::string()));
-    content::DownloadItemUtils::AttachInfo(item.get(), GetProfile(), nullptr);
-    return item;
-  }
-
-  MockDownloadManager* download_manager() { return download_manager_.get(); }
-
- private:
-  // HoldingSpaceKeyedServiceTest:
-  void SetUp() override {
-    SetUpDownloadManager();
-    HoldingSpaceKeyedServiceTest::SetUp();
-  }
-
-  void SetUpDownloadManager() {
-    // The `content::DownloadManager` needs to be set prior to initialization
-    // of the `HoldingSpaceDownloadsDelegate`. This must happen before the
-    // `HoldingSpaceKeyedService` is created for the profile under test.
-    MockDownloadManager* mock_download_manager = download_manager();
-    HoldingSpaceDownloadsDelegate::SetDownloadManagerForTesting(
-        mock_download_manager);
-  }
-
-  std::unique_ptr<MockDownloadManager> download_manager_;
-};
-
-TEST_F(HoldingSpaceKeyedServiceDownloadsTest, RetrieveHistory) {
+TEST_F(HoldingSpaceKeyedServiceTest, RetrieveHistory) {
   TestingProfile* profile = GetProfile();
   // Create a test downloads mount point.
   ScopedDownloadsMountPoint downloads_mount(profile);
@@ -674,7 +674,7 @@
     delete download_items_mock[i];
 }
 
-TEST_F(HoldingSpaceKeyedServiceDownloadsTest, AddDownloadItem) {
+TEST_F(HoldingSpaceKeyedServiceTest, AddDownloadItem) {
   TestingProfile* profile = GetProfile();
   // Create a test downloads mount point.
   ScopedDownloadsMountPoint downloads_mount(profile);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
index 9e416b9..332eaed 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
@@ -21,11 +21,12 @@
     HoldingSpaceModel* model,
     HoldingSpaceThumbnailLoader* thumbnail_loader,
     ItemRestoredCallback item_restored_callback,
-    ModelRestoredCallback model_restored_callback)
+    PersistenceRestoredCallback persistence_restored_callback)
     : HoldingSpaceKeyedServiceDelegate(profile, model),
       thumbnail_loader_(thumbnail_loader),
       item_restored_callback_(item_restored_callback),
-      model_restored_callback_(std::move(model_restored_callback)) {}
+      persistence_restored_callback_(std::move(persistence_restored_callback)) {
+}
 
 HoldingSpacePersistenceDelegate::~HoldingSpacePersistenceDelegate() = default;
 
@@ -44,7 +45,7 @@
 
 void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemAdded(
     const HoldingSpaceItem* item) {
-  if (is_restoring())
+  if (is_restoring_persistence())
     return;
 
   // `kDownload` type holding space items have their own persistence mechanism.
@@ -58,7 +59,7 @@
 
 void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemRemoved(
     const HoldingSpaceItem* item) {
-  if (is_restoring())
+  if (is_restoring_persistence())
     return;
 
   // `kDownload` type holding space items have their own persistence mechanism.
@@ -73,7 +74,6 @@
   });
 }
 
-// TODO(dmblack): Restore download holding space items.
 void HoldingSpacePersistenceDelegate::RestoreModelFromPersistence() {
   DCHECK(model()->items().empty());
 
@@ -81,9 +81,9 @@
       profile()->GetPrefs()->GetList(kPersistencePath);
 
   // If persistent storage is empty we can immediately notify the callback of
-  // model restoration completion and quit early.
+  // persistence restoration completion and quit early.
   if (persisted_holding_space_items->GetList().empty()) {
-    std::move(model_restored_callback_).Run();
+    std::move(persistence_restored_callback_).Run();
     return;
   }
 
@@ -128,8 +128,8 @@
     });
   }
 
-  // Notify the callback of model restoration completion.
-  std::move(model_restored_callback_).Run();
+  // Notify completion of persistence restoration.
+  std::move(persistence_restored_callback_).Run();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
index 8760054..b30bedf 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
@@ -37,15 +37,15 @@
   using ItemRestoredCallback =
       base::RepeatingCallback<void(HoldingSpaceItemPtr)>;
 
-  // Callback to invoke when the model has been restored from persistence.
-  using ModelRestoredCallback = base::OnceClosure;
+  // Callback to invoke when holding space persistence has been restored.
+  using PersistenceRestoredCallback = base::OnceClosure;
 
   HoldingSpacePersistenceDelegate(
       Profile* profile,
       HoldingSpaceModel* model,
       HoldingSpaceThumbnailLoader* thumbnail_loader,
       ItemRestoredCallback item_restored_callback,
-      ModelRestoredCallback model_restored_callback);
+      PersistenceRestoredCallback persistence_restored_callback);
   HoldingSpacePersistenceDelegate(const HoldingSpacePersistenceDelegate&) =
       delete;
   HoldingSpacePersistenceDelegate& operator=(
@@ -73,8 +73,8 @@
   // Callback to invoke when an item has been restored from persistence.
   ItemRestoredCallback item_restored_callback_;
 
-  // Callback to invoke when the model has been restored from persistence.
-  ModelRestoredCallback model_restored_callback_;
+  // Callback to invoke when holding space persistence has been restored.
+  PersistenceRestoredCallback persistence_restored_callback_;
 
   base::WeakPtrFactory<HoldingSpacePersistenceDelegate> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/ash/test_session_controller.cc b/chrome/browser/ui/ash/test_session_controller.cc
index f348313..c833212 100644
--- a/chrome/browser/ui/ash/test_session_controller.cc
+++ b/chrome/browser/ui/ash/test_session_controller.cc
@@ -6,9 +6,17 @@
 
 #include <utility>
 
+#include "ash/public/cpp/session/session_observer.h"
+
 TestSessionController::TestSessionController() = default;
 TestSessionController::~TestSessionController() = default;
 
+void TestSessionController::SetScreenLocked(bool locked) {
+  is_screen_locked_ = locked;
+  for (auto& observer : observers_)
+    observer.OnLockStateChanged(locked);
+}
+
 void TestSessionController::SetClient(ash::SessionControllerClient* client) {}
 
 void TestSessionController::SetSessionInfo(const ash::SessionInfo& info) {
@@ -77,11 +85,14 @@
     const AccountId& account_id,
     ash::SessionActivationObserver* observer) {}
 
-void TestSessionController::AddObserver(ash::SessionObserver* observer) {}
+void TestSessionController::AddObserver(ash::SessionObserver* observer) {
+  observers_.AddObserver(observer);
+}
 
-void TestSessionController::RemoveObserver(ash::SessionObserver* observer) {}
+void TestSessionController::RemoveObserver(ash::SessionObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
 
 bool TestSessionController::IsScreenLocked() const {
-  NOTIMPLEMENTED();
-  return false;
+  return is_screen_locked_;
 }
diff --git a/chrome/browser/ui/ash/test_session_controller.h b/chrome/browser/ui/ash/test_session_controller.h
index e872d84..e26e019 100644
--- a/chrome/browser/ui/ash/test_session_controller.h
+++ b/chrome/browser/ui/ash/test_session_controller.h
@@ -10,6 +10,7 @@
 
 #include "ash/public/cpp/session/session_controller.h"
 #include "base/macros.h"
+#include "base/observer_list.h"
 #include "base/optional.h"
 
 // Test implementation of ash's SessionController interface.
@@ -44,6 +45,8 @@
     return set_user_session_order_count_;
   }
 
+  void SetScreenLocked(bool locked);
+
   // ash::SessionController:
   void SetClient(ash::SessionControllerClient* client) override;
   void SetSessionInfo(const ash::SessionInfo& info) override;
@@ -82,6 +85,8 @@
   int update_user_session_count_ = 0;
   int lock_animation_complete_call_count_ = 0;
   int set_user_session_order_count_ = 0;
+  bool is_screen_locked_ = false;
+  base::ObserverList<ash::SessionObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSessionController);
 };
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 36e9248..8875011 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -77,6 +77,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/window_open_disposition.h"
+#include "ui/events/event.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/resources/grit/ui_resources.h"
 
@@ -793,7 +794,7 @@
   void BlockedUrlAdded(int32_t id, const GURL& url) override;
 
  private:
-  void OnListItemClicked(int index, int event_flags) override;
+  void OnListItemClicked(int index, const ui::Event& event) override;
 
   int32_t item_id_from_item_index(int index) const {
     return bubble_content().list_items[index].item_id;
@@ -833,11 +834,11 @@
 }
 
 void ContentSettingPopupBubbleModel::OnListItemClicked(int index,
-                                                       int event_flags) {
+                                                       const ui::Event& event) {
   auto* helper =
       blocked_content::PopupBlockerTabHelper::FromWebContents(web_contents());
   helper->ShowBlockedPopup(item_id_from_item_index(index),
-                           ui::DispositionFromEventFlags(event_flags));
+                           ui::DispositionFromEventFlags(event.flags()));
   RemoveListItem(index);
   content_settings::RecordPopupsAction(
       content_settings::POPUPS_ACTION_CLICKED_LIST_ITEM_CLICKED);
@@ -1550,7 +1551,7 @@
 
 void ContentSettingFramebustBlockBubbleModel::OnListItemClicked(
     int index,
-    int event_flags) {
+    const ui::Event& event) {
   FramebustBlockTabHelper::FromWebContents(web_contents())
       ->OnBlockedUrlClicked(index);
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index ae133ec..296192f 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -41,6 +41,10 @@
 class RapporServiceImpl;
 }
 
+namespace ui {
+class Event;
+}
+
 // The hierarchy of bubble models:
 //
 // ContentSettingBubbleModel                  - base class
@@ -182,7 +186,7 @@
 
   void set_owner(Owner* owner) { owner_ = owner; }
 
-  virtual void OnListItemClicked(int index, int event_flags) {}
+  virtual void OnListItemClicked(int index, const ui::Event& event) {}
   virtual void OnCustomLinkClicked() {}
   virtual void OnManageButtonClicked() {}
   virtual void OnManageCheckboxChecked(bool is_checked) {}
@@ -554,7 +558,7 @@
   ~ContentSettingFramebustBlockBubbleModel() override;
 
   // ContentSettingBubbleModel:
-  void OnListItemClicked(int index, int event_flags) override;
+  void OnListItemClicked(int index, const ui::Event& event) override;
   ContentSettingFramebustBlockBubbleModel* AsFramebustBlockBubbleModel()
       override;
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index d128e537..6c1f86d2 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -35,7 +35,8 @@
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
-#include "ui/events/event_constants.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
 
 using content_settings::PageSpecificContentSettings;
 
@@ -328,7 +329,11 @@
         "ContentSettings.Popups",
         content_settings::POPUPS_ACTION_DISPLAYED_BUBBLE, 1);
 
-  model->OnListItemClicked(0, ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EF_LEFT_MOUSE_BUTTON);
+
+  model->OnListItemClicked(0, click_event);
   histograms.ExpectBucketCount(
         "ContentSettings.Popups",
         content_settings::POPUPS_ACTION_CLICKED_LIST_ITEM_CLICKED, 1);
diff --git a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
index 3c6bee21..60a2c05 100644
--- a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
+++ b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
@@ -37,7 +37,8 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/event_constants.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
 #include "url/gurl.h"
 
 #if defined(OS_CHROMEOS)
@@ -141,8 +142,10 @@
   EXPECT_FALSE(clicked_url_.has_value());
 
   content::TestNavigationObserver observer(GetWebContents());
-  framebust_block_bubble_model.OnListItemClicked(/* index = */ 1,
-                                                 ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EF_LEFT_MOUSE_BUTTON);
+  framebust_block_bubble_model.OnListItemClicked(/* index = */ 1, click_event);
   observer.Wait();
 
   EXPECT_TRUE(clicked_index_.has_value());
diff --git a/chrome/browser/ui/in_product_help/feature_promo_snooze_service.cc b/chrome/browser/ui/in_product_help/feature_promo_snooze_service.cc
new file mode 100644
index 0000000..dfa5673
--- /dev/null
+++ b/chrome/browser/ui/in_product_help/feature_promo_snooze_service.cc
@@ -0,0 +1,154 @@
+// Copyright 2020 The Chromium Authors. 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/in_product_help/feature_promo_snooze_service.h"
+
+#include <ostream>
+
+#include "base/feature_list.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/util/values/values_util.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+namespace {
+// Snooze data will be saved as a dictionary in the PrefService of a profile.
+
+// PrefService path.
+const char kIPHSnoozeDataPath[] = "in_product_help.snoozed_feature";
+
+// Path to the boolean indicates if an IPH is dismissed.
+// in_product_help.snoozed_feature.[iph_name].is_dismissed
+constexpr char kIPHIsFeatureDismissed[] = "is_dismissed";
+// Path to the timestamp an IPH is snoozed.
+// in_product_help.snoozed_feature.[iph_name].last_snooze_time
+constexpr char kIPHSnoozedFeatureTime[] = "last_snooze_time";
+// Path to the duration of snooze.
+// in_product_help.snoozed_feature.[iph_name].last_snooze_duration
+constexpr char kIPHSnoozedFeatureDuration[] = "last_snooze_duration";
+// Path to the count of how many times this IPH has been snoozed.
+// in_product_help.snoozed_feature.[iph_name].snooze_count
+constexpr char kIPHSnoozedFeatureCount[] = "snooze_count";
+}  // namespace
+
+FeaturePromoSnoozeService::FeaturePromoSnoozeService(Profile* profile)
+    : profile_(profile) {}
+
+void FeaturePromoSnoozeService::OnUserSnooze(const base::Feature& iph_feature,
+                                             base::TimeDelta snooze_duration) {
+  DCHECK(snooze_duration > base::TimeDelta::FromSeconds(0));
+  auto snooze_data = ReadSnoozeData(iph_feature);
+
+  if (!snooze_data)
+    snooze_data = SnoozeData();
+
+  snooze_data->last_snooze_time = base::Time::Now();
+  snooze_data->last_snooze_duration = snooze_duration;
+  snooze_data->snooze_count++;
+
+  SaveSnoozeData(iph_feature, *snooze_data);
+}
+
+void FeaturePromoSnoozeService::OnUserDismiss(
+    const base::Feature& iph_feature) {
+  auto snooze_data = ReadSnoozeData(iph_feature);
+
+  if (!snooze_data)
+    snooze_data = SnoozeData();
+
+  snooze_data->is_dismissed = true;
+
+  SaveSnoozeData(iph_feature, *snooze_data);
+}
+
+bool FeaturePromoSnoozeService::IsBlocked(const base::Feature& iph_feature) {
+  auto snooze_data = ReadSnoozeData(iph_feature);
+
+  if (!snooze_data)
+    return false;
+
+  // This IPH has been dismissed by user permanently.
+  if (snooze_data->is_dismissed)
+    return true;
+
+  // This IPH has neither been dismissed nor snoozed.
+  if (snooze_data->snooze_count == 0)
+    return false;
+
+  // Corruption: Snooze time is in the future.
+  if (snooze_data->last_snooze_time > base::Time::Now())
+    return true;
+
+  // This IPH is snoozed. Test if snooze period has expired.
+  return base::Time::Now() <
+         snooze_data->last_snooze_time + snooze_data->last_snooze_duration;
+}
+
+// static
+void FeaturePromoSnoozeService::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(kIPHSnoozeDataPath);
+}
+
+void FeaturePromoSnoozeService::Reset(const base::Feature& iph_feature) {
+  DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath);
+  base::DictionaryValue* pref_data = update.Get();
+  pref_data->RemovePath(iph_feature.name);
+}
+
+base::Optional<FeaturePromoSnoozeService::SnoozeData>
+FeaturePromoSnoozeService::ReadSnoozeData(const base::Feature& iph_feature) {
+  std::string path_prefix = std::string(iph_feature.name) + ".";
+
+  const base::DictionaryValue* pref_data =
+      profile_->GetPrefs()->GetDictionary(kIPHSnoozeDataPath);
+  base::Optional<bool> is_dismissed =
+      pref_data->FindBoolPath(path_prefix + kIPHIsFeatureDismissed);
+  base::Optional<base::Time> snooze_time = util::ValueToTime(
+      pref_data->FindPath(path_prefix + kIPHSnoozedFeatureTime));
+  base::Optional<int> snooze_count =
+      pref_data->FindIntPath(path_prefix + kIPHSnoozedFeatureCount);
+  base::Optional<base::TimeDelta> snooze_duration = util::ValueToTimeDelta(
+      pref_data->FindPath(path_prefix + kIPHSnoozedFeatureDuration));
+
+  base::Optional<SnoozeData> snooze_data;
+
+  if (!is_dismissed)
+    return snooze_data;
+
+  if (!snooze_time || !snooze_count || !snooze_duration) {
+    // IPH snooze data is corrupt. Clear data of this feature.
+    Reset(iph_feature);
+    return snooze_data;
+  }
+
+  snooze_data = SnoozeData();
+  snooze_data->is_dismissed = *is_dismissed;
+  snooze_data->last_snooze_time = *snooze_time;
+  snooze_data->last_snooze_duration = *snooze_duration;
+  snooze_data->snooze_count = *snooze_count;
+
+  return snooze_data;
+}
+
+void FeaturePromoSnoozeService::SaveSnoozeData(
+    const base::Feature& iph_feature,
+    const FeaturePromoSnoozeService::SnoozeData& snooze_data) {
+  std::string path_prefix = std::string(iph_feature.name) + ".";
+
+  DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath);
+  base::DictionaryValue* pref_data = update.Get();
+
+  pref_data->SetBoolPath(path_prefix + kIPHIsFeatureDismissed,
+                         snooze_data.is_dismissed);
+  pref_data->SetPath(path_prefix + kIPHSnoozedFeatureTime,
+                     util::TimeToValue(snooze_data.last_snooze_time));
+  pref_data->SetPath(path_prefix + kIPHSnoozedFeatureDuration,
+                     util::TimeDeltaToValue(snooze_data.last_snooze_duration));
+  pref_data->SetIntPath(path_prefix + kIPHSnoozedFeatureCount,
+                        snooze_data.snooze_count);
+}
diff --git a/chrome/browser/ui/in_product_help/feature_promo_snooze_service.h b/chrome/browser/ui/in_product_help/feature_promo_snooze_service.h
new file mode 100644
index 0000000..3403cc8c
--- /dev/null
+++ b/chrome/browser/ui/in_product_help/feature_promo_snooze_service.h
@@ -0,0 +1,73 @@
+// Copyright 2020 The Chromium 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_IN_PRODUCT_HELP_FEATURE_PROMO_SNOOZE_SERVICE_H_
+#define CHROME_BROWSER_UI_IN_PRODUCT_HELP_FEATURE_PROMO_SNOOZE_SERVICE_H_
+
+#include <string>
+#include "base/optional.h"
+#include "base/time/time.h"
+
+namespace base {
+struct Feature;
+}  // namespace base
+
+class Profile;
+class PrefRegistrySimple;
+
+// This service manages snooze and dismiss of in-product help promo.
+//
+// An IPH controller notifies this service when an IPH is snoozed or dismissed.
+// When the snooze period expires, this service will approve controller's
+// request to retrigger the IPH.
+// If an IPH is dismissed, this service will reject controller's request.
+class FeaturePromoSnoozeService {
+ public:
+  explicit FeaturePromoSnoozeService(Profile* profile);
+
+  // Disallow copy and assign.
+  FeaturePromoSnoozeService(const FeaturePromoSnoozeService&) = delete;
+  FeaturePromoSnoozeService& operator=(const FeaturePromoSnoozeService&) =
+      delete;
+
+  // The IPH controller must call this method when the user snoozes an IPH.
+  // The snooze duration defaults to 1 day plus 2 additional hours in hope to
+  // stagger busy hours in the days.
+  void OnUserSnooze(
+      const base::Feature& iph_feature,
+      base::TimeDelta snooze_duration = base::TimeDelta::FromHours(26));
+
+  // The IPH controller must call this method when the user actively dismiss an
+  // IPH. Don't call this method in case of a passive dismiss, i.e. auto dismiss
+  // after a fixed amount of time.
+  void OnUserDismiss(const base::Feature& iph_feature);
+
+  // The IPH controller must call this method to check if an IPH is blocked by
+  // dismiss or snooze. An IPH will be approved if it is not snoozed or the
+  // snoozing period has timed out.
+  bool IsBlocked(const base::Feature& iph_feature);
+
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  // Reset the state of |iph_feature|.
+  void Reset(const base::Feature& iph_feature);
+
+ private:
+  // Snooze information dictionary saved under path
+  // in_product_help.snoozed_feature.[iph_name] in PerfService.
+  struct SnoozeData {
+    bool is_dismissed = false;
+    base::Time last_snooze_time = base::Time();
+    base::TimeDelta last_snooze_duration = base::TimeDelta();
+    int snooze_count = 0;
+  };
+
+  base::Optional<SnoozeData> ReadSnoozeData(const base::Feature& iph_feature);
+  void SaveSnoozeData(const base::Feature& iph_feature,
+                      const SnoozeData& snooze_data);
+
+  Profile* const profile_;
+};
+
+#endif  // CHROME_BROWSER_UI_IN_PRODUCT_HELP_FEATURE_PROMO_SNOOZE_SERVICE_H_
diff --git a/chrome/browser/ui/in_product_help/feature_promo_snooze_service_unittest.cc b/chrome/browser/ui/in_product_help/feature_promo_snooze_service_unittest.cc
new file mode 100644
index 0000000..d7cb8c8
--- /dev/null
+++ b/chrome/browser/ui/in_product_help/feature_promo_snooze_service_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2020 The Chromium Authors. 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/in_product_help/feature_promo_snooze_service.h"
+
+#include <memory>
+
+#include "base/feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+base::Feature kTestIPHFeature{"TestIPHFeature",
+                              base::FEATURE_ENABLED_BY_DEFAULT};
+base::Feature kTestIPHFeature2{"TestIPHFeature2",
+                               base::FEATURE_ENABLED_BY_DEFAULT};
+}  // namespace
+
+class FeaturePromoSnoozeServiceTest : public testing::Test {
+ public:
+  FeaturePromoSnoozeServiceTest()
+      : task_environment_{base::test::SingleThreadTaskEnvironment::TimeSource::
+                              MOCK_TIME},
+        service_{&profile_} {}
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+  TestingProfile profile_;
+  FeaturePromoSnoozeService service_;
+};
+
+TEST_F(FeaturePromoSnoozeServiceTest, AllowNonSnoozedIPH) {
+  service_.Reset(kTestIPHFeature);
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, BlockDismissedIPH) {
+  service_.Reset(kTestIPHFeature);
+  service_.OnUserDismiss(kTestIPHFeature);
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+  service_.Reset(kTestIPHFeature);
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, BlockSnoozedIPH) {
+  service_.Reset(kTestIPHFeature);
+  service_.OnUserSnooze(kTestIPHFeature);
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, ReleaseSnoozedIPH) {
+  service_.Reset(kTestIPHFeature);
+  service_.OnUserSnooze(kTestIPHFeature, base::TimeDelta::FromHours(1));
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+  task_environment_.FastForwardBy(base::TimeDelta::FromHours(2));
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, MultipleIPH) {
+  service_.Reset(kTestIPHFeature);
+  service_.Reset(kTestIPHFeature2);
+  service_.OnUserSnooze(kTestIPHFeature, base::TimeDelta::FromHours(1));
+  service_.OnUserSnooze(kTestIPHFeature2, base::TimeDelta::FromHours(3));
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature2));
+  task_environment_.FastForwardBy(base::TimeDelta::FromHours(2));
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+  EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature2));
+  task_environment_.FastForwardBy(base::TimeDelta::FromHours(2));
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+  EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature2));
+}
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.cc b/chrome/browser/ui/search/ntp_user_data_logger.cc
index d6289df2..ad8b732 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger.cc
@@ -548,6 +548,9 @@
     case NTP_CUSTOMIZE_SHORTCUT_VISIBILITY_TOGGLE_CLICKED:
       RecordAction(LoggingEventToShortcutUserActionName(event));
       break;
+    case NTP_MODULES_SHOWN:
+      UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.ShownTime", time);
+      break;
   }
 }
 
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
index 2193c86d..9587148 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -72,7 +72,7 @@
   // Overridden from views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
-  void OpenLink(const std::string& url, int event_flags);
+  void OpenLink(const std::string& url, const ui::Event& event);
 
   Browser* browser_;
 
@@ -141,12 +141,13 @@
 void InvertBubbleView::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
   if (sender->tag() == kLearnMoreButton)
-    OpenLink(kLearnMoreUrl, event.flags());
+    OpenLink(kLearnMoreUrl, event);
 }
 
-void InvertBubbleView::OpenLink(const std::string& url, int event_flags) {
+void InvertBubbleView::OpenLink(const std::string& url,
+                                const ui::Event& event) {
   WindowOpenDisposition disposition = ui::DispositionFromEventFlags(
-      event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB);
+      event.flags(), WindowOpenDisposition::NEW_FOREGROUND_TAB);
   content::OpenURLParams params(GURL(url), content::Referrer(), disposition,
                                 ui::PAGE_TRANSITION_LINK, false);
   browser_->OpenURL(params);
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index a2a4ea02..abeea0e 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -251,10 +251,10 @@
     link->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
     link->set_callback(base::BindRepeating(
         [](const std::vector<Row>* items, const views::Link* link,
-           ContentSettingBubbleContents* parent, int event_flags) {
+           ContentSettingBubbleContents* parent, const ui::Event& event) {
           const auto it = base::ranges::find(*items, link, &Row::second);
           DCHECK(it != items->cend());
-          parent->LinkClicked(std::distance(items->cbegin(), it), event_flags);
+          parent->LinkClicked(std::distance(items->cbegin(), it), event);
         },
         base::Unretained(&list_item_views_), base::Unretained(link.get()),
         base::Unretained(parent_)));
@@ -619,10 +619,11 @@
   return container;
 }
 
-void ContentSettingBubbleContents::LinkClicked(int row, int event_flags) {
+void ContentSettingBubbleContents::LinkClicked(int row,
+                                               const ui::Event& event) {
   DCHECK(content_setting_bubble_model_);
   DCHECK_NE(row, -1);
-  content_setting_bubble_model_->OnListItemClicked(row, event_flags);
+  content_setting_bubble_model_->OnListItemClicked(row, event);
 }
 
 void ContentSettingBubbleContents::CustomLinkClicked() {
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index efbf8d18..36fd469 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -76,7 +76,7 @@
   // "learn more" button and a "manage" button.
   std::unique_ptr<View> CreateHelpAndManageView();
 
-  void LinkClicked(int row, int event_flags);
+  void LinkClicked(int row, const ui::Event& event);
   void CustomLinkClicked();
 
   void OnPerformAction(views::Combobox* combobox);
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index f604cd99..7c2006a 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -268,11 +268,8 @@
   if (audio_share_checkbox_) {
     switch (source_types_[index]) {
       case DesktopMediaID::TYPE_SCREEN:
-#if defined(USE_CRAS) || defined(OS_WIN)
-        audio_share_checkbox_->SetVisible(true);
-#else
-        audio_share_checkbox_->SetVisible(false);
-#endif
+        audio_share_checkbox_->SetVisible(
+            DesktopMediaPickerViews::kScreenAudioShareSupportedOnPlatform);
         break;
       case DesktopMediaID::TYPE_WINDOW:
         audio_share_checkbox_->SetVisible(false);
@@ -341,7 +338,8 @@
 bool DesktopMediaPickerDialogView::IsDialogButtonEnabled(
     ui::DialogButton button) const {
   return button != ui::DIALOG_BUTTON_OK ||
-         GetSelectedController()->GetSelection().has_value();
+         GetSelectedController()->GetSelection().has_value() ||
+         accepted_source_.has_value();
 }
 
 views::View* DesktopMediaPickerDialogView::GetInitiallyFocusedView() {
@@ -349,6 +347,7 @@
 }
 
 bool DesktopMediaPickerDialogView::Accept() {
+  DCHECK(IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
   // Ok button should only be enabled when a source is selected.
   base::Optional<DesktopMediaID> source_optional =
       accepted_source_.has_value() ? accepted_source_
@@ -445,6 +444,8 @@
   GetWidget()->CenterWindow(new_size);
 }
 
+constexpr bool DesktopMediaPickerViews::kScreenAudioShareSupportedOnPlatform;
+
 DesktopMediaPickerViews::DesktopMediaPickerViews() : dialog_(nullptr) {}
 
 DesktopMediaPickerViews::~DesktopMediaPickerViews() {
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
index 96dc67ab..da08c138 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_DESKTOP_CAPTURE_DESKTOP_MEDIA_PICKER_VIEWS_H_
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker.h"
 #include "chrome/browser/ui/views/desktop_capture/desktop_media_list_controller.h"
 #include "ui/views/controls/label.h"
@@ -86,6 +87,12 @@
 // DesktopMediaPicker.
 class DesktopMediaPickerViews : public DesktopMediaPicker {
  public:
+#if defined(OS_WIN) || defined(USE_CRAS)
+  static constexpr bool kScreenAudioShareSupportedOnPlatform = true;
+#else
+  static constexpr bool kScreenAudioShareSupportedOnPlatform = false;
+#endif
+
   DesktopMediaPickerViews();
   ~DesktopMediaPickerViews() override;
 
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
index 227772d..4e4017d 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
@@ -22,7 +22,6 @@
 #include "chrome/test/views/chrome_test_views_delegate.h"
 #include "components/web_modal/test_web_contents_modal_dialog_host.h"
 #include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/events/event_utils.h"
@@ -30,6 +29,8 @@
 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
 #include "ui/views/test/scoped_views_test_helper.h"
 #include "ui/views/test/test_views_delegate.h"
+#include "ui/views/test/widget_test.h"
+#include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_delegate.h"
 
@@ -37,11 +38,19 @@
 
 namespace views {
 
-class MockDesktopMediaPickerDialogObserver
-    : public DesktopMediaPickerManager::DialogObserver {
+class TestDialogObserver : public DesktopMediaPickerManager::DialogObserver {
  public:
-  MOCK_METHOD0(OnDialogOpened, void());
-  MOCK_METHOD0(OnDialogClosed, void());
+  ~TestDialogObserver() override {
+    EXPECT_TRUE(opened_);
+    EXPECT_TRUE(closed_);
+  }
+
+ private:
+  void OnDialogOpened() override { opened_ = true; }
+  void OnDialogClosed() override { closed_ = true; }
+
+  bool opened_ = false;
+  bool closed_ = false;
 };
 
 const std::vector<DesktopMediaID::Type> kSourceTypes = {
@@ -63,37 +72,41 @@
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kDisableModalAnimations);
 #endif
-
-    std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
-    for (auto type : source_types_) {
-      media_lists_[type] = new FakeDesktopMediaList(type);
-      source_lists.push_back(
-          std::unique_ptr<FakeDesktopMediaList>(media_lists_[type]));
-    }
-
-    base::string16 app_name = base::ASCIIToUTF16("foo");
+    DesktopMediaPickerManager::Get()->AddObserver(&observer_);
 
     picker_views_ = std::make_unique<DesktopMediaPickerViews>();
     test_api_.set_picker(picker_views_.get());
+
+    views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                         "DesktopMediaPickerDialogView");
+
+    const base::string16 kAppName = base::ASCIIToUTF16("foo");
     DesktopMediaPicker::Params picker_params;
     picker_params.context = test_helper_.GetContext();
-    picker_params.app_name = app_name;
-    picker_params.target_name = app_name;
+    picker_params.app_name = kAppName;
+    picker_params.target_name = kAppName;
     picker_params.request_audio = true;
-    DesktopMediaPickerManager::Get()->AddObserver(&observer_);
-    EXPECT_CALL(observer_, OnDialogOpened());
-    EXPECT_CALL(observer_, OnDialogClosed());
+
+    std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
+    for (auto type : source_types_) {
+      source_lists.push_back(std::make_unique<FakeDesktopMediaList>(type));
+      media_lists_[type] =
+          static_cast<FakeDesktopMediaList*>(source_lists.back().get());
+    }
+
     picker_views_->Show(
         picker_params, std::move(source_lists),
         base::BindOnce(&DesktopMediaPickerViewsTest::OnPickerDone,
                        base::Unretained(this)));
+    widget_destroyed_waiter_ =
+        std::make_unique<views::test::WidgetDestroyedWaiter>(
+            waiter.WaitIfNeededAndGet());
   }
 
   void TearDown() override {
-    if (GetPickerDialogView()) {
-      EXPECT_CALL(*this, OnPickerDone(content::DesktopMediaID()));
+    if (GetPickerDialogView())
       GetPickerDialogView()->GetWidget()->CloseNow();
-    }
+    widget_destroyed_waiter_->Wait();
     DesktopMediaPickerManager::Get()->RemoveObserver(&observer_);
   }
 
@@ -101,7 +114,19 @@
     return picker_views_->GetDialogViewForTesting();
   }
 
-  MOCK_METHOD1(OnPickerDone, void(content::DesktopMediaID));
+  void OnPickerDone(content::DesktopMediaID picked_id) {
+    picked_id_ = picked_id;
+    run_loop_.Quit();
+  }
+
+  base::Optional<content::DesktopMediaID> WaitForPickerDone() {
+    run_loop_.Run();
+    return picked_id_;
+  }
+
+  base::Optional<content::DesktopMediaID> picked_id() const {
+    return picked_id_;
+  }
 
  protected:
   content::BrowserTaskEnvironment task_environment_;
@@ -110,20 +135,21 @@
   std::map<DesktopMediaID::Type, FakeDesktopMediaList*> media_lists_;
   std::unique_ptr<DesktopMediaPickerViews> picker_views_;
   DesktopMediaPickerViewsTestApi test_api_;
-  MockDesktopMediaPickerDialogObserver observer_;
+  TestDialogObserver observer_;
   const std::vector<DesktopMediaID::Type> source_types_;
+
+  base::RunLoop run_loop_;
+  base::Optional<content::DesktopMediaID> picked_id_;
+  std::unique_ptr<views::test::WidgetDestroyedWaiter> widget_destroyed_waiter_;
 };
 
 TEST_F(DesktopMediaPickerViewsTest, DoneCallbackCalledWhenWindowClosed) {
-  EXPECT_CALL(*this, OnPickerDone(content::DesktopMediaID()));
-
   GetPickerDialogView()->GetWidget()->Close();
-  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(content::DesktopMediaID(), WaitForPickerDone());
 }
 
 TEST_F(DesktopMediaPickerViewsTest, DoneCallbackCalledOnOkButtonPressed) {
   const DesktopMediaID kFakeId(DesktopMediaID::TYPE_WINDOW, 222);
-  EXPECT_CALL(*this, OnPickerDone(kFakeId));
 
   media_lists_[DesktopMediaID::TYPE_WINDOW]->AddSourceByFullMediaID(kFakeId);
   test_api_.GetAudioShareCheckbox()->SetChecked(true);
@@ -138,7 +164,7 @@
       GetPickerDialogView()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
 
   GetPickerDialogView()->AcceptDialog();
-  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(kFakeId, WaitForPickerDone());
 }
 
 // Verifies that a MediaSourceView is selected with mouse left click and
@@ -166,8 +192,7 @@
 
 // Regression test for https://crbug.com/1102153
 TEST_F(DesktopMediaPickerViewsTest, DoneCallbackNotCalledOnDoubleClick) {
-  const DesktopMediaID kFakeId(DesktopMediaID::TYPE_WEB_CONTENTS, 222);
-  EXPECT_CALL(*this, OnPickerDone(kFakeId)).Times(0);
+  constexpr DesktopMediaID kFakeId(DesktopMediaID::TYPE_WEB_CONTENTS, 222);
 
   media_lists_[DesktopMediaID::TYPE_WEB_CONTENTS]->AddSourceByFullMediaID(
       kFakeId);
@@ -175,20 +200,19 @@
   test_api_.GetAudioShareCheckbox()->SetChecked(false);
 
   test_api_.PressMouseOnSourceAtIndex(0, true);
-  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(picked_id().has_value());
 }
 
 // Regression test for https://crbug.com/1102153
 TEST_F(DesktopMediaPickerViewsTest, DoneCallbackNotCalledOnDoubleTap) {
   const DesktopMediaID kFakeId(DesktopMediaID::TYPE_SCREEN, 222);
-  EXPECT_CALL(*this, OnPickerDone(kFakeId)).Times(0);
 
   test_api_.SelectTabForSourceType(DesktopMediaID::TYPE_SCREEN);
   test_api_.GetAudioShareCheckbox()->SetChecked(false);
 
   media_lists_[DesktopMediaID::TYPE_SCREEN]->AddSourceByFullMediaID(kFakeId);
   test_api_.DoubleTapSourceAtIndex(0);
-  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(picked_id().has_value());
 }
 
 TEST_F(DesktopMediaPickerViewsTest, CancelButtonAlwaysEnabled) {
@@ -261,12 +285,9 @@
 
 // Verifies the visible status of audio checkbox.
 TEST_F(DesktopMediaPickerViewsTest, AudioCheckboxState) {
-  bool expect_value = false;
   test_api_.SelectTabForSourceType(DesktopMediaID::TYPE_SCREEN);
-#if defined(OS_WIN) || defined(USE_CRAS)
-  expect_value = true;
-#endif
-  EXPECT_EQ(expect_value, test_api_.GetAudioShareCheckbox()->GetVisible());
+  EXPECT_EQ(DesktopMediaPickerViews::kScreenAudioShareSupportedOnPlatform,
+            test_api_.GetAudioShareCheckbox()->GetVisible());
 
   test_api_.SelectTabForSourceType(DesktopMediaID::TYPE_WINDOW);
   EXPECT_FALSE(test_api_.GetAudioShareCheckbox()->GetVisible());
@@ -278,23 +299,36 @@
 // Verifies that audio share information is recorded in the ID if the checkbox
 // is checked.
 TEST_F(DesktopMediaPickerViewsTest, DoneWithAudioShare) {
-  DesktopMediaID originId(DesktopMediaID::TYPE_WEB_CONTENTS, 222);
-  DesktopMediaID returnId = originId;
-  returnId.audio_share = true;
+  constexpr DesktopMediaID kOriginId(DesktopMediaID::TYPE_WEB_CONTENTS, 222);
+  constexpr DesktopMediaID kResultId(DesktopMediaID::TYPE_WEB_CONTENTS, 222,
+                                     true);
 
   // This matches the real workflow that when a source is generated in
   // media_list, its |audio_share| bit is not set. The bit is set by the picker
   // UI if the audio checkbox is checked.
-  EXPECT_CALL(*this, OnPickerDone(returnId));
   media_lists_[DesktopMediaID::TYPE_WEB_CONTENTS]->AddSourceByFullMediaID(
-      originId);
+      kOriginId);
 
   test_api_.SelectTabForSourceType(DesktopMediaID::TYPE_WEB_CONTENTS);
   test_api_.GetAudioShareCheckbox()->SetChecked(true);
   test_api_.FocusSourceAtIndex(0);
 
   GetPickerDialogView()->AcceptDialog();
-  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(kResultId, WaitForPickerDone());
+}
+
+TEST_F(DesktopMediaPickerViewsTest, OkButtonEnabledDuringAcceptSpecific) {
+  constexpr DesktopMediaID kFakeId(
+      DesktopMediaID::TYPE_SCREEN, 222,
+      DesktopMediaPickerViews::kScreenAudioShareSupportedOnPlatform);
+
+  media_lists_[DesktopMediaID::TYPE_WINDOW]->AddSourceByFullMediaID(kFakeId);
+
+  EXPECT_FALSE(
+      GetPickerDialogView()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+
+  GetPickerDialogView()->AcceptSpecificSource(kFakeId);
+  EXPECT_EQ(kFakeId, WaitForPickerDone());
 }
 
 // Creates a single pane DesktopMediaPickerViews that only has a tab list.
diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc
index bd1e906..547aba1 100644
--- a/chrome/browser/ui/views/infobars/infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_view.cc
@@ -402,9 +402,9 @@
                                  0));
 }
 
-void InfoBarView::LinkClicked(int event_flags) {
+void InfoBarView::LinkClicked(const ui::Event& event) {
   if (!owner())
     return;  // We're closing; don't call anything, it might access the owner.
-  if (delegate()->LinkClicked(ui::DispositionFromEventFlags(event_flags)))
+  if (delegate()->LinkClicked(ui::DispositionFromEventFlags(event.flags())))
     RemoveSelf();
 }
diff --git a/chrome/browser/ui/views/infobars/infobar_view.h b/chrome/browser/ui/views/infobars/infobar_view.h
index 48b819a0..530999a 100644
--- a/chrome/browser/ui/views/infobars/infobar_view.h
+++ b/chrome/browser/ui/views/infobars/infobar_view.h
@@ -109,7 +109,7 @@
   void SetLabelDetails(views::Label* label) const;
 
   // Callback used by the link created by CreateLink().
-  void LinkClicked(int event_flags);
+  void LinkClicked(const ui::Event& event);
 
   // The optional icon at the left edge of the InfoBar.
   views::ImageView* icon_ = nullptr;
diff --git a/chrome/browser/ui/views/network_profile_bubble_view.cc b/chrome/browser/ui/views/network_profile_bubble_view.cc
index d1e7f318..d644a73 100644
--- a/chrome/browser/ui/views/network_profile_bubble_view.cc
+++ b/chrome/browser/ui/views/network_profile_bubble_view.cc
@@ -35,7 +35,7 @@
   void Init() override;
   bool Accept() override;
 
-  void LinkClicked(int event_flags);
+  void LinkClicked(const ui::Event&);
 
   // Used for loading pages.
   content::PageNavigator* navigator_;
@@ -87,11 +87,11 @@
   return true;
 }
 
-void NetworkProfileBubbleView::LinkClicked(int event_flags) {
+void NetworkProfileBubbleView::LinkClicked(const ui::Event& event) {
   NetworkProfileBubble::RecordUmaEvent(
       NetworkProfileBubble::METRIC_LEARN_MORE_CLICKED);
   WindowOpenDisposition disposition = ui::DispositionFromEventFlags(
-      event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB);
+      event.flags(), WindowOpenDisposition::NEW_FOREGROUND_TAB);
   content::OpenURLParams params(
       GURL("https://sites.google.com/a/chromium.org/dev/administrators/"
            "common-problems-and-solutions#network_profile"),
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 8ad2c50..0f0decb1 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -30,7 +30,7 @@
     {ExperimentIcon::kDownload, "download"}};
 
 constexpr base::FeatureParam<ExperimentIcon> kInstallIconParam{
-    &kInstallIconExperiment, "shape", ExperimentIcon::kDownloadToDevice,
+    &kInstallIconExperiment, "installIcon", ExperimentIcon::kDownloadToDevice,
     &kIconParamOptions};
 
 }  // namespace
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index d0769ee2..792487b 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -311,8 +311,8 @@
   EXPECT_FALSE(pwa_install_view_->GetVisible());
 }
 
-// Tests that the plus icon is shown when an existing app is installed and set
-// to open in a tab.
+// Tests that the install icon is shown when an existing app is installed and
+// set to open in a tab.
 IN_PROC_BROWSER_TEST_P(PwaInstallViewBrowserTest,
                        PwaSetToOpenInTabIsInstallable) {
   bool installable = OpenTab(GetInstallableAppURL()).installable;
@@ -333,6 +333,18 @@
   EXPECT_TRUE(pwa_install_view_->GetVisible());
 }
 
+// Test that the accept metrics is reported correctly.
+IN_PROC_BROWSER_TEST_P(PwaInstallViewBrowserTest, PwaInstalledMetricRecorded) {
+  bool installable = OpenTab(GetInstallableAppURL()).installable;
+  ASSERT_TRUE(installable);
+
+  base::HistogramTester histograms;
+  ExecutePwaInstallIcon();
+  histograms.ExpectUniqueSample(
+      "WebApp.InstallConfirmation.CloseReason",
+      views::Widget::ClosedReason::kAcceptButtonClicked, 1);
+}
+
 // Tests that the plus icon updates its visibility when switching between
 // installable/non-installable tabs.
 IN_PROC_BROWSER_TEST_P(PwaInstallViewBrowserTest,
@@ -360,7 +372,7 @@
   EXPECT_FALSE(pwa_install_view_->GetVisible());
 }
 
-// Tests that the plus icon updates its visibility when tab crashes.
+// Tests that the install icon updates its visibility when tab crashes.
 IN_PROC_BROWSER_TEST_P(PwaInstallViewBrowserTest,
                        IconVisibilityAfterTabCrashed) {
   StartNavigateToUrl(GetInstallableAppURL());
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 8de3ef7..d798dd1 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -448,14 +448,14 @@
                                 web_contents, url, std::move(closing_callback));
 }
 
-void PageInfoBubbleView::SecurityDetailsClicked(int event_flags) {
+void PageInfoBubbleView::SecurityDetailsClicked(const ui::Event& event) {
   if (GetSecurityDescriptionType() == SecurityDescriptionType::SAFETY_TIP) {
     OpenHelpCenterFromSafetyTip(web_contents());
   } else {
     web_contents()->OpenURL(content::OpenURLParams(
         GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(),
         ui::DispositionFromEventFlags(
-            event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB),
+            event.flags(), WindowOpenDisposition::NEW_FOREGROUND_TAB),
         ui::PAGE_TRANSITION_LINK, false));
     presenter_->RecordPageInfoAction(
         PageInfo::PAGE_INFO_CONNECTION_HELP_OPENED);
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.h b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
index 0c307bca..8123ad6 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.h
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
@@ -96,7 +96,7 @@
       const GURL& url,
       PageInfoClosingCallback closing_callback);
 
-  void SecurityDetailsClicked(int event_flags);
+  void SecurityDetailsClicked(const ui::Event& event);
   void ResetDecisionsClicked();
 
  protected:
diff --git a/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc b/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
index 079e7a0..5759252 100644
--- a/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
+++ b/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
@@ -85,12 +85,12 @@
   gfx::Range learn_more_range(offsets[1], message_text.length());
   views::StyledLabel::RangeStyleInfo link_style =
       views::StyledLabel::RangeStyleInfo::CreateForLink(base::BindRepeating(
-          [](content::WebContents* web_contents, int event_flags) {
+          [](content::WebContents* web_contents, const ui::Event& event) {
             web_contents->OpenURL(content::OpenURLParams(
                 GURL(chrome::kAdvancedProtectionDownloadLearnMoreURL),
                 content::Referrer(),
                 ui::DispositionFromEventFlags(
-                    event_flags, WindowOpenDisposition::NEW_FOREGROUND_TAB),
+                    event.flags(), WindowOpenDisposition::NEW_FOREGROUND_TAB),
                 ui::PAGE_TRANSITION_LINK, /*is_renderer_initiated=*/false));
           },
           web_contents));
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc
index 7ac583b..c83ea43f 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view.cc
+++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -293,11 +293,12 @@
   return uma_view;
 }
 
-void SessionCrashedBubbleView::ExplainStatisticsLinkClicked(int event_flags) {
+void SessionCrashedBubbleView::ExplainStatisticsLinkClicked(
+    const ui::Event& event) {
   browser_->OpenURL(content::OpenURLParams(
       GURL("https://support.google.com/chrome/answer/96817"),
       content::Referrer(),
-      ui::DispositionFromEventFlags(event_flags,
+      ui::DispositionFromEventFlags(event.flags(),
                                     WindowOpenDisposition::NEW_FOREGROUND_TAB),
       ui::PAGE_TRANSITION_LINK, false));
   RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_HELP);
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.h b/chrome/browser/ui/views/session_crashed_bubble_view.h
index 909c6467..8e857b9f9 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view.h
+++ b/chrome/browser/ui/views/session_crashed_bubble_view.h
@@ -57,7 +57,7 @@
   std::unique_ptr<views::View> CreateUmaOptInView();
 
   // Called when the user clicks the "statistics" link to get more information.
-  void ExplainStatisticsLinkClicked(int event_flags);
+  void ExplainStatisticsLinkClicked(const ui::Event& event);
 
   // Restore previous session after user selects so.
   void RestorePreviousSession();
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
index beac287..7553e1d 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
@@ -357,7 +357,7 @@
   return label;
 }
 
-void SharingDialogView::HelpLinkClicked(int event_flags) {
+void SharingDialogView::HelpLinkClicked() {
   std::move(data_.help_callback).Run(GetDialogType());
   CloseBubble();
 }
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.h b/chrome/browser/ui/views/sharing/sharing_dialog_view.h
index 0e060cc..9ea368cd 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view.h
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.h
@@ -75,7 +75,7 @@
   std::unique_ptr<views::StyledLabel> CreateHelpText();
 
   // Called when the "help" link is clicked.
-  void HelpLinkClicked(int event_flags);
+  void HelpLinkClicked();
 
   SharingDialogData data_;
 
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
index e23df02..566a517 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
@@ -181,7 +181,7 @@
   auto dialog_data = CreateDialogData(/*devices=*/0, /*apps=*/0);
   auto dialog = CreateDialogView(std::move(dialog_data));
 
-  dialog->HelpLinkClicked(ui::EF_NONE);
+  dialog->HelpLinkClicked();
 }
 
 TEST_F(SharingDialogViewTest, HelpTextClickedOnlyApps) {
@@ -191,7 +191,7 @@
   auto dialog_data = CreateDialogData(/*devices=*/0, /*apps=*/1);
   auto dialog = CreateDialogView(std::move(dialog_data));
 
-  dialog->HelpLinkClicked(ui::EF_NONE);
+  dialog->HelpLinkClicked();
 }
 
 TEST_F(SharingDialogViewTest, ThemeChangedEmptyList) {
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index 95cdcfef1..ff2e5fe0 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -257,12 +257,13 @@
   GetWidget()->Close();
 }
 
-void ProfileSigninConfirmationDialogViews::LearnMoreClicked(int event_flags) {
+void ProfileSigninConfirmationDialogViews::LearnMoreClicked(
+    const ui::Event& event) {
   NavigateParams params(
       browser_, GURL("https://support.google.com/chromebook/answer/1331549"),
       ui::PAGE_TRANSITION_LINK);
   params.disposition = ui::DispositionFromEventFlags(
-      event_flags, WindowOpenDisposition::NEW_POPUP);
+      event.flags(), WindowOpenDisposition::NEW_POPUP);
   params.window_action = NavigateParams::SHOW_WINDOW;
   Navigate(&params);
 }
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
index 3877bb0..d83079fab 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
@@ -51,7 +51,7 @@
   void ButtonPressed(views::Button*, const ui::Event& event) override;
 
   // Called when the "learn more" link is clicked.
-  void LearnMoreClicked(int event_flags);
+  void LearnMoreClicked(const ui::Event& event);
 
   // Weak ptr to parent view.
   Browser* const browser_;
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
index b02fc66..b4182f33 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
@@ -105,8 +105,8 @@
   GetWidget()->Close();
 }
 
-void TabModalConfirmDialogViews::LinkClicked(int event_flags) {
-  delegate_->LinkClicked(ui::DispositionFromEventFlags(event_flags));
+void TabModalConfirmDialogViews::LinkClicked(const ui::Event& event) {
+  delegate_->LinkClicked(ui::DispositionFromEventFlags(event.flags()));
 }
 
 views::View* TabModalConfirmDialogViews::GetInitiallyFocusedView() {
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
index ac10386..0106345 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
@@ -50,7 +50,7 @@
   void CancelTabModalDialog() override;
   void CloseDialog() override;
 
-  void LinkClicked(int event_flags);
+  void LinkClicked(const ui::Event& event);
 
   views::View* GetInitiallyFocusedView() override;
 
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
index f9c9eef..eb17b0c 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/i18n/message_formatter.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
@@ -172,6 +173,13 @@
 
 PWAConfirmationBubbleView::~PWAConfirmationBubbleView() = default;
 
+bool PWAConfirmationBubbleView::OnCloseRequested(
+    views::Widget::ClosedReason close_reason) {
+  base::UmaHistogramEnumeration("WebApp.InstallConfirmation.CloseReason",
+                                close_reason);
+  return LocationBarBubbleDelegateView::OnCloseRequested(close_reason);
+}
+
 views::View* PWAConfirmationBubbleView::GetInitiallyFocusedView() {
   return nullptr;
 }
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
index d32b360..58daea2f 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "chrome/common/web_application_info.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/views/widget/widget.h"
 
 namespace views {
 class Checkbox;
@@ -31,6 +32,7 @@
   ~PWAConfirmationBubbleView() override;
 
   // LocationBarBubbleDelegateView:
+  bool OnCloseRequested(views::Widget::ClosedReason close_reason) override;
   views::View* GetInitiallyFocusedView() override;
   void WindowClosing() override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
index 09a8204c..edb3b0b9 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
@@ -10,6 +10,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -114,3 +115,28 @@
       GetCallbackAppInfoFromDialog(/*run_on_os_login_checked=*/false);
   EXPECT_FALSE(resulting_app_info->run_on_os_login);
 }
+
+IN_PROC_BROWSER_TEST_F(PWAConfirmationBubbleViewBrowserTest,
+                       CancelledDialogReportsMetrics) {
+  auto app_info = GetAppInfo();
+  base::RunLoop loop;
+  // Show the PWA install dialog.
+  chrome::ShowPWAInstallBubble(
+      browser()->tab_strip_model()->GetActiveWebContents(), std::move(app_info),
+      base::BindLambdaForTesting(
+          [&](bool accepted,
+              std::unique_ptr<WebApplicationInfo> app_info_callback) {
+            loop.Quit();
+          }));
+
+  PWAConfirmationBubbleView* bubble_dialog =
+      PWAConfirmationBubbleView::GetBubbleForTesting();
+
+  base::HistogramTester histograms;
+  bubble_dialog->CancelDialog();
+  loop.Run();
+
+  histograms.ExpectUniqueSample(
+      "WebApp.InstallConfirmation.CloseReason",
+      views::Widget::ClosedReason::kCancelButtonClicked, 1);
+}
diff --git a/chrome/browser/ui/web_applications/test/ssl_test_utils.cc b/chrome/browser/ui/web_applications/test/ssl_test_utils.cc
index 7f4c6e3..e027590 100644
--- a/chrome/browser/ui/web_applications/test/ssl_test_utils.cc
+++ b/chrome/browser/ui/web_applications/test/ssl_test_utils.cc
@@ -46,11 +46,7 @@
   DCHECK(browser);
   ssl_test_util::CheckSecurityState(
       browser->tab_strip_model()->GetActiveWebContents(),
-      ssl_test_util::CertError::NONE,
-      base::FeatureList::IsEnabled(
-          security_state::features::kPassiveMixedContentWarning)
-          ? security_state::WARNING
-          : security_state::NONE,
+      ssl_test_util::CertError::NONE, security_state::WARNING,
       ssl_test_util::AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
index 32112b0..bd4d45b5 100644
--- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -6,6 +6,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/search/local_ntp_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
@@ -67,6 +68,10 @@
     }
   }
 
+  GURL NtpUrl() {
+    return local_ntp_test_utils::GetFinalNtpUrl(browser()->profile());
+  }
+
  protected:
   AppId app_id_;
   GURL app_url_{"https://example.org/dir/start.html"};
@@ -75,7 +80,6 @@
   GURL in_scope_2_{"https://example.org/dir/page2.html"};
   GURL origin_{"https://example.org/"};
   GURL out_of_scope_{"https://other-domain.org/"};
-  GURL new_tab_{"chrome://newtab/"};
 
  private:
   base::test::ScopedFeatureList features_;
@@ -121,7 +125,7 @@
   NavigateMainBrowser(in_scope_1_);
   Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
   EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(app_browser, app_id_));
-  ExpectTabs(browser(), {new_tab_});
+  ExpectTabs(browser(), {NtpUrl()});
   ExpectTabs(app_browser, {in_scope_1_});
   EXPECT_EQ(reparent_web_contents,
             app_browser->tab_strip_model()->GetActiveWebContents());
@@ -130,7 +134,7 @@
   // reparent. When there is already an app window open we should reparent into
   // it.
   chrome::AddTabAt(browser(), about_blank_, /*index=*/-1, /*foreground=*/true);
-  ExpectTabs(browser(), {new_tab_, about_blank_});
+  ExpectTabs(browser(), {NtpUrl(), about_blank_});
   reparent_web_contents = browser()->tab_strip_model()->GetActiveWebContents();
   {
     auto observer =
@@ -141,7 +145,7 @@
         base::StringPrintf("location = '%s';", in_scope_2_.spec().c_str())));
     observer->Wait();
   }
-  ExpectTabs(browser(), {new_tab_});
+  ExpectTabs(browser(), {NtpUrl()});
   ExpectTabs(app_browser, {in_scope_1_, in_scope_2_});
   EXPECT_EQ(reparent_web_contents,
             app_browser->tab_strip_model()->GetActiveWebContents());
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 80c304a..cb18c2ca 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -178,7 +178,6 @@
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_ui.h"
 #include "chrome/browser/ui/webui/chromeos/cryptohome_ui.h"
 #include "chrome/browser/ui/webui/chromeos/drive_internals_ui.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h"
 #include "chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h"
 #include "chrome/browser/ui/webui/chromeos/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h"
@@ -219,6 +218,8 @@
 
 #if defined(OS_CHROMEOS) && !defined(OFFICIAL_BUILD)
 #include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h"
+#include "chromeos/components/file_manager/file_manager_ui.h"
+#include "chromeos/components/file_manager/url_constants.h"
 #include "chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.h"
 #include "chromeos/components/sample_system_web_app_ui/url_constants.h"
 #include "chromeos/components/telemetry_extension_ui/telemetry_extension_ui.h"
@@ -648,9 +649,6 @@
     return &NewWebUI<chromeos::CryptohomeUI>;
   if (url.host_piece() == chrome::kChromeUIDriveInternalsHost)
     return &NewWebUI<chromeos::DriveInternalsUI>;
-  if (base::FeatureList::IsEnabled(chromeos::features::kFilesSWA) &&
-      url.host_piece() == chrome::kChromeUIFileManagerHost)
-    return &NewWebUI<chromeos::file_manager::FileManagerUI>;
   if (url.host_piece() == chrome::kChromeUIFirstRunHost)
     return &NewWebUI<chromeos::FirstRunUI>;
   if (url.host_piece() == chromeos::kChromeUIHelpAppHost)
@@ -735,6 +733,8 @@
       return &NewWebUI<DeviceEmulatorUI>;
   }
 #endif  // !defined(USE_REAL_DBUS_CLIENTS)
+  if (url.host_piece() == chromeos::file_manager::kChromeUIFileManagerHost)
+    return &NewWebUI<chromeos::file_manager::FileManagerUI>;
   if (url.host_piece() == chromeos::kChromeUISampleSystemWebAppHost)
     return &NewWebUI<chromeos::SampleSystemWebAppUI>;
   if (url.host_piece() == chromeos::kChromeUITelemetryExtensionHost) {
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
index c5c2c9bef..9a465126 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
@@ -49,7 +49,6 @@
       {"errorTitle", IDS_CROSTINI_INSTALLER_ERROR_TITLE},
 
       {"loadTerminaError", IDS_CROSTINI_INSTALLER_LOAD_TERMINA_ERROR},
-      {"startConciergeError", IDS_CROSTINI_INSTALLER_START_CONCIERGE_ERROR},
       {"createDiskImageError", IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_ERROR},
       {"startTerminaVmError", IDS_CROSTINI_INSTALLER_START_TERMINA_VM_ERROR},
       {"startContainerError", IDS_CROSTINI_INSTALLER_START_CONTAINER_ERROR},
@@ -61,7 +60,6 @@
       {"unknownError", IDS_CROSTINI_INSTALLER_UNKNOWN_ERROR},
 
       {"loadTerminaMessage", IDS_CROSTINI_INSTALLER_LOAD_TERMINA_MESSAGE},
-      {"startConciergeMessage", IDS_CROSTINI_INSTALLER_START_CONCIERGE_MESSAGE},
       {"createDiskImageMessage",
        IDS_CROSTINI_INSTALLER_CREATE_DISK_IMAGE_MESSAGE},
       {"startTerminaVmMessage",
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/BUILD.gn b/chrome/browser/ui/webui/chromeos/file_manager/BUILD.gn
deleted file mode 100644
index db7d34a..0000000
--- a/chrome/browser/ui/webui/chromeos/file_manager/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojo_bindings") {
-  sources = [ "file_manager.mojom" ]
-}
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.cc b/chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.cc
deleted file mode 100644
index 88d3cb9..0000000
--- a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-
-namespace chromeos {
-namespace file_manager {
-
-FileManagerUI::FileManagerUI(content::WebUI* web_ui)
-    : MojoWebUIController(web_ui) {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIFileManagerHost);
-  source->AddResourcePath("file_manager.css", IDR_FILE_MANAGER_CSS);
-  source->AddResourcePath("file_manager.js", IDR_FILE_MANAGER_JS);
-  source->AddResourcePath("file_manager.mojom-lite.js",
-                          IDR_FILE_MANAGER_MOJO_LITE_JS);
-  source->AddResourcePath("browser_proxy.js", IDR_FILE_MANAGER_PROXY_JS);
-
-  // Default content for chrome://file-manager: ensures unhandled URLs return
-  // 404 rather than content from SetDefaultResource().
-  source->AddResourcePath("", IDR_FILE_MANAGER_HTML);
-
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(profile, source);
-}
-
-FileManagerUI::~FileManagerUI() = default;
-
-void FileManagerUI::BindInterface(
-    mojo::PendingReceiver<mojom::PageHandlerFactory> pending_receiver) {
-  if (page_factory_receiver_.is_bound()) {
-    page_factory_receiver_.reset();
-  }
-  page_factory_receiver_.Bind(std::move(pending_receiver));
-}
-
-void FileManagerUI::CreatePageHandler(
-    mojo::PendingRemote<mojom::Page> pending_page,
-    mojo::PendingReceiver<mojom::PageHandler> pending_page_handler) {
-  DCHECK(pending_page.is_valid());
-
-  page_handler_ = std::make_unique<FileManagerPageHandler>(
-      std::move(pending_page_handler), std::move(pending_page));
-}
-
-WEB_UI_CONTROLLER_TYPE_IMPL(FileManagerUI)
-
-}  // namespace file_manager
-}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index 70df10d..7323af3 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -377,6 +377,8 @@
   OnVoiceSearchAction(VoiceSearchAction action);
   // Logs an error occurred while using voice search.
   OnVoiceSearchError(VoiceSearchError error);
+  // Logs that the modules have been shown at |time|.
+  OnModulesRendered(double time);
 
   // ======= REALBOX =======
   // Queries autocomplete matches from the browser.
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index dee7b7e..f950231 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -922,6 +922,11 @@
   LogEvent(event);
 }
 
+void NewTabPageHandler::OnModulesRendered(double time) {
+  logger_->LogEvent(NTP_MODULES_SHOWN,
+                    base::Time::FromJsTime(time) - ntp_navigation_start_time_);
+}
+
 void NewTabPageHandler::QueryAutocomplete(const base::string16& input,
                                           bool prevent_inline_autocomplete) {
   if (!autocomplete_controller_) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 1512ba16..025f694 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -122,6 +122,7 @@
   void OnVoiceSearchAction(
       new_tab_page::mojom::VoiceSearchAction action) override;
   void OnVoiceSearchError(new_tab_page::mojom::VoiceSearchError error) override;
+  void OnModulesRendered(double time) override;
   void QueryAutocomplete(const base::string16& input,
                          bool prevent_inline_autocomplete) override;
   void StopAutocomplete(bool clear_result) override;
diff --git a/chrome/browser/ui/webui/omnibox/omnibox.mojom b/chrome/browser/ui/webui/omnibox/omnibox.mojom
index 8648487..2bd08370 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox.mojom
+++ b/chrome/browser/ui/webui/omnibox/omnibox.mojom
@@ -70,7 +70,7 @@
 interface OmniboxPageHandler {
   // Registers the webui page.
   SetClientPage(pending_remote<OmniboxPage> page);
-  // Prompts a autocopmlete controller to process an omnibox query.
+  // Prompts an autocomplete controller to process an omnibox query.
   StartOmniboxQuery(string input_string,
                     bool reset_autocomplete_controller,
                     int32 cursor_position,
@@ -83,14 +83,14 @@
 
 interface OmniboxPage {
   // Notifies the page of an omnibox response from a autocomplete
-  // controller. |is_page_controller| indicates wether the response
+  // controller. |is_page_controller| indicates whether the response
   // originates from a query initiated from the page via
   // |StartOmniboxQuery| or from the browser omnibox.
   HandleNewAutocompleteResponse(OmniboxResponse response,
                                 bool is_page_controller);
   // Notifies the page a new omnibox query has begun.
   HandleNewAutocompleteQuery(bool is_page_controller, string input_text);
-  // Asyncronously notifies the page of the image data URLs for previous omnibox
-  // responses.
+  // Asynchronously notifies the page of the image data URLs for previous
+  // omnibox responses.
   HandleAnswerImageData(string image_url, string image_data);
 };
diff --git a/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc b/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
index aa0f46e..1f86b91c 100644
--- a/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
@@ -50,8 +50,10 @@
 
     for (variations::IDCollectionKey key :
          {variations::GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
+          variations::GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
           variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN,
-          variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT}) {
+          variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+          variations::GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY}) {
       const variations::VariationID id = variations::GetGoogleVariationID(
           key, trial->trial_name(), trial->group_name());
       if (id != variations::EMPTY_ID) {
diff --git a/chrome/browser/util/BUILD.gn b/chrome/browser/util/BUILD.gn
index 290dfd8..e8388828 100644
--- a/chrome/browser/util/BUILD.gn
+++ b/chrome/browser/util/BUILD.gn
@@ -23,7 +23,6 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_core_core_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
     "//ui/android:ui_full_java",
     "//url:gurl_java",
   ]
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 65b715d..9e1da36b 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -118,7 +118,10 @@
     ]
 
     if (!is_official_build) {
-      deps += [ "//chromeos/components/sample_system_web_app_ui" ]
+      deps += [
+        "//chromeos/components/file_manager:file_manager_ui",
+        "//chromeos/components/sample_system_web_app_ui",
+      ]
     }
   }
 
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index d368a83..6aa9111 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -59,6 +59,7 @@
 #include "extensions/common/constants.h"
 
 #if !defined(OFFICIAL_BUILD)
+#include "chrome/browser/chromeos/web_applications/file_manager_web_app_info.h"
 #include "chrome/browser/chromeos/web_applications/sample_system_web_app_info.h"
 #include "chrome/browser/chromeos/web_applications/telemetry_extension_web_app_info.h"
 #endif  // !defined(OFFICIAL_BUILD)
@@ -195,6 +196,12 @@
   }
 
   infos.emplace(
+      SystemAppType::FILE_MANAGER,
+      SystemAppInfo("File Manager", GURL("chrome://file-manager"),
+                    base::BindRepeating(&CreateWebAppInfoForFileManager)));
+  infos.at(SystemAppType::FILE_MANAGER).capture_navigations = true;
+
+  infos.emplace(
       SystemAppType::SAMPLE,
       SystemAppInfo(
           "Sample", GURL("chrome://sample-system-web-app/pwa.html"),
@@ -326,6 +333,8 @@
     case SystemAppType::TELEMETRY:
       return base::FeatureList::IsEnabled(
           chromeos::features::kTelemetryExtension);
+    case SystemAppType::FILE_MANAGER:
+      return base::FeatureList::IsEnabled(chromeos::features::kFilesSWA);
     case SystemAppType::SAMPLE:
       NOTREACHED();
       return false;
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index ddf6d55..add81c9 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -57,6 +57,7 @@
   SCANNING,
   DIAGNOSTICS,
 #if !defined(OFFICIAL_BUILD)
+  FILE_MANAGER,
   TELEMETRY,
   SAMPLE,
 #endif  // !defined(OFFICIAL_BUILD)
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java
index f3e9a5a..dcebe16 100644
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java
@@ -37,6 +37,22 @@
      *
      * @param url URL to request
      * @param responseConsumer The callback to call with the response
+     * @return Request ID that can be passed to cancel()
+     */
+    default int sendCancelableRequest(String url, HttpResponseConsumer responseConsumer) {
+        return 0;
+    }
+
+    /**
+     * Send a GET request. TODO(iwells): Remove when the caller switches to the cancelable version.
      */
     default void sendRequest(String url, HttpResponseConsumer responseConsumer) {}
+
+    /**
+     * Cancel a pending request. Causes the request's response callback to be called with an empty
+     * response body and net::Error::ERR_ABORTED.
+     *
+     * @param requestId ID of request to be canceled.
+     */
+    default void cancel(int requestId) {}
 }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 45c4d4c..14b466a 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1600106373-71253587ca194e48d78c58c419f94bf1bf8da7c5.profdata
+chrome-linux-master-1600127994-a617f89793652b5fd810a69f7c568f33dff933e7.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f9d791c55..24ded4da 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1600084447-6a3b264d40de14a9dcaaf57db5d6240ab2f7fff3.profdata
+chrome-mac-master-1600127994-515c383edf0ef93d8562747294461d0f81f3cc39.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 35132db..1cab3b61 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1600041484-a30af8f2a2acd0d94a4d04ce1984e089b193fca0.profdata
+chrome-win64-master-1600084447-f1bad6c3f3b8af9e95e3b8f79a7a1df8147866eb.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 0cd6ab7..d94b5a1 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -208,10 +208,12 @@
 
       if (!is_official_build) {
         sources += [
+          "$root_gen_dir/chromeos/chromeos_file_manager_resources.pak",
           "$root_gen_dir/chromeos/chromeos_sample_system_web_app_resources.pak",
           "$root_gen_dir/chromeos/chromeos_telemetry_extension_resources.pak",
         ]
         deps += [
+          "//chromeos/resources:file_manager_resources",
           "//chromeos/resources:sample_system_web_app_resources",
           "//chromeos/resources:telemetry_extension_resources",
         ]
diff --git a/chrome/common/search/ntp_logging_events.h b/chrome/common/search/ntp_logging_events.h
index a776c381..80dc2ee 100644
--- a/chrome/common/search/ntp_logging_events.h
+++ b/chrome/common/search/ntp_logging_events.h
@@ -173,7 +173,10 @@
   // Daily refresh was enabled by clicked 'Done' in the richer picker.
   NTP_BACKGROUND_DAILY_REFRESH_ENABLED = 82,
 
-  NTP_EVENT_TYPE_LAST = NTP_BACKGROUND_DAILY_REFRESH_ENABLED
+  // The NTP modules were shown.
+  NTP_MODULES_SHOWN = 83,
+
+  NTP_EVENT_TYPE_LAST = NTP_MODULES_SHOWN
 };
 
 // The different types of events that are logged for NTP search suggestions,
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 202d666..e1394cf 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -241,7 +241,6 @@
 const char kChromeUICryptohomeHost[] = "cryptohome";
 const char kChromeUIDeviceEmulatorHost[] = "device-emulator";
 const char kChromeUIDiscoverURL[] = "chrome://oobe/discover";
-const char kChromeUIFileManagerHost[] = "file-manager";
 const char kChromeUIFirstRunHost[] = "first-run";
 const char kChromeUIFirstRunURL[] = "chrome://first-run/";
 const char kChromeUIIntenetConfigDialogURL[] =
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index b97d699..ef5f63f 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -236,7 +236,6 @@
 extern const char kChromeUICryptohomeHost[];
 extern const char kChromeUIDeviceEmulatorHost[];
 extern const char kChromeUIDiscoverURL[];
-extern const char kChromeUIFileManagerHost[];
 extern const char kChromeUIFirstRunHost[];
 extern const char kChromeUIFirstRunURL[];
 extern const char kChromeUIIntenetConfigDialogURL[];
diff --git a/chrome/credential_provider/gaiacp/dllmain.cc b/chrome/credential_provider/gaiacp/dllmain.cc
index 323bfb7..5507c68d 100644
--- a/chrome/credential_provider/gaiacp/dllmain.cc
+++ b/chrome/credential_provider/gaiacp/dllmain.cc
@@ -67,6 +67,9 @@
 
 // Returns a class factory to create an object of the requested type.
 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
+  // This is performed in here to avoid from doing substantial work in DLLMain.
+  _AtlModule.LogProcessDetails();
+
   // Check to see if the credential provider has crashed too much recently.
   // If it has then do not allow it to create any credential providers.
   if (!credential_provider::WriteToStartupSentinel()) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
index ef20858..afcf745 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
@@ -104,6 +104,17 @@
   }
 }
 
+void CGaiaCredentialProviderModule::LogProcessDetails() {
+  wchar_t process_name[MAX_PATH] = {0};
+  GetModuleFileName(nullptr, process_name, MAX_PATH);
+
+  LOGFN(INFO) << "GCPW Initialized in " << process_name
+              << " GCPW Version: " << (CHROME_VERSION_STRING)
+              << " Windows Build: "
+              << base::win::OSInfo::GetInstance()->Kernel32BaseVersion()
+              << " Version:" << GetWindowsVersion();
+}
+
 BOOL CGaiaCredentialProviderModule::DllMain(HINSTANCE /*hinstance*/,
                                             DWORD reason,
                                             LPVOID reserved) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.h b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.h
index 42d337c1..d6d3dba 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.h
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.h
@@ -47,6 +47,9 @@
   // once even if the function is called multiple times.
   void InitializeCrashReporting();
 
+  // Logs the details of the module such as version, loading process.
+  void LogProcessDetails();
+
  private:
   std::unique_ptr<base::AtExitManager> exit_manager_;
   bool is_testing_ = false;
diff --git a/chrome/services/sharing/nearby/platform.cc b/chrome/services/sharing/nearby/platform.cc
index f735430..fab0eeed1 100644
--- a/chrome/services/sharing/nearby/platform.cc
+++ b/chrome/services/sharing/nearby/platform.cc
@@ -60,9 +60,9 @@
 
 std::unique_ptr<SubmittableExecutor>
 ImplementationPlatform::CreateMultiThreadExecutor(int max_concurrency) {
-  // Chrome task runner does not support max_concurrency.
-  return std::make_unique<chrome::SubmittableExecutor>(
-      base::ThreadPool::CreateTaskRunner({base::MayBlock()}));
+  // Chrome TaskRunner does not support |max_concurrency|. Simply use our
+  // SingleThreadExecutor.
+  return CreateSingleThreadExecutor();
 }
 
 std::unique_ptr<ScheduledExecutor>
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
index 6070f2e..5a4bf52 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
@@ -12,19 +12,48 @@
 
 BleMedium::BleMedium(bluetooth::mojom::Adapter* adapter) : adapter_(adapter) {}
 
-BleMedium::~BleMedium() = default;
+BleMedium::~BleMedium() {
+  for (auto& it : registered_advertisements_map_) {
+    // Note: this call is blocking.
+    it.second->Unregister();
+  }
+}
 
 bool BleMedium::StartAdvertising(const std::string& service_id,
                                  const ByteArray& advertisement) {
-  // TODO(b/154845685): Implement this method.
-  NOTIMPLEMENTED();
-  return false;
+  StopAdvertising(service_id);
+
+  auto service_uuid = device::BluetoothUUID(service_id);
+  mojo::PendingRemote<bluetooth::mojom::Advertisement> pending_advertisement;
+  bool success = adapter_->RegisterAdvertisement(
+      service_uuid,
+      std::vector<uint8_t>(advertisement.data(),
+                           advertisement.data() + advertisement.size()),
+      &pending_advertisement);
+
+  if (!success || !pending_advertisement.is_valid())
+    return false;
+
+  auto& remote_advertisement =
+      registered_advertisements_map_
+          .emplace(service_uuid, std::move(pending_advertisement))
+          .first->second;
+  remote_advertisement.set_disconnect_handler(base::BindOnce(
+      &BleMedium::AdvertisementReleased, base::Unretained(this), service_uuid));
+
+  return true;
 }
 
 bool BleMedium::StopAdvertising(const std::string& service_id) {
-  // TODO(b/154845685): Implement this method.
-  NOTIMPLEMENTED();
-  return false;
+  auto it =
+      registered_advertisements_map_.find(device::BluetoothUUID(service_id));
+  if (it == registered_advertisements_map_.end())
+    return true;
+
+  bool success = it->second->Unregister();
+  registered_advertisements_map_.erase(it);
+
+  return success;
 }
 
 bool BleMedium::StartScanning(const std::string& service_id,
@@ -209,6 +238,11 @@
   discovered_ble_peripherals_map_.erase(address);
 }
 
+void BleMedium::AdvertisementReleased(
+    const device::BluetoothUUID& service_uuid) {
+  registered_advertisements_map_.erase(service_uuid);
+}
+
 bool BleMedium::IsScanning() {
   return adapter_observer_.is_bound() && discovery_session_.is_bound() &&
          !discovered_peripheral_callbacks_map_.empty();
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.h b/chrome/services/sharing/nearby/platform_v2/ble_medium.h
index eac2628..0b151590 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium.h
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.h
@@ -55,6 +55,9 @@
   void DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) override;
   void DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) override;
 
+  // A bluetooth::mojom::Advertisement message pipe was destroyed.
+  void AdvertisementReleased(const device::BluetoothUUID& service_uuid);
+
   // Query if any service IDs are being scanned for.
   bool IsScanning();
 
@@ -72,6 +75,10 @@
   // events we don't care about outside of discovery don't pile up.
   mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this};
 
+  // Keyed by service UUID of the advertisement.
+  std::map<device::BluetoothUUID, mojo::Remote<bluetooth::mojom::Advertisement>>
+      registered_advertisements_map_;
+
   // Keyed by requested service UUID. Discovery is active as long as this map is
   // non-empty.
   std::map<device::BluetoothUUID, DiscoveredPeripheralCallback>
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
index 2347e72b..0ad91f2 100644
--- a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
+++ b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
@@ -31,6 +31,10 @@
 const char kDeviceServiceData1Str[] = "Device_Advertisement1";
 const char kDeviceServiceData2Str[] = "Device_Advertisement2";
 
+std::vector<uint8_t> GetByteVector(const std::string& str) {
+  return std::vector<uint8_t>(str.begin(), str.end());
+}
+
 }  // namespace
 
 class BleMediumTest : public testing::Test {
@@ -132,10 +136,6 @@
     run_loop.Run();
   }
 
-  std::vector<uint8_t> GetByteVector(const std::string& str) {
-    return std::vector<uint8_t>(str.begin(), str.end());
-  }
-
   void VerifyByteArrayEquals(const ByteArray& byte_array,
                              const std::string& expected_value) {
     EXPECT_EQ(expected_value,
@@ -219,7 +219,48 @@
 };
 
 TEST_F(BleMediumTest, TestAdvertising) {
-  // TODO(b/154845685): Write test.
+  ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId1)));
+  ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId2)));
+
+  ble_medium_->StartAdvertising(kServiceId1, ByteArray(kDeviceServiceData1Str));
+  EXPECT_EQ(GetByteVector(kDeviceServiceData1Str),
+            *fake_adapter_->GetRegisteredAdvertisementServiceData(
+                device::BluetoothUUID(kServiceId1)));
+  EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId2)));
+
+  ble_medium_->StartAdvertising(kServiceId2, ByteArray(kDeviceServiceData2Str));
+  EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId1)));
+  EXPECT_EQ(GetByteVector(kDeviceServiceData2Str),
+            *fake_adapter_->GetRegisteredAdvertisementServiceData(
+                device::BluetoothUUID(kServiceId2)));
+
+  {
+    base::RunLoop run_loop;
+    fake_adapter_->SetAdvertisementDestroyedCallback(run_loop.QuitClosure());
+    ble_medium_->StopAdvertising(kServiceId1);
+    run_loop.Run();
+  }
+
+  EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId1)));
+  EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId2)));
+
+  {
+    base::RunLoop run_loop;
+    fake_adapter_->SetAdvertisementDestroyedCallback(run_loop.QuitClosure());
+    ble_medium_->StopAdvertising(kServiceId2);
+    run_loop.Run();
+  }
+
+  EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId1)));
+  EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
+      device::BluetoothUUID(kServiceId2)));
 }
 
 TEST_F(BleMediumTest, TestScanning_OneService) {
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.cc b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.cc
index be629ca..be25b15 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.cc
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.cc
@@ -13,11 +13,16 @@
 
 BluetoothServerSocket::BluetoothServerSocket(
     mojo::PendingRemote<bluetooth::mojom::ServerSocket> server_socket)
-    : server_socket_(std::move(server_socket)) {}
+    : pending_server_socket_(std::move(server_socket)) {}
 
 BluetoothServerSocket::~BluetoothServerSocket() = default;
 
 std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
+  // We're now in the thread that BluetoothServerSocket primarily operates in.
+  // Bind |server_socket_| in this correct thread. See header documentation.
+  if (pending_server_socket_)
+    server_socket_.Bind(std::move(pending_server_socket_));
+
   bluetooth::mojom::AcceptConnectionResultPtr result;
   bool success = server_socket_->Accept(&result);
 
@@ -31,7 +36,7 @@
 }
 
 Exception BluetoothServerSocket::Close() {
-  // TODO(b/154849933): Implement this in a subsequent CL.
+  pending_server_socket_.reset();
   server_socket_.reset();
   return {Exception::kSuccess};
 }
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.h b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.h
index 656673f..1ebd149 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.h
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket.h
@@ -31,6 +31,14 @@
   Exception Close() override;
 
  private:
+  // BluetoothServerSocket is created on the main thread, but its public methods
+  // are used on a separate dedicated thread. mojo::Remote objects (namely,
+  // |server_socket_|) must be bound on the same thread they are used on, to
+  // prevent deadlock. So, we hold onto this mojo::PendingRemote
+  // |pending_server_socket_| until Accept() is called, at which point
+  // |server_socket_| is bound with it (it is acceptable to pass a
+  // mojo::PendingRemote around multiple threads).
+  mojo::PendingRemote<bluetooth::mojom::ServerSocket> pending_server_socket_;
   mojo::Remote<bluetooth::mojom::ServerSocket> server_socket_;
 };
 
diff --git a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket_unittest.cc b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket_unittest.cc
index 2a05983..9ef2a9e0 100644
--- a/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket_unittest.cc
+++ b/chrome/services/sharing/nearby/platform_v2/bluetooth_server_socket_unittest.cc
@@ -90,8 +90,19 @@
         std::move(fake_server_socket),
         pending_server_socket.InitWithNewPipeAndPassReceiver());
 
-    bluetooth_server_socket_ = std::make_unique<BluetoothServerSocket>(
-        std::move(pending_server_socket));
+    // In production Nearby Connections code, BluetoothServerSocket is created
+    // on a thread separate from the thread it is later used on. Replicate this
+    // behavior by creating |bluetooth_server_socket_| on a dedicated thread.
+    base::RunLoop run_loop;
+    auto creation_task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
+        {base::MayBlock()}, base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+    creation_task_runner->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&] {
+          bluetooth_server_socket_ = std::make_unique<BluetoothServerSocket>(
+              std::move(pending_server_socket));
+          run_loop.Quit();
+        }));
+    run_loop.Run();
   }
 
  protected:
diff --git a/chrome/services/sharing/nearby/test_support/fake_adapter.cc b/chrome/services/sharing/nearby/test_support/fake_adapter.cc
index 77a76c85..452affa6 100644
--- a/chrome/services/sharing/nearby/test_support/fake_adapter.cc
+++ b/chrome/services/sharing/nearby/test_support/fake_adapter.cc
@@ -13,6 +13,21 @@
 
 namespace {
 
+class FakeAdvertisement : public mojom::Advertisement {
+ public:
+  explicit FakeAdvertisement(base::OnceClosure on_destroy_callback)
+      : on_destroy_callback_(std::move(on_destroy_callback)) {}
+  ~FakeAdvertisement() override { std::move(on_destroy_callback_).Run(); }
+
+ private:
+  // mojom::Advertisement:
+  void Unregister(UnregisterCallback callback) override {
+    std::move(callback).Run();
+  }
+
+  base::OnceClosure on_destroy_callback_;
+};
+
 class FakeDiscoverySession : public mojom::DiscoverySession {
  public:
   explicit FakeDiscoverySession(base::OnceClosure on_destroy_callback)
@@ -88,6 +103,29 @@
   std::move(callback).Run();
 }
 
+void FakeAdapter::RegisterAdvertisement(
+    const device::BluetoothUUID& service_uuid,
+    const std::vector<uint8_t>& service_data,
+    RegisterAdvertisementCallback callback) {
+  if (!should_advertisement_registration_succeed_) {
+    std::move(callback).Run(mojo::NullRemote());
+    return;
+  }
+
+  registered_advertisements_map_.insert({service_uuid, service_data});
+
+  auto advertisement = std::make_unique<FakeAdvertisement>(
+      base::BindOnce(&FakeAdapter::OnAdvertisementDestroyed,
+                     base::Unretained(this), service_uuid));
+
+  mojo::PendingRemote<mojom::Advertisement> pending_advertisement;
+  mojo::MakeSelfOwnedReceiver(
+      std::move(advertisement),
+      pending_advertisement.InitWithNewPipeAndPassReceiver());
+
+  std::move(callback).Run(std::move(pending_advertisement));
+}
+
 void FakeAdapter::SetDiscoverable(bool discoverable,
                                   SetDiscoverableCallback callback) {
   discoverable_ = discoverable;
@@ -181,6 +219,17 @@
   should_discovery_succeed_ = should_discovery_succeed;
 }
 
+void FakeAdapter::SetAdvertisementDestroyedCallback(
+    base::OnceClosure callback) {
+  on_advertisement_destroyed_callback_ = std::move(callback);
+}
+
+const std::vector<uint8_t>* FakeAdapter::GetRegisteredAdvertisementServiceData(
+    const device::BluetoothUUID& service_uuid) {
+  auto it = registered_advertisements_map_.find(service_uuid);
+  return it == registered_advertisements_map_.end() ? nullptr : &it->second;
+}
+
 void FakeAdapter::SetDiscoverySessionDestroyedCallback(
     base::OnceClosure callback) {
   on_discovery_session_destroyed_callback_ = std::move(callback);
@@ -218,6 +267,14 @@
                                                               service_uuid);
 }
 
+void FakeAdapter::OnAdvertisementDestroyed(
+    const device::BluetoothUUID& service_uuid) {
+  DCHECK(!registered_advertisements_map_.empty());
+  registered_advertisements_map_.erase(service_uuid);
+  if (on_advertisement_destroyed_callback_)
+    std::move(on_advertisement_destroyed_callback_).Run();
+}
+
 void FakeAdapter::OnDiscoverySessionDestroyed() {
   DCHECK(discovery_session_);
   discovery_session_ = nullptr;
diff --git a/chrome/services/sharing/nearby/test_support/fake_adapter.h b/chrome/services/sharing/nearby/test_support/fake_adapter.h
index 4b4e15c..188759b 100644
--- a/chrome/services/sharing/nearby/test_support/fake_adapter.h
+++ b/chrome/services/sharing/nearby/test_support/fake_adapter.h
@@ -27,6 +27,9 @@
   void GetInfo(GetInfoCallback callback) override;
   void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
                    AddObserverCallback callback) override;
+  void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
+                             const std::vector<uint8_t>& service_data,
+                             RegisterAdvertisementCallback callback) override;
   void SetDiscoverable(bool discoverable,
                        SetDiscoverableCallback callback) override;
   void SetName(const std::string& name, SetNameCallback callback) override;
@@ -39,6 +42,9 @@
                            const device::BluetoothUUID& service_uuid,
                            CreateRfcommServiceCallback callback) override;
 
+  void SetAdvertisementDestroyedCallback(base::OnceClosure callback);
+  const std::vector<uint8_t>* GetRegisteredAdvertisementServiceData(
+      const device::BluetoothUUID& service_uuid);
   void SetShouldDiscoverySucceed(bool should_discovery_succeed);
   void SetDiscoverySessionDestroyedCallback(base::OnceClosure callback);
   bool IsDiscoverySessionActive();
@@ -61,11 +67,18 @@
   bool discovering_ = false;
 
  private:
+  void OnAdvertisementDestroyed(const device::BluetoothUUID& service_uuid);
   void OnDiscoverySessionDestroyed();
 
-  mojom::DiscoverySession* discovery_session_ = nullptr;
+  bool should_advertisement_registration_succeed_ = true;
+  std::map<device::BluetoothUUID, std::vector<uint8_t>>
+      registered_advertisements_map_;
+  base::OnceClosure on_advertisement_destroyed_callback_;
+
   bool should_discovery_succeed_ = true;
+  mojom::DiscoverySession* discovery_session_ = nullptr;
   base::OnceClosure on_discovery_session_destroyed_callback_;
+
   std::set<std::pair<std::string, device::BluetoothUUID>>
       allowed_connections_for_address_and_uuid_pair_;
   std::set<std::pair<std::string, device::BluetoothUUID>>
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6eb4883..bddbc4d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1273,6 +1273,7 @@
       "../browser/storage/durable_storage_browsertest.cc",
       "../browser/storage_access_api/api_browsertest.cc",
       "../browser/subresource_filter/ad_tagging_browsertest.cc",
+      "../browser/subresource_filter/ads_intervention_manager_browsertest.cc",
       "../browser/subresource_filter/ruleset_browsertest.cc",
       "../browser/subresource_filter/subresource_filter_browser_test_harness.cc",
       "../browser/subresource_filter/subresource_filter_browser_test_harness.h",
@@ -2718,6 +2719,8 @@
         "../browser/enterprise/reporting/report_scheduler_browsertest.cc",
         "../browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc",
       ]
+
+      deps += [ "//components/crash/content/browser/error_reporting:mock_crash_endpoint" ]
     } else {  # !is_chromeos
       sources -= [
         "../browser/invalidation/profile_invalidation_provider_factory_browsertest.cc",
@@ -3624,6 +3627,7 @@
     "../browser/status_icons/status_tray_unittest.cc",
     "../browser/storage/durable_storage_permission_context_unittest.cc",
     "../browser/storage_access_api/storage_access_grant_permission_context_unittest.cc",
+    "../browser/subresource_filter/ads_intervention_manager_unittest.cc",
     "../browser/subresource_filter/subresource_filter_abusive_unittest.cc",
     "../browser/subresource_filter/subresource_filter_configuration_unittest.cc",
     "../browser/subresource_filter/subresource_filter_content_settings_manager_unittest.cc",
@@ -5637,6 +5641,7 @@
     sources += [
       "../../ui/views/controls/webview/web_dialog_view_unittest.cc",
       "../../ui/views/controls/webview/webview_unittest.cc",
+      "../browser/ui/in_product_help/feature_promo_snooze_service_unittest.cc",
       "../browser/ui/media_router/media_router_ui_unittest.cc",
       "../browser/ui/views/accelerator_table_unittest.cc",
       "../browser/ui/views/accelerator_table_unittest_mac.mm",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index fb9ad051..cc999c43 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -302,7 +302,6 @@
     "//third_party/android_deps:androidx_appcompat_appcompat_java",
     "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_v4_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java
index f46a4c5..a51dd03 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java
@@ -23,9 +23,9 @@
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.test.util.ChromeRestriction;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.policy.test.annotations.Policies;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
diff --git a/chrome/test/data/pdf/annotations_feature_enabled_test.js b/chrome/test/data/pdf/annotations_feature_enabled_test.js
index 52dad0c9..838b87e 100644
--- a/chrome/test/data/pdf/annotations_feature_enabled_test.js
+++ b/chrome/test/data/pdf/annotations_feature_enabled_test.js
@@ -68,12 +68,6 @@
 
       chrome.test.assertEq(3, cameras.length);
 
-      if (updateEnabled) {
-        // TODO (https://crbug.com/1120279): Determine what the expectations
-        // below should be for the new UI and fix if needed to meet them.
-        return;
-      }
-
       const expectations = [
         {top: 44.25, left: -106.5, right: 718.5, bottom: -448.5},
         {top: 23.25, left: -3.75, right: 408.75, bottom: -223.125},
@@ -82,7 +76,10 @@
 
       for (const expectation of expectations) {
         const actual = cameras.shift();
-        chrome.test.assertEq(expectation.top, actual.top);
+        const expectationTop = updateEnabled ?
+            Math.min(2.25, expectation.top - 21) :
+            expectation.top;
+        chrome.test.assertEq(expectationTop, actual.top);
         chrome.test.assertEq(expectation.left, actual.left);
         chrome.test.assertEq(expectation.bottom, actual.bottom);
         chrome.test.assertEq(expectation.right, actual.right);
@@ -392,7 +389,7 @@
       const toolbar = document.createElement('viewer-pdf-toolbar-new');
       document.body.appendChild(toolbar);
       toolbar.toggleAnnotation();
-      chrome.test.assertTrue(toolbar.annotationMode);
+      toolbar.annotationMode = true;
 
       await toolbar.addEventListener('display-annotations-changed', async e => {
         chrome.test.assertFalse(e.detail);
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index dc99aab5..3ba36ed 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -268,6 +268,7 @@
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_nameservers_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_password_input_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_property_list_mojo_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_input_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_select_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.m.js",
diff --git a/chrome/test/data/webui/bluetooth_internals_browsertest.js b/chrome/test/data/webui/bluetooth_internals_browsertest.js
index 4dddc7b9..7bc01feb 100644
--- a/chrome/test/data/webui/bluetooth_internals_browsertest.js
+++ b/chrome/test/data/webui/bluetooth_internals_browsertest.js
@@ -126,6 +126,11 @@
         this.methodCalled('addObserver', observer);
       }
 
+      async registerAdvertisement() {
+        this.methodCalled('registerAdvertisement');
+        return {advertisement: null};
+      }
+
       async setDiscoverable() {
         this.methodCalled('setDiscoverable');
         return {success: true};
diff --git a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
index 7afe8f9..617eea1 100644
--- a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
+++ b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
@@ -149,7 +149,8 @@
     expectEquals(fakeBrowserProxy.handler.getCallCount('install'), 1);
     expectTrue(getInstallButton().hidden);
 
-    fakeBrowserProxy.page.onProgressUpdate(InstallerState.kStartConcierge, 0.5);
+    fakeBrowserProxy.page.onProgressUpdate(
+        InstallerState.kCreateDiskImage, 0.5);
     await flushTasks();
     expectTrue(
         !!app.$$('#installing-message > div').textContent.trim(),
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
index 507ea62..cc746ffb 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
@@ -43,6 +43,7 @@
   ['NetworkNameservers', 'network/network_nameservers_test.js', []],
   ['NetworkPasswordInput', 'network/network_password_input_test.js', []],
   ['NetworkPropertyListMojo', 'network/network_property_list_mojo_test.js', []],
+  ['NetworkProxyInput', 'network/network_proxy_input_test.js', []],
   ['NetworkSiminfo', 'network/network_siminfo_test.js', []],
 ].forEach(test => registerTest('NetworkComponents', 'os-settings', ...test));
 
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
index 84675bf..e7c58cd3 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
@@ -25,6 +25,7 @@
  ['NetworkNameservers', 'network/network_nameservers_test.m.js'],
  ['NetworkPasswordInput', 'network/network_password_input_test.m.js'],
  ['NetworkPropertyListMojo', 'network/network_property_list_mojo_test.m.js'],
+ ['NetworkProxyInput', 'network/network_proxy_input_test.m.js'],
  ['NetworkSelect', 'network/network_select_test.m.js'],
  ['NetworkSiminfo', 'network/network_siminfo_test.m.js'],
 ].forEach(test => registerTest('NetworkComponents', ...test));
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
index 5a744c3e..1eb1e489 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/network/BUILD.gn
@@ -21,6 +21,7 @@
     "network_nameservers_test.js",
     "network_password_input_test.js",
     "network_property_list_mojo_test.js",
+    "network_proxy_input_test.js",
     "network_select_test.js",
     "network_siminfo_test.js",
   ]
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_input_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_input_test.js
new file mode 100644
index 0000000..190362f0
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_proxy_input_test.js
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// clang-format off
+// #import 'chrome://os-settings/strings.m.js';
+// #import 'chrome://resources/cr_components/chromeos/network/network_proxy_input.m.js';
+//
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+suite('NetworkProxyInputTest', function() {
+  /** @type {!NetworkProxyInput|undefined} */
+  let proxyInput;
+
+  setup(function() {
+    proxyInput = document.createElement('network-proxy-input');
+    document.body.appendChild(proxyInput);
+    Polymer.dom.flush();
+  });
+
+  test('Editable inputs', function() {
+    assertFalse(proxyInput.editable);
+    assertTrue(proxyInput.$$('#host').readonly);
+    assertTrue(proxyInput.$$('#port').readonly);
+
+    proxyInput.editable = true;
+    Polymer.dom.flush();
+
+    assertFalse(proxyInput.$$('#host').readonly);
+    assertFalse(proxyInput.$$('#port').readonly);
+  });
+});
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js
index 31cdb0c..a761d4e 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.js
+++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -456,11 +456,11 @@
         }
       ]);
       await flushTasks();  // Wait for module descriptor resolution.
-      $$(app, '#modules').render();
 
       // Assert.
       const modules = app.shadowRoot.querySelectorAll('ntp-module-wrapper');
       assertEquals(2, modules.length);
+      assertEquals(1, testProxy.handler.getCallCount('onModulesRendered'));
     });
   });
 });
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 036cd58e..9c0cf707 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -117,7 +117,6 @@
   deps = [
     "//base:base_java",
     "//chromecast/base:base_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
     "//third_party/android_deps:androidx_localbroadcastmanager_localbroadcastmanager_java",
   ]
 }
@@ -192,7 +191,6 @@
     "//third_party/android_deps:androidx_core_core_java",
 
     # TODO(slan): We may need to pass this in as a parameter.
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
     "//third_party/android_deps:androidx_localbroadcastmanager_localbroadcastmanager_java",
     "//third_party/android_deps:androidx_slice_slice_builders_java",
 
@@ -256,7 +254,6 @@
     "//chromecast/base:base_java",
     "//chromecast/base:cast_base_test_utils_java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
     "//third_party/android_deps:androidx_localbroadcastmanager_localbroadcastmanager_java",
     "//third_party/hamcrest:hamcrest_java",
   ]
diff --git a/chromecast/media/audio/audio_log.cc b/chromecast/media/audio/audio_log.cc
index fbc5b2b..9c1d4a7 100644
--- a/chromecast/media/audio/audio_log.cc
+++ b/chromecast/media/audio/audio_log.cc
@@ -188,7 +188,9 @@
 }
 
 void AudioLogMessage::Cancel() {
-  buffer_->Cancel();
+  if (buffer_) {
+    buffer_->Cancel();
+  }
 }
 
 void InitializeAudioLog() {
diff --git a/chromecast/media/cma/backend/android/BUILD.gn b/chromecast/media/cma/backend/android/BUILD.gn
index 13649df2..dd4f679 100644
--- a/chromecast/media/cma/backend/android/BUILD.gn
+++ b/chromecast/media/cma/backend/android/BUILD.gn
@@ -74,7 +74,6 @@
     "//chromecast/base:base_java",
     "//chromecast/public:volume_control_enums_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_v13_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index 0a1402c..678048a4 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -57,6 +57,7 @@
 
   if (!is_official_build) {
     deps += [
+      "//chromeos/components/file_manager/resources:closure_compile",
       "//chromeos/components/sample_system_web_app_ui:closure_compile",
       "//chromeos/components/telemetry_extension_ui:closure_compile",
     ]
diff --git a/chromeos/components/cdm_factory_daemon/cdm_storage_adapter_unittest.cc b/chromeos/components/cdm_factory_daemon/cdm_storage_adapter_unittest.cc
index 37985dc..714ce17 100644
--- a/chromeos/components/cdm_factory_daemon/cdm_storage_adapter_unittest.cc
+++ b/chromeos/components/cdm_factory_daemon/cdm_storage_adapter_unittest.cc
@@ -84,8 +84,7 @@
         });
     cdm_storage_adapter_ = std::make_unique<CdmStorageAdapter>(
         mock_frame_interface_factory_.get(),
-        daemon_storage_mojo_
-            .BindNewEndpointAndPassDedicatedReceiverForTesting());
+        daemon_storage_mojo_.BindNewEndpointAndPassDedicatedReceiver());
     task_environment_.RunUntilIdle();
   }
 
diff --git a/chromeos/components/cdm_factory_daemon/content_decryption_module_adapter_unittest.cc b/chromeos/components/cdm_factory_daemon/content_decryption_module_adapter_unittest.cc
index 9172c10..8417658 100644
--- a/chromeos/components/cdm_factory_daemon/content_decryption_module_adapter_unittest.cc
+++ b/chromeos/components/cdm_factory_daemon/content_decryption_module_adapter_unittest.cc
@@ -130,7 +130,7 @@
   ContentDecryptionModuleAdapterTest() {
     mojo::AssociatedRemote<cdm::mojom::ContentDecryptionModule> daemon_cdm_mojo;
     mock_daemon_cdm_ = std::make_unique<MockDaemonCdm>(
-        daemon_cdm_mojo.BindNewEndpointAndPassDedicatedReceiverForTesting());
+        daemon_cdm_mojo.BindNewEndpointAndPassDedicatedReceiver());
     cdm_adapter_ = base::WrapRefCounted<ContentDecryptionModuleAdapter>(
         new ContentDecryptionModuleAdapter(
             nullptr /* storage */, std::move(daemon_cdm_mojo),
diff --git a/chromeos/components/file_manager/BUILD.gn b/chromeos/components/file_manager/BUILD.gn
new file mode 100644
index 0000000..14a08c17
--- /dev/null
+++ b/chromeos/components/file_manager/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(is_chromeos, "File Manager is Chrome OS only")
+assert(!is_official_build, "File Manager is only built for unofficial builds")
+
+mojom("file_manager_mojom") {
+  sources = [ "file_manager.mojom" ]
+}
+
+static_library("file_manager_ui") {
+  sources = [
+    "file_manager_page_handler.cc",
+    "file_manager_page_handler.h",
+    "file_manager_ui.cc",
+    "file_manager_ui.h",
+    "url_constants.cc",
+    "url_constants.h",
+  ]
+
+  deps = [
+    ":file_manager_mojom",
+    "//chromeos/constants",
+    "//chromeos/resources:file_manager_resources",
+    "//content/public/browser",
+    "//ui/webui",
+  ]
+}
diff --git a/chromeos/components/file_manager/DEPS b/chromeos/components/file_manager/DEPS
new file mode 100644
index 0000000..4cfbff5
--- /dev/null
+++ b/chromeos/components/file_manager/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  # Do not add chrome here (use a delegate instead).
+  "+chromeos/grit/chromeos_file_manager_resources.h",
+  "+content/public/browser",
+  "+ui/webui",
+]
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/OWNERS b/chromeos/components/file_manager/OWNERS
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/file_manager/OWNERS
rename to chromeos/components/file_manager/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/file_manager.mojom b/chromeos/components/file_manager/file_manager.mojom
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/file_manager/file_manager.mojom
rename to chromeos/components/file_manager/file_manager.mojom
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.cc b/chromeos/components/file_manager/file_manager_page_handler.cc
similarity index 77%
rename from chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.cc
rename to chromeos/components/file_manager/file_manager_page_handler.cc
index 52255d05a..ff83729 100644
--- a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.cc
+++ b/chromeos/components/file_manager/file_manager_page_handler.cc
@@ -2,15 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h"
+#include "chromeos/components/file_manager/file_manager_page_handler.h"
 
 #include "base/bind.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
 
 namespace chromeos {
 namespace file_manager {
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.h b/chromeos/components/file_manager/file_manager_page_handler.h
similarity index 79%
rename from chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.h
rename to chromeos/components/file_manager/file_manager_page_handler.h
index 603c689..9cc7f316 100644
--- a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_page_handler.h
+++ b/chromeos/components/file_manager/file_manager_page_handler.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
+#ifndef CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
+#define CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
 
 #include <memory>
 
 #include "base/macros.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager.mojom.h"
+#include "chromeos/components/file_manager/file_manager.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -47,4 +47,4 @@
 }  // namespace file_manager
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
+#endif  // CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_PAGE_HANDLER_H_
diff --git a/chromeos/components/file_manager/file_manager_ui.cc b/chromeos/components/file_manager/file_manager_ui.cc
new file mode 100644
index 0000000..b70e0d8
--- /dev/null
+++ b/chromeos/components/file_manager/file_manager_ui.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/file_manager/file_manager_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chromeos/components/file_manager/file_manager_page_handler.h"
+#include "chromeos/components/file_manager/url_constants.h"
+#include "chromeos/grit/chromeos_file_manager_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+namespace file_manager {
+
+FileManagerUI::FileManagerUI(content::WebUI* web_ui)
+    : MojoWebUIController(web_ui) {
+  auto source = base::WrapUnique(content::WebUIDataSource::Create(
+      chromeos::file_manager::kChromeUIFileManagerHost));
+  // The HTML content loaded on chrome://file-manager.
+  source->AddResourcePath("", IDR_FILE_MANAGER_FILE_MANAGER_HTML);
+
+  // The resources requested by chrome://file-manager HTML.
+  source->AddResourcePath("file_manager.css",
+                          IDR_FILE_MANAGER_FILE_MANAGER_CSS);
+  source->AddResourcePath("file_manager.js", IDR_FILE_MANAGER_FILE_MANAGER_JS);
+  source->AddResourcePath("file_manager.mojom-lite.js",
+                          IDR_FILE_MANAGER_MOJO_LITE_JS);
+  source->AddResourcePath("browser_proxy.js",
+                          IDR_FILE_MANAGER_BROWSER_PROXY_JS);
+
+#if !DCHECK_IS_ON()
+  // If a user goes to an invalid url and non-DCHECK mode (DHECK = debug mode)
+  // is set, serve a default page so the user sees your default page instead
+  // of an unexpected error. But if DCHECK is set, the user will be a
+  // developer and be able to identify an error occurred.
+  source->SetDefaultResource(IDR_FILE_MANAGER_FILE_MANAGER_HTML);
+#endif  // !DCHECK_IS_ON()
+
+  auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
+  content::WebUIDataSource::Add(browser_context, source.release());
+}
+
+FileManagerUI::~FileManagerUI() = default;
+
+void FileManagerUI::BindInterface(
+    mojo::PendingReceiver<mojom::PageHandlerFactory> pending_receiver) {
+  if (page_factory_receiver_.is_bound()) {
+    page_factory_receiver_.reset();
+  }
+  page_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void FileManagerUI::CreatePageHandler(
+    mojo::PendingRemote<mojom::Page> pending_page,
+    mojo::PendingReceiver<mojom::PageHandler> pending_page_handler) {
+  DCHECK(pending_page.is_valid());
+
+  page_handler_ = std::make_unique<FileManagerPageHandler>(
+      std::move(pending_page_handler), std::move(pending_page));
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(FileManagerUI)
+
+}  // namespace file_manager
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h b/chromeos/components/file_manager/file_manager_ui.h
similarity index 81%
rename from chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h
rename to chromeos/components/file_manager/file_manager_ui.h
index b33ab46..5d30f88 100644
--- a/chrome/browser/ui/webui/chromeos/file_manager/file_manager_ui.h
+++ b/chromeos/components/file_manager/file_manager_ui.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_UI_H_
+#ifndef CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_UI_H_
+#define CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_UI_H_
 
 #include <memory>
 
-#include "chrome/browser/ui/webui/chromeos/file_manager/file_manager.mojom.h"
+#include "chromeos/components/file_manager/file_manager.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -47,4 +47,4 @@
 }  // namespace file_manager
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_FILE_MANAGER_FILE_MANAGER_UI_H_
+#endif  // CHROMEOS_COMPONENTS_FILE_MANAGER_FILE_MANAGER_UI_H_
diff --git a/chromeos/components/file_manager/resources/BUILD.gn b/chromeos/components/file_manager/resources/BUILD.gn
new file mode 100644
index 0000000..5d64f74
--- /dev/null
+++ b/chromeos/components/file_manager/resources/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_library("file_manager_js") {
+  sources = [
+    "browser_proxy.js",
+    "file_manager.js",
+  ]
+  deps = [ "//chromeos/components/file_manager:file_manager_mojom_js_library_for_compile" ]
+}
+
+js_type_check("closure_compile") {
+  deps = [ ":file_manager_js" ]
+}
diff --git a/chrome/browser/resources/chromeos/file_manager/browser_proxy.js b/chromeos/components/file_manager/resources/browser_proxy.js
similarity index 100%
rename from chrome/browser/resources/chromeos/file_manager/browser_proxy.js
rename to chromeos/components/file_manager/resources/browser_proxy.js
diff --git a/chrome/browser/resources/chromeos/file_manager/file_manager.css b/chromeos/components/file_manager/resources/file_manager.css
similarity index 98%
rename from chrome/browser/resources/chromeos/file_manager/file_manager.css
rename to chromeos/components/file_manager/resources/file_manager.css
index 4a56066..2f32ae0 100644
--- a/chrome/browser/resources/chromeos/file_manager/file_manager.css
+++ b/chromeos/components/file_manager/resources/file_manager.css
@@ -1,4 +1,3 @@
 /* Copyright (c) 2020 The Chromium Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
-
diff --git a/chrome/browser/resources/chromeos/file_manager/file_manager.html b/chromeos/components/file_manager/resources/file_manager.html
similarity index 100%
rename from chrome/browser/resources/chromeos/file_manager/file_manager.html
rename to chromeos/components/file_manager/resources/file_manager.html
diff --git a/chrome/browser/resources/chromeos/file_manager/file_manager.js b/chromeos/components/file_manager/resources/file_manager.js
similarity index 88%
rename from chrome/browser/resources/chromeos/file_manager/file_manager.js
rename to chromeos/components/file_manager/resources/file_manager.js
index 31beeeb..8ed4391 100644
--- a/chrome/browser/resources/chromeos/file_manager/file_manager.js
+++ b/chromeos/components/file_manager/resources/file_manager.js
@@ -20,6 +20,6 @@
       console.log('eh? ' + something + '. what? ' + other);
     });
 
-document.addEventListener('DOMContentLoaded', function() {
-  alert('DOMContentLoaded');
+document.addEventListener('DOMContentLoaded', () => {
+  console.info('File manager launched ...');
 });
diff --git a/chromeos/components/file_manager/resources/file_manager_resources.grd b/chromeos/components/file_manager/resources/file_manager_resources.grd
new file mode 100644
index 0000000..4044b190
--- /dev/null
+++ b/chromeos/components/file_manager/resources/file_manager_resources.grd
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/chromeos_file_manager_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/chromeos_file_manager_resources_map.cc"
+      type="resource_file_map_source" />
+    <output filename="grit/chromeos_file_manager_resources_map.h"
+      type="resource_map_header" />
+    <output filename="chromeos_file_manager_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <includes>
+      <if expr="is_official_build == false">
+        <!-- Privileged app host contents. -->
+        <include name="IDR_FILE_MANAGER_BROWSER_PROXY_JS" file="browser_proxy.js" type="BINDATA" />
+        <include name="IDR_FILE_MANAGER_FILE_MANAGER_HTML" file="file_manager.html" type="BINDATA" />
+        <include name="IDR_FILE_MANAGER_FILE_MANAGER_CSS" file="file_manager.css" type="BINDATA" />
+        <include name="IDR_FILE_MANAGER_FILE_MANAGER_JS" file="file_manager.js" type="BINDATA" />
+        <include name="IDR_FILE_MANAGER_ICON_192" file="icon192.png" type="BINDATA" />
+
+        <!-- Mojo resources -->
+        <include name="IDR_FILE_MANAGER_MOJO_LITE_JS"
+          file="$root_gen_dir\chromeos\components\file_manager\file_manager.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA" />
+      </if>
+    </includes>
+  </release>
+</grit>
+
diff --git a/chromeos/components/file_manager/resources/icon192.png b/chromeos/components/file_manager/resources/icon192.png
new file mode 100644
index 0000000..b49fdee
--- /dev/null
+++ b/chromeos/components/file_manager/resources/icon192.png
Binary files differ
diff --git a/chromeos/components/file_manager/url_constants.cc b/chromeos/components/file_manager/url_constants.cc
new file mode 100644
index 0000000..c4be8375
--- /dev/null
+++ b/chromeos/components/file_manager/url_constants.cc
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/file_manager/url_constants.h"
+
+namespace chromeos {
+namespace file_manager {
+
+constexpr char kChromeUIFileManagerHost[] = "file-manager";
+constexpr char kChromeUIFileManagerURL[] = "chrome://file-manager";
+
+}  // namespace file_manager
+}  // namespace chromeos
diff --git a/chromeos/components/file_manager/url_constants.h b/chromeos/components/file_manager/url_constants.h
new file mode 100644
index 0000000..143d697
--- /dev/null
+++ b/chromeos/components/file_manager/url_constants.h
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_FILE_MANAGER_URL_CONSTANTS_H_
+#define CHROMEOS_COMPONENTS_FILE_MANAGER_URL_CONSTANTS_H_
+
+namespace chromeos {
+namespace file_manager {
+
+extern const char kChromeUIFileManagerHost[];
+extern const char kChromeUIFileManagerURL[];
+
+}  // namespace file_manager
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_FILE_MANAGER_URL_CONSTANTS_H_
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.cc b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
index 4ae7156..878c339 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
@@ -531,16 +531,6 @@
                        std::move(error_callback)));
   }
 
-  void StartConcierge(ConciergeCallback callback) override {
-    dbus::MethodCall method_call(debugd::kDebugdInterface,
-                                 debugd::kStartVmConcierge);
-    dbus::MessageWriter writer(&method_call);
-    debugdaemon_proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&DebugDaemonClientImpl::OnStartConcierge,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  }
-
   void StartPluginVmDispatcher(const std::string& owner_id,
                                const std::string& lang,
                                PluginVmDispatcherCallback callback) override {
@@ -870,15 +860,6 @@
       std::move(error_callback).Run();
   }
 
-  void OnStartConcierge(ConciergeCallback callback, dbus::Response* response) {
-    bool result = false;
-    if (response) {
-      dbus::MessageReader reader(response);
-      reader.PopBool(&result);
-    }
-    std::move(callback).Run(result);
-  }
-
   void OnStartPluginVmDispatcher(PluginVmDispatcherCallback callback,
                                  dbus::Response* response,
                                  dbus::ErrorResponse* error) {
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.h b/chromeos/dbus/debug_daemon/debug_daemon_client.h
index bed3e4f..d493c10 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.h
@@ -235,14 +235,6 @@
                                  CupsRemovePrinterCallback callback,
                                  base::OnceClosure error_callback) = 0;
 
-  // A callback to handle the result of StartConcierge.
-  using ConciergeCallback = base::OnceCallback<void(bool success)>;
-  // Calls debugd::kStartVmConcierge, which starts the Concierge service.
-  // |callback| is called when the method finishes. If the |callback| is called
-  // with true, it is guaranteed that the service is ready to accept requests.
-  // It is not necessary for ConciergeClient to use WaitForServiceToBeAvailable.
-  virtual void StartConcierge(ConciergeCallback callback) = 0;
-
   // A callback to handle the result of
   // StartPluginVmDispatcher/StopPluginVmDispatcher.
   using PluginVmDispatcherCallback = base::OnceCallback<void(bool success)>;
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
index a4d66a5e..5bda3a2 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
@@ -257,11 +257,6 @@
       FROM_HERE, base::BindOnce(std::move(callback), has_printer));
 }
 
-void FakeDebugDaemonClient::StartConcierge(ConciergeCallback callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), true));
-}
-
 void FakeDebugDaemonClient::StartPluginVmDispatcher(
     const std::string& /* owner_id */,
     const std::string& /* lang */,
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
index 798a642a..069b356 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
@@ -88,7 +88,6 @@
   void CupsRemovePrinter(const std::string& name,
                          CupsRemovePrinterCallback callback,
                          base::OnceClosure error_callback) override;
-  void StartConcierge(ConciergeCallback callback) override;
   void StartPluginVmDispatcher(const std::string& owner_id,
                                const std::string& lang,
                                PluginVmDispatcherCallback callback) override;
diff --git a/chromeos/resources/BUILD.gn b/chromeos/resources/BUILD.gn
index 8226d82..ea4e295 100644
--- a/chromeos/resources/BUILD.gn
+++ b/chromeos/resources/BUILD.gn
@@ -66,6 +66,29 @@
   ]
 }
 
+# Resources used by chrome://file-manager
+grit("file_manager_resources") {
+  source = "../components/file_manager/resources/file_manager_resources.grd"
+
+  outputs = [
+    "grit/chromeos_file_manager_resources.h",
+    "grit/chromeos_file_manager_resources_map.cc",
+    "grit/chromeos_file_manager_resources_map.h",
+    "chromeos_file_manager_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/chromeos"
+
+  grit_flags = [
+    "-E",
+    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
+  ]
+
+  deps = [
+    "//chromeos/components/file_manager:file_manager_mojom_js",
+    "//mojo/public/js:bindings_lite",
+  ]
+}
+
 # Resources used by chrome://help-app, and parts of the sandboxed app it hosts
 # that do not come from the app bundle (below).
 grit("help_app_resources") {
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 0dcf6da..ebf7e34 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -301,9 +301,10 @@
       "//components/webcrypto:unit_tests",
     ]
 
-    if (!is_fuchsia) {
+    if (!is_fuchsia) {  # !iOS and !Fuchsia
       deps += [
         "//components/crash/content/browser:unit_tests",
+        "//components/crash/content/browser/error_reporting:unit_tests",
         "//components/crash/core/app:unit_tests",
         "//components/data_reduction_proxy/core/browser:unit_tests",
         "//components/data_reduction_proxy/core/common:unit_tests",
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 9b62fe38..d831266 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -67,13 +67,7 @@
     LOG(ERROR) << "SetVmCpuRestriction for ARCVM failed";
 }
 
-void DoSetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state,
-                              bool concierge_started) {
-  if (!concierge_started) {
-    LOG(ERROR) << "Concierge D-Bus service is not available";
-    return;
-  }
-
+void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state) {
   auto* client = chromeos::DBusThreadManager::Get()->GetConciergeClient();
   if (!client) {
     LOG(ERROR) << "ConciergeClient is not available";
@@ -97,17 +91,6 @@
                               base::BindOnce(&OnSetArcVmCpuRestriction));
 }
 
-void SetArcVmCpuRestriction(CpuRestrictionState cpu_restriction_state) {
-  auto* client = chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
-  if (!client) {
-    LOG(WARNING) << "DebugDaemonClient is not available";
-    return;
-  }
-  // TODO(wvk): Call StartConcierge() only when the service is not running.
-  client->StartConcierge(
-      base::BindOnce(&DoSetArcVmCpuRestriction, cpu_restriction_state));
-}
-
 void SetArcContainerCpuRestriction(CpuRestrictionState cpu_restriction_state) {
   if (!chromeos::SessionManagerClient::Get()) {
     LOG(WARNING) << "SessionManagerClient is not available";
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index d25be35..d112e43 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -527,14 +527,6 @@
                        weak_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  void UpgradeArc(UpgradeParams params,
-                  chromeos::VoidDBusMethodCallback callback) override {
-    VLOG(1) << "Starting Concierge service";
-    GetDebugDaemonClient()->StartConcierge(base::BindOnce(
-        &ArcVmClientAdapter::OnConciergeStarted, weak_factory_.GetWeakPtr(),
-        std::move(params), std::move(callback)));
-  }
-
   void StopArcInstance(bool on_shutdown, bool should_backup_log) override {
     if (on_shutdown) {
       // Do nothing when |on_shutdown| is true because either vm_concierge.conf
@@ -675,14 +667,8 @@
     should_notify_observers_ = true;
   }
 
-  void OnConciergeStarted(UpgradeParams params,
-                          chromeos::VoidDBusMethodCallback callback,
-                          bool success) {
-    if (!success) {
-      LOG(ERROR) << "Failed to start Concierge service for arcvm";
-      std::move(callback).Run(false);
-      return;
-    }
+  void UpgradeArc(UpgradeParams params,
+                  chromeos::VoidDBusMethodCallback callback) override {
     VLOG(2) << "Checking file system status";
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 6a469e4..0cc55878 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -97,11 +97,6 @@
   TestDebugDaemonClient() = default;
   ~TestDebugDaemonClient() override = default;
 
-  void StartConcierge(ConciergeCallback callback) override {
-    start_concierge_called_ = true;
-    std::move(callback).Run(start_concierge_result_);
-  }
-
   void BackupArcBugReport(const std::string& userhash,
                           chromeos::VoidDBusMethodCallback callback) override {
     backup_arc_bug_report_called_ = true;
@@ -115,14 +110,7 @@
     backup_arc_bug_report_result_ = result;
   }
 
-  bool start_concierge_called() const { return start_concierge_called_; }
-  void set_start_concierge_result(bool result) {
-    start_concierge_result_ = result;
-  }
-
  private:
-  bool start_concierge_called_ = false;
-  bool start_concierge_result_ = true;
   bool backup_arc_bug_report_called_ = false;
   bool backup_arc_bug_report_result_ = true;
 
@@ -313,14 +301,6 @@
   }
 
  protected:
-  bool GetStartConciergeCalled() {
-    return GetTestDebugDaemonClient()->start_concierge_called();
-  }
-
-  void SetStartConciergeResponse(bool response) {
-    GetTestDebugDaemonClient()->set_start_concierge_result(response);
-  }
-
   void SetValidUserInfo() { SetUserInfo(kUserIdHash, kSerialNumber); }
 
   void SetUserInfo(const std::string& hash, const std::string& serial) {
@@ -755,7 +735,6 @@
   InjectUpstartStartJobFailure(kArcVmServerProxyJobName);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -781,7 +760,6 @@
 
   // Upgrade should still succeed.
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -796,7 +774,6 @@
 
   EnableAdbOverUsbForTesting();
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -821,7 +798,6 @@
   InjectUpstartStartJobFailure(kArcCreateDataJobName);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -847,7 +823,6 @@
   InjectUpstartStartJobFailure(kArcVmMountMyFilesJobName);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -874,30 +849,6 @@
   InjectUpstartStartJobFailure(kArcVmMountRemovableMediaJobName);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
-  EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
-  EXPECT_FALSE(arc_instance_stopped_called());
-
-  // Try to stop the VM. StopVm will fail in this case because
-  // no VM is running.
-  vm_tools::concierge::StopVmResponse response;
-  response.set_success(false);
-  GetTestConciergeClient()->set_stop_vm_response(response);
-  adapter()->StopArcInstance(/*on_shutdown=*/false,
-                             /*should_backup_log=*/false);
-  run_loop()->Run();
-  EXPECT_TRUE(GetTestConciergeClient()->stop_vm_called());
-  EXPECT_TRUE(arc_instance_stopped_called());
-}
-
-// Tests that UpgradeArc() handles StartConcierge() failures properly.
-TEST_F(ArcVmClientAdapterTest, UpgradeArc_StartConciergeFailure) {
-  SetValidUserInfo();
-  StartMiniArc();
-  // Inject failure to StartConcierge().
-  SetStartConciergeResponse(false);
-  UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -921,7 +872,6 @@
   StartMiniArc();
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -945,7 +895,6 @@
   StartMiniArc();
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -971,7 +920,6 @@
   GetTestConciergeClient()->set_start_vm_response(start_vm_response);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -994,7 +942,6 @@
   GetTestConciergeClient()->set_start_vm_response(base::nullopt);
 
   UpgradeArc(false);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1015,7 +962,6 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1040,7 +986,6 @@
 
   UpgradeParams params(GetPopulatedUpgradeParams());
   UpgradeArcWithParams(true, std::move(params));
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -1064,7 +1009,6 @@
   params.preferred_languages = {"en_US"};
 
   UpgradeArcWithParams(true, std::move(params));
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 }
@@ -1084,7 +1028,6 @@
   params.demo_session_apps_path = base::FilePath(kDemoImage);
 
   UpgradeArcWithParams(true, std::move(params));
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1108,7 +1051,6 @@
   StartMiniArcWithParams(true, std::move(start_params));
   UpgradeParams params(GetPopulatedUpgradeParams());
   UpgradeArcWithParams(true, std::move(params));
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
   EXPECT_TRUE(
@@ -1141,7 +1083,6 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1156,7 +1097,6 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1171,7 +1111,6 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
@@ -1194,7 +1133,6 @@
   SetValidUserInfo();
   StartMiniArc();
   UpgradeArc(true);
-  EXPECT_TRUE(GetStartConciergeCalled());
   EXPECT_TRUE(GetTestConciergeClient()->start_arc_vm_called());
   EXPECT_FALSE(arc_instance_stopped_called());
 
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index b13a360..d50a485 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -1062,7 +1062,8 @@
   delete active_experiments_;
   active_experiments_ = new std::vector<variations::VariationID>(
       variations_ids_provider->GetVariationsVector(
-          variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT));
+          {variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+           variations::GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY}));
   base::EraseIf(*active_experiments_, [](variations::VariationID id) {
     return !IsAutofillExperimentId(id);
   });
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 6a29dfd..748e5d2 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -112,17 +112,15 @@
     ON_CALL(*this, GetChannel())
         .WillByDefault(Return(version_info::Channel::UNKNOWN));
   }
-
-  ~MockAutofillClient() override {}
+  MockAutofillClient(const MockAutofillClient&) = delete;
+  MockAutofillClient& operator=(const MockAutofillClient&) = delete;
+  ~MockAutofillClient() override = default;
 
   MOCK_METHOD0(ShouldShowSigninPromo, bool());
   MOCK_CONST_METHOD0(GetChannel, version_info::Channel());
   MOCK_METHOD2(ConfirmSaveUpiIdLocally,
                void(const std::string& upi_id,
                     base::OnceCallback<void(bool user_decision)> callback));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockAutofillClient);
 };
 
 class MockAutofillDownloadManager : public TestAutofillDownloadManager {
@@ -130,6 +128,9 @@
   MockAutofillDownloadManager(AutofillDriver* driver,
                               AutofillDownloadManager::Observer* observer)
       : TestAutofillDownloadManager(driver, observer) {}
+  MockAutofillDownloadManager(const MockAutofillDownloadManager&) = delete;
+  MockAutofillDownloadManager& operator=(const MockAutofillDownloadManager&) =
+      delete;
 
   MOCK_METHOD6(StartUploadRequest,
                bool(const FormStructure&,
@@ -138,9 +139,6 @@
                     const std::string&,
                     bool,
                     PrefService*));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockAutofillDownloadManager);
 };
 
 void ExpectFilledField(const char* expected_label,
@@ -305,7 +303,9 @@
 
 class MockAutofillDriver : public TestAutofillDriver {
  public:
-  MockAutofillDriver() {}
+  MockAutofillDriver() = default;
+  MockAutofillDriver(const MockAutofillDriver&) = delete;
+  MockAutofillDriver& operator=(const MockAutofillDriver&) = delete;
 
   // Mock methods to enable testability.
   MOCK_METHOD3(SendFormDataToRenderer,
@@ -315,16 +315,13 @@
 
   MOCK_METHOD1(SendAutofillTypePredictionsToRenderer,
                void(const std::vector<FormStructure*>& forms));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver);
 };
 
 }  // namespace
 
 class AutofillManagerTest : public testing::Test {
  public:
-  AutofillManagerTest() {}
+  AutofillManagerTest() = default;
 
   void SetUp() override {
     autofill_client_.SetPrefs(test::PrefServiceForTesting());
@@ -5575,10 +5572,9 @@
     : public AutofillManagerTest,
       public ::testing::WithParamInterface<
           std::tuple<ProfileMatchingTypesTestCase,
-                     int,    // AutofillDataModel::ValidityState
-                     bool,   // AutofillDataModel::ValidationSource
-                     bool>>  // kAutofillEnableSupportForMoreStructureInNames
-{
+                     int,      // AutofillDataModel::ValidityState
+                     bool,     // AutofillDataModel::ValidationSource
+                     bool>> {  // kAutofillEnableSupportForMoreStructureInNames
  protected:
   void SetUp() override {
     AutofillManagerTest::SetUp();
@@ -5763,7 +5759,7 @@
   for (auto type : expected_possible_types) {
     if (GroupTypeOfServerFieldType(type) != CREDIT_CARD) {
       for (auto& profile : profiles) {
-        ASSERT_TRUE(test_case.field_types.size() > 0);
+        ASSERT_GT(test_case.field_types.size(), 0U);
         if (type == UNKNOWN_TYPE) {
           // An UNKNOWN type is always UNVALIDATED
           validity_state = AutofillDataModel::UNVALIDATED;
@@ -6154,9 +6150,8 @@
                       (possible_types.count(NAME_LAST_SECOND) ||
                        possible_types.count(NAME_LAST_FIRST) ||
                        possible_types.count(NAME_FULL)));
-        }
-        // Or even all three.
-        else if (StructuredNames() && possible_types.size() == 3) {
+        } else if (StructuredNames() && possible_types.size() == 3) {
+          // Or even all three.
           EXPECT_TRUE(possible_types.count(NAME_FULL) &&
                       possible_types.count(NAME_LAST) &&
                       (possible_types.count(NAME_LAST_SECOND) ||
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 59461e39..67cdd64 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -487,7 +487,6 @@
                           "2109" /* Mastercard */, NextMonth().c_str(),
                           NextYear().c_str(), "1");
   credit_card.SetNetworkForMaskedCard(kMasterCard);
-  credit_card.set_instrument_id(1);
   return credit_card;
 }
 
@@ -708,7 +707,6 @@
     card.set_record_type(CreditCard::MASKED_SERVER_CARD);
     card.SetNumber(card.LastFourDigits());
     card.SetNetworkForMaskedCard(card.network());
-    card.set_instrument_id(card.instrument_id());
   }
   table->SetServerCreditCards(as_masked_cards);
 
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index f8c3ba0..5ffd0f6 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -404,7 +404,8 @@
   DCHECK(pending_profiles_query_ || pending_server_profiles_query_ ||
          pending_creditcards_query_ || pending_server_creditcards_query_ ||
          pending_server_creditcard_cloud_token_data_query_ ||
-         pending_customer_data_query_ || pending_upi_ids_query_);
+         pending_customer_data_query_ || pending_upi_ids_query_ ||
+         pending_offer_data_query_);
 
   if (!result) {
     // Error from the web database.
@@ -422,6 +423,8 @@
       pending_customer_data_query_ = 0;
     else if (h == pending_upi_ids_query_)
       pending_upi_ids_query_ = 0;
+    else if (h == pending_offer_data_query_)
+      pending_offer_data_query_ = 0;
   } else {
     switch (result->GetType()) {
       case AUTOFILL_PROFILES_RESULT:
@@ -474,6 +477,12 @@
             static_cast<WDResult<std::vector<std::string>>*>(result.get())
                 ->GetValue();
         break;
+      case AUTOFILL_OFFER_DATA:
+        DCHECK_EQ(h, pending_offer_data_query_)
+            << "received autofill offer data from invalid request.";
+        ReceiveLoadedDbValues(h, result.get(), &pending_offer_data_query_,
+                              &autofill_offer_data_);
+        break;
       default:
         NOTREACHED();
     }
@@ -911,6 +920,7 @@
   server_profiles_.clear();
   payments_customer_data_.reset();
   server_credit_card_cloud_token_data_.clear();
+  autofill_offer_data_.clear();
 }
 
 void PersonalDataManager::ClearAllLocalData() {
@@ -1151,12 +1161,25 @@
   return result;
 }
 
+std::vector<AutofillOfferData*> PersonalDataManager::GetCreditCardOffers()
+    const {
+  if (!IsAutofillWalletImportEnabled())
+    return {};
+
+  std::vector<AutofillOfferData*> result;
+  result.reserve(autofill_offer_data_.size());
+  for (const auto& data : autofill_offer_data_)
+    result.push_back(data.get());
+  return result;
+}
+
 void PersonalDataManager::Refresh() {
   LoadProfiles();
   LoadCreditCards();
   LoadCreditCardCloudTokenData();
   LoadPaymentsCustomerData();
   LoadUpiIds();
+  LoadCreditCardOffers();
 }
 
 std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
@@ -1699,6 +1722,16 @@
       database_helper_->GetLocalDatabase()->GetAllUpiIds(this);
 }
 
+void PersonalDataManager::LoadCreditCardOffers() {
+  if (!database_helper_->GetServerDatabase())
+    return;
+
+  CancelPendingServerQuery(&pending_offer_data_query_);
+
+  pending_offer_data_query_ =
+      database_helper_->GetServerDatabase()->GetCreditCardOffers(this);
+}
+
 void PersonalDataManager::CancelPendingLocalQuery(
     WebDataServiceBase::Handle* handle) {
   if (*handle) {
@@ -1728,6 +1761,7 @@
   CancelPendingServerQuery(&pending_server_creditcards_query_);
   CancelPendingServerQuery(&pending_customer_data_query_);
   CancelPendingServerQuery(&pending_server_creditcard_cloud_token_data_query_);
+  CancelPendingServerQuery(&pending_offer_data_query_);
 }
 
 bool PersonalDataManager::HasPendingQueriesForTesting() {
@@ -2591,7 +2625,8 @@
          pending_server_profiles_query_ != 0 ||
          pending_server_creditcards_query_ != 0 ||
          pending_server_creditcard_cloud_token_data_query_ != 0 ||
-         pending_customer_data_query_ != 0 || pending_upi_ids_query_ != 0;
+         pending_customer_data_query_ != 0 || pending_upi_ids_query_ != 0 ||
+         pending_offer_data_query_ != 0;
 }
 
 void PersonalDataManager::MigrateUserOptedInWalletSyncTransportIfNeeded() {
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index 42d68aa..9317043 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -20,6 +20,7 @@
 #include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_profile_validator.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
@@ -261,6 +262,9 @@
   virtual std::vector<CreditCardCloudTokenData*> GetCreditCardCloudTokenData()
       const;
 
+  // Returns the credit card offer data.
+  virtual std::vector<AutofillOfferData*> GetCreditCardOffers() const;
+
   // Updates the validity states of |profiles| according to server validity map.
   void UpdateProfilesServerValidityMapsIfNeeded(
       const std::vector<AutofillProfile*>& profiles);
@@ -541,6 +545,9 @@
   // Loads the saved UPI IDs from the web database.
   virtual void LoadUpiIds();
 
+  // Loads the offer data from the web database.
+  virtual void LoadCreditCardOffers();
+
   // Cancels a pending query to the local web database.  |handle| is a pointer
   // to the query handle.
   void CancelPendingLocalQuery(WebDataServiceBase::Handle* handle);
@@ -606,6 +613,9 @@
   std::vector<std::unique_ptr<CreditCardCloudTokenData>>
       server_credit_card_cloud_token_data_;
 
+  // Offer data for user's credit cards.
+  std::vector<std::unique_ptr<AutofillOfferData>> autofill_offer_data_;
+
   // When the manager makes a request from WebDataServiceBase, the database
   // is queried on another sequence, we record the query handle until we
   // get called back.  We store handles for both profile and credit card queries
@@ -618,6 +628,7 @@
       0;
   WebDataServiceBase::Handle pending_customer_data_query_ = 0;
   WebDataServiceBase::Handle pending_upi_ids_query_ = 0;
+  WebDataServiceBase::Handle pending_offer_data_query_ = 0;
 
   // The observers.
   base::ObserverList<PersonalDataManagerObserver>::Unchecked observers_;
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
index 48dc9b3..badd7f1 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
@@ -103,7 +103,6 @@
       static_cast<CreditCard::Issuer>(card.card_issuer().issuer()));
   if (!card.nickname().empty())
     result.SetNickname(base::UTF8ToUTF16(card.nickname()));
-  result.set_instrument_id(card.instrument_id());
   return result;
 }
 
@@ -248,7 +247,6 @@
     wallet_card->set_nickname(base::UTF16ToUTF8(card.nickname()));
   wallet_card->mutable_card_issuer()->set_issuer(
       static_cast<sync_pb::CardIssuer::Issuer>(card.card_issuer()));
-  wallet_card->set_instrument_id(card.instrument_id());
 }
 
 void SetAutofillWalletSpecificsFromPaymentsCustomerData(
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index 0e069ec..10e5914 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -25,6 +25,7 @@
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
@@ -590,7 +591,8 @@
           InitServerAddressesTable() && InitServerAddressMetadataTable() &&
           InitAutofillSyncMetadataTable() && InitModelTypeStateTable() &&
           InitPaymentsCustomerDataTable() && InitPaymentsUPIVPATable() &&
-          InitServerCreditCardCloudTokenDataTable());
+          InitServerCreditCardCloudTokenDataTable() && InitOfferDataTable() &&
+          InitOfferEligibleInstrumentTable() && InitOfferMerchantDomainTable());
 }
 
 bool AutofillTable::IsSyncable() {
@@ -1432,8 +1434,7 @@
       "metadata.billing_address_id,"  // 10
       "bank_name,"                    // 11
       "nickname,"                     // 12
-      "card_issuer,"                  // 13
-      "instrument_id "                // 14
+      "card_issuer "                  // 13
       "FROM masked_credit_cards masked "
       "LEFT OUTER JOIN unmasked_credit_cards USING (id) "
       "LEFT OUTER JOIN server_card_metadata metadata USING (id)"));
@@ -1479,7 +1480,6 @@
     card->SetNickname(s.ColumnString16(index++));
     card->set_card_issuer(
         static_cast<CreditCard::Issuer>(s.ColumnInt(index++)));
-    card->set_instrument_id(s.ColumnInt64(index++));
     credit_cards->push_back(std::move(card));
   }
   return s.Succeeded();
@@ -1730,18 +1730,17 @@
   // Add all the masked cards.
   sql::Statement masked_insert(
       db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
-                              "id,"             // 0
-                              "network,"        // 1
-                              "status,"         // 2
-                              "name_on_card,"   // 3
-                              "last_four,"      // 4
-                              "exp_month,"      // 5
-                              "exp_year,"       // 6
-                              "bank_name,"      // 7
-                              "nickname,"       // 8
-                              "card_issuer,"    // 9
-                              "instrument_id)"  // 10
-                              "VALUES (?,?,?,?,?,?,?,?,?,?,?)"));
+                              "id,"            // 0
+                              "network,"       // 1
+                              "status,"        // 2
+                              "name_on_card,"  // 3
+                              "last_four,"     // 4
+                              "exp_month,"     // 5
+                              "exp_year,"      // 6
+                              "bank_name,"     // 7
+                              "nickname,"      // 8
+                              "card_issuer)"   // 9
+                              "VALUES (?,?,?,?,?,?,?,?,?,?)"));
   int index;
   for (const CreditCard& card : credit_cards) {
     DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
@@ -1758,7 +1757,6 @@
     masked_insert.BindString(index++, card.bank_name());
     masked_insert.BindString16(index++, card.nickname());
     masked_insert.BindInt(index++, static_cast<int>(card.card_issuer()));
-    masked_insert.BindInt(index++, card.instrument_id());
     masked_insert.Run();
     masked_insert.Reset(true);
   }
@@ -1926,6 +1924,127 @@
   return s.Succeeded();
 }
 
+void AutofillTable::SetCreditCardOffers(
+    const std::vector<AutofillOfferData>& autofill_offer_data) {
+  sql::Transaction transaction(db_);
+  if (!transaction.Begin())
+    return;
+
+  // Delete all old values.
+  sql::Statement delete_offers(
+      db_->GetUniqueStatement("DELETE FROM offer_data"));
+  sql::Statement delete_offer_eligible_instruments(
+      db_->GetUniqueStatement("DELETE FROM offer_eligible_instrument"));
+  sql::Statement delete_offer_merchant_domains(
+      db_->GetUniqueStatement("DELETE FROM offer_merchant_domain"));
+  delete_offers.Run();
+  delete_offer_eligible_instruments.Run();
+  delete_offer_merchant_domains.Run();
+
+  // Insert new values.
+  sql::Statement insert_offers(
+      db_->GetUniqueStatement("INSERT INTO offer_data("
+                              "offer_id, "             // 0
+                              "offer_reward_amount, "  // 1
+                              "expiry, "               // 2
+                              "offer_details_url) "    // 3
+                              "VALUES (?,?,?,?)"));
+
+  for (const AutofillOfferData& data : autofill_offer_data) {
+    insert_offers.BindInt64(0, data.offer_id);
+    insert_offers.BindString(1, data.offer_reward_amount);
+    insert_offers.BindInt64(
+        2, data.expiry.ToDeltaSinceWindowsEpoch().InMilliseconds());
+    insert_offers.BindString(3, data.offer_details_url.spec());
+    insert_offers.Run();
+    insert_offers.Reset(true);
+
+    for (const int64_t instrument_id : data.eligible_instrument_id) {
+      // Insert new offer_eligible_instrument values.
+      sql::Statement insert_offer_eligible_instruments(
+          db_->GetUniqueStatement("INSERT INTO offer_eligible_instrument("
+                                  "offer_id, "       // 0
+                                  "instrument_id) "  // 1
+                                  "VALUES (?,?)"));
+      insert_offer_eligible_instruments.BindInt64(0, data.offer_id);
+      insert_offer_eligible_instruments.BindInt64(1, instrument_id);
+      insert_offer_eligible_instruments.Run();
+      insert_offer_eligible_instruments.Reset(true);
+    }
+
+    for (const GURL& merchant_domain : data.merchant_domain) {
+      // Insert new offer_merchant_domain values.
+      sql::Statement insert_offer_merchant_domains(
+          db_->GetUniqueStatement("INSERT INTO offer_merchant_domain("
+                                  "offer_id, "         // 0
+                                  "merchant_domain) "  // 1
+                                  "VALUES (?,?)"));
+      insert_offer_merchant_domains.BindInt64(0, data.offer_id);
+      insert_offer_merchant_domains.BindString(1, merchant_domain.spec());
+      insert_offer_merchant_domains.Run();
+      insert_offer_merchant_domains.Reset(true);
+    }
+  }
+  transaction.Commit();
+}
+
+bool AutofillTable::GetCreditCardOffers(
+    std::vector<std::unique_ptr<AutofillOfferData>>* autofill_offer_data) {
+  autofill_offer_data->clear();
+
+  sql::Statement s(
+      db_->GetUniqueStatement("SELECT "
+                              "offer_id, "             // 0
+                              "offer_reward_amount, "  // 1
+                              "expiry, "               // 2
+                              "offer_details_url "     // 3
+                              "FROM offer_data"));
+
+  while (s.Step()) {
+    int index = 0;
+    std::unique_ptr<AutofillOfferData> data =
+        std::make_unique<AutofillOfferData>();
+    data->offer_id = s.ColumnInt64(index++);
+    data->offer_reward_amount = s.ColumnString(index++);
+    data->expiry = base::Time::FromDeltaSinceWindowsEpoch(
+        base::TimeDelta::FromMilliseconds(s.ColumnInt64(index++)));
+    data->offer_details_url = GURL(s.ColumnString(index++));
+
+    sql::Statement s_offer_eligible_instrument(
+        db_->GetUniqueStatement("SELECT "
+                                "offer_id, "      // 0
+                                "instrument_id "  // 1
+                                "FROM offer_eligible_instrument "
+                                "WHERE offer_id = ?"));
+    s_offer_eligible_instrument.BindInt64(0, data->offer_id);
+    while (s_offer_eligible_instrument.Step()) {
+      const int64_t instrument_id = s_offer_eligible_instrument.ColumnInt64(1);
+      if (instrument_id != 0) {
+        data->eligible_instrument_id.push_back(instrument_id);
+      }
+    }
+
+    sql::Statement s_offer_merchant_domain(
+        db_->GetUniqueStatement("SELECT "
+                                "offer_id, "        // 0
+                                "merchant_domain "  // 1
+                                "FROM offer_merchant_domain "
+                                "WHERE offer_id = ?"));
+    s_offer_merchant_domain.BindInt64(0, data->offer_id);
+    while (s_offer_merchant_domain.Step()) {
+      const std::string merchant_domain =
+          s_offer_merchant_domain.ColumnString(1);
+      if (!merchant_domain.empty()) {
+        data->merchant_domain.emplace_back(GURL(merchant_domain));
+      }
+    }
+
+    autofill_offer_data->emplace_back(std::move(data));
+  }
+
+  return s.Succeeded();
+}
+
 bool AutofillTable::InsertUpiId(const std::string& upi_id) {
   sql::Transaction transaction(db_);
   if (!transaction.Begin())
@@ -1992,6 +2111,21 @@
   cloud_token_data.Run();
   changed |= db_->GetLastChangeCount() > 0;
 
+  sql::Statement autofill_offer_data(
+      db_->GetUniqueStatement("DELETE FROM offer_data"));
+  autofill_offer_data.Run();
+  changed |= db_->GetLastChangeCount() > 0;
+
+  sql::Statement autofill_offer_eligible_instrument(
+      db_->GetUniqueStatement("DELETE FROM offer_eligible_instrument"));
+  autofill_offer_eligible_instrument.Run();
+  changed |= db_->GetLastChangeCount() > 0;
+
+  sql::Statement autofill_offer_merchant_domain(
+      db_->GetUniqueStatement("DELETE FROM offer_merchant_domain"));
+  autofill_offer_merchant_domain.Run();
+  changed |= db_->GetLastChangeCount() > 0;
+
   transaction.Commit();
   return changed;
 }
@@ -3244,18 +3378,17 @@
   DCHECK_GT(db_->transaction_nesting(), 0);
   sql::Statement masked_insert(
       db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
-                              "id,"             // 0
-                              "network,"        // 1
-                              "status,"         // 2
-                              "name_on_card,"   // 3
-                              "last_four,"      // 4
-                              "exp_month,"      // 5
-                              "exp_year,"       // 6
-                              "bank_name,"      // 7
-                              "nickname,"       // 8
-                              "card_issuer,"    // 9
-                              "instrument_id)"  // 10
-                              "VALUES (?,?,?,?,?,?,?,?,?,?,?)"));
+                              "id,"            // 0
+                              "network,"       // 1
+                              "status,"        // 2
+                              "name_on_card,"  // 3
+                              "last_four,"     // 4
+                              "exp_month,"     // 5
+                              "exp_year,"      // 6
+                              "bank_name,"     // 7
+                              "nickname,"      // 8
+                              "card_issuer)"   // 9
+                              "VALUES (?,?,?,?,?,?,?,?,?,?)"));
   int index;
   for (const CreditCard& card : credit_cards) {
     DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
@@ -3272,7 +3405,6 @@
     masked_insert.BindString(index++, card.bank_name());
     masked_insert.BindString16(index++, card.nickname());
     masked_insert.BindInt(index++, static_cast<int>(card.card_issuer()));
-    masked_insert.BindInt(index++, card.instrument_id());
     masked_insert.Run();
     masked_insert.Reset(true);
 
@@ -3624,4 +3756,43 @@
   return true;
 }
 
+bool AutofillTable::InitOfferDataTable() {
+  if (!db_->DoesTableExist("offer_data")) {
+    if (!db_->Execute("CREATE TABLE offer_data ( "
+                      "offer_id UNSIGNED LONG, "
+                      "offer_reward_amount VARCHAR, "
+                      "expiry UNSIGNED LONG, "
+                      "offer_details_url VARCHAR, "
+                      "merchant_domain VARCHAR)")) {
+      NOTREACHED();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AutofillTable::InitOfferEligibleInstrumentTable() {
+  if (!db_->DoesTableExist("offer_eligible_instrument")) {
+    if (!db_->Execute("CREATE TABLE offer_eligible_instrument ( "
+                      "offer_id UNSIGNED LONG,"
+                      "instrument_id UNSIGNED LONG)")) {
+      NOTREACHED();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AutofillTable::InitOfferMerchantDomainTable() {
+  if (!db_->DoesTableExist("offer_merchant_domain")) {
+    if (!db_->Execute("CREATE TABLE offer_merchant_domain ( "
+                      "offer_id UNSIGNED LONG,"
+                      "merchant_domain VARCHAR)")) {
+      NOTREACHED();
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h
index 3b62eb2..51ae7ecd 100644
--- a/components/autofill/core/browser/webdata/autofill_table.h
+++ b/components/autofill/core/browser/webdata/autofill_table.h
@@ -31,6 +31,7 @@
 class AutofillChange;
 class AutofillEntry;
 struct AutofillMetadata;
+struct AutofillOfferData;
 class AutofillProfile;
 class AutofillTableEncryptor;
 class AutofillTableTest;
@@ -352,6 +353,36 @@
 //                      https://en.wikipedia.org/wiki/Unified_Payments_Interface
 //
 //   vpa_id             A string representing the UPI ID (a.k.a. VPA) value.
+//
+// offer_data           The data for credit card offers which will be presented
+//                      in payments autofill flows.
+//
+//   offer_id           The unique server ID for this offer data.
+//   offer_reward_amount
+//                      The string including the reward details of the offer.
+//                      Could be either percentage cashback (XXX%) or fixed
+//                      amount cashback (XXX$).
+//   expiry             The timestamp when the offer will go expired. Expired
+//                      offers will not be shown in the frontend.
+//   offer_details_url  The link leading to the offer details page on Gpay app.
+//
+// offer_eligible_instrument
+//                      Contains the mapping of credit cards and card linked
+//                      offers.
+//
+//   offer_id           Int 64 to identify the relevant offer. Matches the
+//                      offer_id in the offer_data table.
+//   instrument_id      The new form of instrument id of the card. Will not be
+//                      used for now.
+//
+// offer_merchant_domain
+//                      Contains the mapping of merchant domains and card linked
+//                      offers.
+//
+//   offer_id           Int 64 to identify the relevant offer. Matches the
+//                      offer_id in the offer_data table.
+//   merchant_domain    List of domain names for merchant websites on which
+//                      this offer would apply.
 
 class AutofillTable : public WebDatabaseTable,
                       public syncer::SyncMetadataStore {
@@ -520,6 +551,13 @@
   bool GetPaymentsCustomerData(
       std::unique_ptr<PaymentsCustomerData>* customer_data) const;
 
+  // |autofill_offer_data| must include all existing offers, since table will
+  // be completely overwritten.
+  void SetCreditCardOffers(
+      const std::vector<AutofillOfferData>& autofill_offer_data);
+  bool GetCreditCardOffers(
+      std::vector<std::unique_ptr<AutofillOfferData>>* autofill_offer_data);
+
   // Adds |upi_id| to the saved UPI IDs.
   bool InsertUpiId(const std::string& upi_id);
 
@@ -728,6 +766,9 @@
   bool InitPaymentsCustomerDataTable();
   bool InitPaymentsUPIVPATable();
   bool InitServerCreditCardCloudTokenDataTable();
+  bool InitOfferDataTable();
+  bool InitOfferEligibleInstrumentTable();
+  bool InitOfferMerchantDomainTable();
 
   std::unique_ptr<AutofillTableEncryptor> autofill_table_encryptor_;
 
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index e8d7597c..a0df43ad 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
@@ -138,8 +139,10 @@
 
 class AutofillTableTest : public testing::Test {
  public:
-  AutofillTableTest() {}
-  ~AutofillTableTest() override {}
+  AutofillTableTest() = default;
+  AutofillTableTest(const AutofillTableTest&) = delete;
+  AutofillTableTest& operator=(const AutofillTableTest&) = delete;
+  ~AutofillTableTest() override = default;
 
  protected:
   void SetUp() override {
@@ -160,9 +163,6 @@
   std::unique_ptr<AutofillTable> table_;
   std::unique_ptr<WebDatabase> db_;
   base::test::ScopedFeatureList scoped_feature_list_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AutofillTableTest);
 };
 
 TEST_F(AutofillTableTest, Autofill) {
@@ -2447,7 +2447,6 @@
   inputs[0].SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
   inputs[0].SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020"));
   inputs[0].SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111111111111111"));
-  inputs[0].set_instrument_id(321);
 
   inputs.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
   inputs[1].SetRawInfo(CREDIT_CARD_NAME_FULL, ASCIIToUTF16("Rick Roman"));
@@ -2459,7 +2458,6 @@
   base::string16 nickname = ASCIIToUTF16("Grocery card");
   inputs[1].SetNickname(nickname);
   inputs[1].set_card_issuer(CreditCard::Issuer::GOOGLE);
-  inputs[1].set_instrument_id(123);
 
   test::SetServerCreditCards(table_.get(), inputs);
 
@@ -2490,9 +2488,6 @@
 
   EXPECT_EQ(CreditCard::Issuer::ISSUER_UNKNOWN, outputs[0]->card_issuer());
   EXPECT_EQ(CreditCard::Issuer::GOOGLE, outputs[1]->card_issuer());
-
-  EXPECT_EQ(321, outputs[0]->instrument_id());
-  EXPECT_EQ(123, outputs[1]->instrument_id());
 }
 
 TEST_F(AutofillTableTest, SetGetRemoveServerCardMetadata) {
@@ -2702,7 +2697,6 @@
   inputs[0].SetNetworkForMaskedCard(kVisaCard);
   inputs[0].SetServerStatus(CreditCard::EXPIRED);
   inputs[0].SetNickname(ASCIIToUTF16("Grocery card"));
-  inputs[0].set_instrument_id(1);
   table_->SetServerCardsData(inputs);
 
   // Make sure the card was added correctly.
@@ -3236,8 +3230,10 @@
 
 class GetFormValuesTest : public testing::TestWithParam<GetFormValuesTestCase> {
  public:
-  GetFormValuesTest() {}
-  ~GetFormValuesTest() override {}
+  GetFormValuesTest() = default;
+  GetFormValuesTest(const GetFormValuesTest&) = delete;
+  GetFormValuesTest& operator=(const GetFormValuesTest&) = delete;
+  ~GetFormValuesTest() override = default;
 
  protected:
   void SetUp() override {
@@ -3257,9 +3253,6 @@
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<AutofillTable> table_;
   std::unique_ptr<WebDatabase> db_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(GetFormValuesTest);
 };
 
 TEST_P(GetFormValuesTest, GetFormValuesForElementName_SubstringMatchEnabled) {
@@ -3336,11 +3329,11 @@
     : public AutofillTableTest,
       public testing::WithParamInterface<syncer::ModelType> {
  public:
-  AutofillTableTestPerModelType() {}
-  ~AutofillTableTestPerModelType() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AutofillTableTestPerModelType);
+  AutofillTableTestPerModelType() = default;
+  AutofillTableTestPerModelType(const AutofillTableTestPerModelType&) = delete;
+  AutofillTableTestPerModelType& operator=(
+      const AutofillTableTestPerModelType&) = delete;
+  ~AutofillTableTestPerModelType() override = default;
 };
 
 TEST_P(AutofillTableTestPerModelType, AutofillNoMetadata) {
@@ -3620,4 +3613,101 @@
   ASSERT_THAT(upi_ids, UnorderedElementsAre(upi_id1, upi_id2));
 }
 
+TEST_F(AutofillTableTest, SetAndGetCreditCardOfferData) {
+  // Create test data.
+  AutofillOfferData credit_card_offer_1;
+  AutofillOfferData credit_card_offer_2;
+  AutofillOfferData credit_card_offer_3;
+
+  // Set Offer ID.
+  credit_card_offer_1.offer_id = 1;
+  credit_card_offer_2.offer_id = 2;
+  credit_card_offer_3.offer_id = 3;
+
+  // Set reward amounts.
+  credit_card_offer_1.offer_reward_amount = "$5";
+  credit_card_offer_2.offer_reward_amount = "10%";
+  credit_card_offer_3.offer_reward_amount = "5%";
+
+  // Set expiry.
+  credit_card_offer_1.expiry = base::Time::FromDoubleT(1000);
+  credit_card_offer_2.expiry = base::Time::FromDoubleT(2000);
+  credit_card_offer_3.expiry = base::Time::FromDoubleT(3000);
+
+  // Set details URL.
+  credit_card_offer_1.offer_details_url =
+      GURL("https://www.offer_1_example.com/");
+  credit_card_offer_2.offer_details_url =
+      GURL("https://www.offer_2_example.com/");
+  credit_card_offer_3.offer_details_url =
+      GURL("https://www.offer_3_example.com/");
+
+  // Set merchant domains for offer 1.
+  credit_card_offer_1.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_1_1.com/"));
+  credit_card_offer_1.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_1_2.com/"));
+  credit_card_offer_1.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_1_3.com/"));
+  // Set merchant domains for offer 2.
+  credit_card_offer_2.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_2_1.com/"));
+  // Set merchant domains for offer 3.
+  credit_card_offer_3.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_3_1.com/"));
+  credit_card_offer_3.merchant_domain.emplace_back(
+      GURL("https://www.merchant_domain_3_2.com/"));
+
+  // Set eligible instrument ID for offer 1.
+  credit_card_offer_1.eligible_instrument_id.push_back(10);
+  credit_card_offer_1.eligible_instrument_id.push_back(11);
+  // Set eligible instrument ID for offer 2.
+  credit_card_offer_2.eligible_instrument_id.push_back(20);
+  credit_card_offer_2.eligible_instrument_id.push_back(21);
+  credit_card_offer_2.eligible_instrument_id.push_back(22);
+  // Set eligible instrument ID for offer 3.
+  credit_card_offer_3.eligible_instrument_id.push_back(30);
+
+  // Create vector of offer data.
+  std::vector<AutofillOfferData> autofill_offer_data;
+  autofill_offer_data.push_back(credit_card_offer_1);
+  autofill_offer_data.push_back(credit_card_offer_2);
+  autofill_offer_data.push_back(credit_card_offer_3);
+
+  table_->SetCreditCardOffers(autofill_offer_data);
+
+  std::vector<std::unique_ptr<AutofillOfferData>> output_offer_data;
+
+  EXPECT_TRUE(table_->GetCreditCardOffers(&output_offer_data));
+  EXPECT_EQ(autofill_offer_data.size(), output_offer_data.size());
+
+  for (const auto& data : autofill_offer_data) {
+    // Find output data with corresponding Offer ID.
+    size_t output_index = 0;
+    while (output_index < output_offer_data.size()) {
+      if (data.offer_id == output_offer_data[output_index]->offer_id) {
+        break;
+      }
+      output_index++;
+    }
+
+    // Expect to find matching Offer ID's.
+    EXPECT_NE(output_index, output_offer_data.size());
+
+    // All corresponding fields must be equal.
+    EXPECT_EQ(data.offer_id, output_offer_data[output_index]->offer_id);
+    EXPECT_EQ(data.offer_reward_amount,
+              output_offer_data[output_index]->offer_reward_amount);
+    EXPECT_EQ(data.expiry, output_offer_data[output_index]->expiry);
+    EXPECT_EQ(data.offer_details_url.spec(),
+              output_offer_data[output_index]->offer_details_url.spec());
+    ASSERT_THAT(data.merchant_domain,
+                testing::UnorderedElementsAreArray(
+                    output_offer_data[output_index]->merchant_domain));
+    ASSERT_THAT(data.eligible_instrument_id,
+                testing::UnorderedElementsAreArray(
+                    output_offer_data[output_index]->eligible_instrument_id));
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
index 0af9de6e..3d4b6c1f 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
@@ -882,7 +882,6 @@
   EXPECT_EQ(card.billing_address_id(), cards[0]->billing_address_id());
   EXPECT_EQ(card.nickname(), cards[0]->nickname());
   EXPECT_EQ(card.card_issuer(), cards[0]->card_issuer());
-  EXPECT_EQ(card.instrument_id(), cards[0]->instrument_id());
 
   // Also make sure that those types are not empty, to exercice all the code
   // paths.
@@ -891,7 +890,6 @@
   EXPECT_NE(0, card.expiration_month());
   EXPECT_NE(0, card.expiration_year());
   EXPECT_FALSE(card.nickname().empty());
-  EXPECT_NE(0, card.instrument_id());
 }
 
 // Test that all field values for a cloud token data sent from the server are
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index 59d3423..24f183c 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -9,6 +9,7 @@
 #include "base/location.h"
 #include "base/notreached.h"
 #include "base/single_thread_task_runner.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
@@ -567,6 +568,16 @@
       AUTOFILL_CLOUDTOKEN_RESULT, std::move(cloud_token_data));
 }
 
+std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetCreditCardOffers(
+    WebDatabase* db) {
+  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
+  std::vector<std::unique_ptr<AutofillOfferData>> offers;
+  AutofillTable::FromWebDatabase(db)->GetCreditCardOffers(&offers);
+  return std::make_unique<
+      WDResult<std::vector<std::unique_ptr<AutofillOfferData>>>>(
+      AUTOFILL_OFFER_DATA, std::move(offers));
+}
+
 WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
     WebDatabase* db) {
   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
index e2e3c00..5525d4a 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -33,7 +33,7 @@
 class AutofillWebDataServiceObserverOnDBSequence;
 class CreditCard;
 
-// Backend implentation for the AutofillWebDataService. This class runs on the
+// Backend implementation for the AutofillWebDataService. This class runs on the
 // DB sequence, as it handles reads and writes to the WebDatabase, and functions
 // in it should only be called from that sequence. Most functions here are just
 // the implementations of the corresponding functions in the Autofill
@@ -198,6 +198,9 @@
   // Returns the CreditCardCloudTokenData from the database.
   std::unique_ptr<WDTypedResult> GetCreditCardCloudTokenData(WebDatabase* db);
 
+  // Returns Credit Card Offers Data from the database.
+  std::unique_ptr<WDTypedResult> GetCreditCardOffers(WebDatabase* db);
+
   WebDatabase::State ClearAllServerData(WebDatabase* db);
   WebDatabase::State ClearAllLocalData(WebDatabase* db);
 
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
index 9168b76..6487f6b 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -10,6 +10,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
@@ -276,6 +277,15 @@
       consumer);
 }
 
+WebDataServiceBase::Handle AutofillWebDataService::GetCreditCardOffers(
+    WebDataServiceConsumer* consumer) {
+  return wdbs_->ScheduleDBTaskWithResult(
+      FROM_HERE,
+      base::BindOnce(&AutofillWebDataBackendImpl::GetCreditCardOffers,
+                     autofill_backend_),
+      consumer);
+}
+
 void AutofillWebDataService::ClearAllServerData() {
   wdbs_->ScheduleDBTask(
       FROM_HERE, base::BindOnce(&AutofillWebDataBackendImpl::ClearAllServerData,
@@ -383,8 +393,7 @@
       consumer);
 }
 
-AutofillWebDataService::~AutofillWebDataService() {
-}
+AutofillWebDataService::~AutofillWebDataService() = default;
 
 void AutofillWebDataService::NotifyAutofillMultipleChangedOnUISequence() {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.h b/components/autofill/core/browser/webdata/autofill_webdata_service.h
index d71da82..9165888 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -159,6 +159,13 @@
   WebDataServiceBase::Handle GetCreditCardCloudTokenData(
       WebDataServiceConsumer* consumer);
 
+  // Initiates the request for credit card offer data. The method
+  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+  // finished, with the offer data included in the argument |result|. The
+  // consumer owns the data.
+  WebDataServiceBase::Handle GetCreditCardOffers(
+      WebDataServiceConsumer* consumer);
+
   void ClearAllServerData();
   void ClearAllLocalData();
 
diff --git a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 973e35e..dc9c2d0 100644
--- a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -64,6 +64,9 @@
       subresource_filter::ActivationDecision* decision) override {
     return initial_activation_level;
   }
+  void OnAdsViolationTriggered(
+      content::RenderFrameHost*,
+      subresource_filter::mojom::AdsViolation) override {}
 
   // content::RenderViewHostTestHarness:
   void SetUp() override {
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 61942d7..9c1ceb1 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -134,7 +134,7 @@
   DCHECK(sync_client_);
 }
 
-ProfileSyncComponentsFactoryImpl::~ProfileSyncComponentsFactoryImpl() {}
+ProfileSyncComponentsFactoryImpl::~ProfileSyncComponentsFactoryImpl() = default;
 
 syncer::DataTypeController::TypeVector
 ProfileSyncComponentsFactoryImpl::CreateCommonDataTypeControllers(
diff --git a/components/browser_sync/profile_sync_components_factory_impl.h b/components/browser_sync/profile_sync_components_factory_impl.h
index 054f328..01909fd0 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/components/browser_sync/profile_sync_components_factory_impl.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/compiler_specific.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "components/sync/base/model_type.h"
@@ -57,6 +56,10 @@
       const scoped_refptr<password_manager::PasswordStore>&
           account_password_store,
       sync_bookmarks::BookmarkSyncService* bookmark_sync_service);
+  ProfileSyncComponentsFactoryImpl(const ProfileSyncComponentsFactoryImpl&) =
+      delete;
+  ProfileSyncComponentsFactoryImpl& operator=(
+      const ProfileSyncComponentsFactoryImpl&) = delete;
   ~ProfileSyncComponentsFactoryImpl() override;
 
   // Creates and returns enabled datatypes and their controllers.
@@ -124,8 +127,6 @@
   const scoped_refptr<password_manager::PasswordStore> profile_password_store_;
   const scoped_refptr<password_manager::PasswordStore> account_password_store_;
   sync_bookmarks::BookmarkSyncService* const bookmark_sync_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfileSyncComponentsFactoryImpl);
 };
 
 }  // namespace browser_sync
diff --git a/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
index e7510d27..efcf562 100644
--- a/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
+++ b/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -23,6 +23,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Log;
 import org.chromium.base.MathUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode;
@@ -44,6 +45,8 @@
  */
 class BottomSheet extends FrameLayout
         implements BottomSheetSwipeDetector.SwipeableBottomSheet, View.OnLayoutChangeListener {
+    private static final String TAG = "BottomSheet";
+
     /**
      * The base duration of the settling animation of the sheet. 218 ms is a spec for material
      * design (this is the minimum time a user is guaranteed to pay attention to something).
@@ -217,6 +220,9 @@
      * Called when the activity containing the {@link BottomSheet} is destroyed.
      */
     void destroy() {
+        Log.i(TAG,
+                "Sheet destroyed: state: " + mCurrentState
+                        + ", content null: " + (getCurrentSheetContent() == null));
         mIsDestroyed = true;
         mIsTouchEnabled = false;
         mObservers.clear();
@@ -496,6 +502,11 @@
         // If the desired content is already showing, do nothing.
         if (mSheetContent == content) return;
 
+        Log.i(TAG,
+                "Setting sheet content: state: " + mCurrentState
+                        + ", content null: " + (content == null));
+        if (content == null) Thread.dumpStack();
+
         // Remove this as listener from previous content layout and size changes.
         if (mSheetContent != null) {
             mSheetContent.setContentSizeListener(null);
@@ -601,6 +612,9 @@
                 if (mIsDestroyed) return;
 
                 mSettleAnimator = null;
+                Log.i(TAG,
+                        "Ending settle animation: target: " + targetState
+                                + ", content null: " + (getCurrentSheetContent() == null));
                 setInternalCurrentState(targetState, reason);
                 mTargetState = SheetState.NONE;
             }
@@ -616,6 +630,10 @@
             }
         });
 
+        Log.i(TAG,
+                "Starting settle animation: target: " + targetState
+                        + ", content null: " + (getCurrentSheetContent() == null));
+
         setInternalCurrentState(SheetState.SCROLLING, reason);
         mSettleAnimator.start();
     }
@@ -864,6 +882,10 @@
     void setSheetState(@SheetState int state, boolean animate, @StateChangeReason int reason) {
         assert state != SheetState.NONE;
 
+        Log.i(TAG,
+                "Setting sheet state: state: " + mCurrentState
+                        + ", content null: " + (getCurrentSheetContent() == null));
+
         // Setting state to SCROLLING is not a valid operation. This can happen only when
         // we're already in the scrolling state. Make it no-op.
         if (state == SheetState.SCROLLING) {
@@ -918,6 +940,10 @@
     private void setInternalCurrentState(@SheetState int state, @StateChangeReason int reason) {
         if (state == mCurrentState) return;
 
+        if (getCurrentSheetContent() == null && state != SheetState.HIDDEN) {
+            Log.i(TAG, "Content null while open!");
+        }
+
         // TODO(mdjones): This shouldn't be able to happen, but does occasionally during layout.
         //                Fix the race condition that is making this happen.
         if (state == SheetState.NONE) {
diff --git a/components/content_capture/browser/content_capture_receiver_test.cc b/components/content_capture/browser/content_capture_receiver_test.cc
index cefc77bf..298fc00 100644
--- a/components/content_capture/browser/content_capture_receiver_test.cc
+++ b/components/content_capture/browser/content_capture_receiver_test.cc
@@ -47,8 +47,7 @@
 
   mojo::PendingAssociatedReceiver<mojom::ContentCaptureReceiver>
   GetPendingAssociatedReceiver() {
-    return content_capture_receiver_
-        .BindNewEndpointAndPassDedicatedReceiverForTesting();
+    return content_capture_receiver_.BindNewEndpointAndPassDedicatedReceiver();
   }
 
  private:
diff --git a/components/crash/content/browser/error_reporting/BUILD.gn b/components/crash/content/browser/error_reporting/BUILD.gn
new file mode 100644
index 0000000..1f31c8c
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/BUILD.gn
@@ -0,0 +1,60 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Error reporting is not enabled on Fuchsia, so there is no consent checking
+# function. TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
+assert(!is_fuchsia)
+
+# Javascript Error reporting is not currently used on iOS
+assert(!is_ios)
+
+static_library("error_reporting") {
+  sources = [
+    "javascript_error_report.cc",
+    "javascript_error_report.h",
+    "send_javascript_error_report.cc",
+    "send_javascript_error_report.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/crash/core/app",
+    "//components/feedback",
+    "//content/public/browser",
+    "//net",
+    "//services/network:network_service",
+    "//services/network/public/cpp",
+  ]
+}
+
+source_set("mock_crash_endpoint") {
+  testonly = true
+  sources = [
+    "mock_crash_endpoint.cc",
+    "mock_crash_endpoint.h",
+  ]
+  deps = [
+    ":error_reporting",
+    "//base",
+    "//components/crash/core/app",
+    "//net",
+    "//net:test_support",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "send_javascript_error_report_unittest.cc" ]
+  deps = [
+    ":error_reporting",
+    ":mock_crash_endpoint",
+    "//base",
+    "//components/crash/core/app",
+    "//content/test:test_support",
+    "//net:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/crash/content/browser/error_reporting/DEPS b/components/crash/content/browser/error_reporting/DEPS
new file mode 100644
index 0000000..3d71d06
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+components/feedback",
+  "+net",
+  "+services/network",
+]
diff --git a/components/crash/content/browser/error_reporting/OWNERS b/components/crash/content/browser/error_reporting/OWNERS
new file mode 100644
index 0000000..986c32b
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/OWNERS
@@ -0,0 +1,4 @@
+iby@chromium.org
+mutexlox@chromium.org
+
+# COMPONENT: Internals>CrashReporting
diff --git a/components/crash/content/browser/error_reporting/javascript_error_report.cc b/components/crash/content/browser/error_reporting/javascript_error_report.cc
new file mode 100644
index 0000000..8614f47
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/javascript_error_report.cc
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/browser/error_reporting/javascript_error_report.h"
+
+JavaScriptErrorReport::JavaScriptErrorReport() = default;
+JavaScriptErrorReport::JavaScriptErrorReport(const JavaScriptErrorReport& rhs) =
+    default;
+JavaScriptErrorReport::JavaScriptErrorReport(
+    JavaScriptErrorReport&& rhs) noexcept = default;
+JavaScriptErrorReport::~JavaScriptErrorReport() = default;
+JavaScriptErrorReport& JavaScriptErrorReport::operator=(
+    const JavaScriptErrorReport& rhs) = default;
+JavaScriptErrorReport& JavaScriptErrorReport::operator=(
+    JavaScriptErrorReport&& rhs) noexcept = default;
diff --git a/components/crash/content/browser/error_reporting/javascript_error_report.h b/components/crash/content/browser/error_reporting/javascript_error_report.h
new file mode 100644
index 0000000..49f1bed
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/javascript_error_report.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_JAVASCRIPT_ERROR_REPORT_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_JAVASCRIPT_ERROR_REPORT_H_
+
+#include <string>
+
+#include "base/optional.h"
+
+// A report about a JavaScript error that we might want to send back to Google
+// so it can be fixed. Fill in the fields and then call
+// SendJavaScriptErrorReport.
+struct JavaScriptErrorReport {
+  JavaScriptErrorReport();
+  JavaScriptErrorReport(const JavaScriptErrorReport& rhs);
+  JavaScriptErrorReport(JavaScriptErrorReport&& rhs) noexcept;
+  ~JavaScriptErrorReport();
+  JavaScriptErrorReport& operator=(const JavaScriptErrorReport& rhs);
+  JavaScriptErrorReport& operator=(JavaScriptErrorReport&& rhs) noexcept;
+
+  // The error message. Must be present
+  std::string message;
+
+  // URL where the error occurred. Must be the full URL, containing the protocol
+  // (e.g. http://www.example.com).
+  std::string url;
+
+  // Name of the product where the error occurred. If empty, use the product
+  // variant of Chrome that is hosting the extension. (e.g. "Chrome" or
+  // "Chrome_ChromeOS").
+  std::string product;
+
+  // Version of the product where the error occurred. If empty, use the version
+  // of Chrome that is hosting the extension (e.g. "73.0.3683.75").
+  std::string version;
+
+  // Line number where the error occurred. Not sent if not present.
+  base::Optional<int> line_number;
+
+  // Column number where the error occurred. Not sent if not present.
+  base::Optional<int> column_number;
+
+  // String containing the stack trace for the error. Not sent if not present.
+  base::Optional<std::string> stack_trace;
+};
+
+#endif  // COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_JAVASCRIPT_ERROR_REPORT_H_
diff --git a/extensions/browser/api/crash_report_private/mock_crash_endpoint.cc b/components/crash/content/browser/error_reporting/mock_crash_endpoint.cc
similarity index 72%
rename from extensions/browser/api/crash_report_private/mock_crash_endpoint.cc
rename to components/crash/content/browser/error_reporting/mock_crash_endpoint.cc
index 9a23cc3..2e8aae4 100644
--- a/extensions/browser/api/crash_report_private/mock_crash_endpoint.cc
+++ b/components/crash/content/browser/error_reporting/mock_crash_endpoint.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/browser/api/crash_report_private/mock_crash_endpoint.h"
+#include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
 
 #include "base/run_loop.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "build/build_config.h"
+#include "components/crash/content/browser/error_reporting/send_javascript_error_report.h"
 #include "components/crash/core/app/crash_reporter_client.h"
-#include "extensions/browser/api/crash_report_private/crash_report_private_api.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"
@@ -30,27 +31,31 @@
         FROM_HERE, base::BlockingType::MAY_BLOCK);
     return owner_->consented_;
   }
+#if defined(OS_POSIX) && !defined(OS_MAC)
   void GetProductNameAndVersion(std::string* product_name,
                                 std::string* version,
                                 std::string* channel) override {
-    *product_name = "Chrome (Chrome OS)";
+    *product_name = "Chrome_ChromeOS";
     *version = "1.2.3.4";
     *channel = "Stable";
   }
-
+#endif
  private:
   MockCrashEndpoint* owner_;
 };
 
+MockCrashEndpoint::Report::Report(std::string query_value,
+                                  std::string content_value)
+    : query(std::move(query_value)), content(std::move(content_value)) {}
+
 MockCrashEndpoint::MockCrashEndpoint(
     net::test_server::EmbeddedTestServer* test_server)
     : test_server_(test_server) {
-  test_server->RegisterRequestHandler(
-      base::Bind(&MockCrashEndpoint::HandleRequest, base::Unretained(this)));
+  test_server->RegisterRequestHandler(base::BindRepeating(
+      &MockCrashEndpoint::HandleRequest, base::Unretained(this)));
   EXPECT_TRUE(test_server->Start());
 
-  extensions::api::SetCrashEndpointForTesting(
-      test_server->GetURL(kTestCrashEndpoint).spec());
+  SetCrashEndpointForTesting(test_server->GetURL(kTestCrashEndpoint).spec());
   client_ = std::make_unique<Client>(this);
   crash_reporter::SetCrashReporterClient(client_.get());
 }
@@ -59,15 +64,15 @@
   crash_reporter::SetCrashReporterClient(nullptr);
 }
 
-const MockCrashEndpoint::Report MockCrashEndpoint::WaitForReport() {
-  if (!last_report_.query.empty()) {
-    return std::move(last_report_);
+MockCrashEndpoint::Report MockCrashEndpoint::WaitForReport() {
+  if (last_report_) {
+    return *last_report_;
   }
   base::RunLoop run_loop;
   on_report_ = run_loop.QuitClosure();
   run_loop.Run();
   on_report_.Reset();
-  return std::move(last_report_);
+  return *last_report_;
 }
 
 std::unique_ptr<net::test_server::HttpResponse>
@@ -77,7 +82,7 @@
     return nullptr;
   }
 
-  last_report_ = {absolute_url.query(), request.content};
+  last_report_ = Report(absolute_url.query(), request.content);
   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
   http_response->set_code(net::HTTP_OK);
   http_response->set_content("123");
diff --git a/extensions/browser/api/crash_report_private/mock_crash_endpoint.h b/components/crash/content/browser/error_reporting/mock_crash_endpoint.h
similarity index 71%
rename from extensions/browser/api/crash_report_private/mock_crash_endpoint.h
rename to components/crash/content/browser/error_reporting/mock_crash_endpoint.h
index 029f219..53b1184 100644
--- a/extensions/browser/api/crash_report_private/mock_crash_endpoint.h
+++ b/components/crash/content/browser/error_reporting/mock_crash_endpoint.h
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_MOCK_CRASH_ENDPOINT_H_
-#define EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_MOCK_CRASH_ENDPOINT_H_
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_MOCK_CRASH_ENDPOINT_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_MOCK_CRASH_ENDPOINT_H_
 
 #include <memory>
 #include <string>
 
 #include "base/callback.h"
+#include "base/optional.h"
+#include "url/gurl.h"
 
 namespace net {
 namespace test_server {
@@ -22,6 +24,7 @@
 class MockCrashEndpoint {
  public:
   struct Report {
+    Report(std::string query_value, std::string content_value);
     std::string query;
     std::string content;
   };
@@ -32,9 +35,10 @@
   ~MockCrashEndpoint();
 
   // Returns the last received report, or waits for a query and returns it.
-  const Report WaitForReport();
+  Report WaitForReport();
 
-  const Report& last_report() const { return last_report_; }
+  // Returns the last report received, if any.
+  const base::Optional<Report>& last_report() const { return last_report_; }
 
   // Configures whether the mock crash reporter client has user-consent for
   // submitting crash reports.
@@ -48,9 +52,9 @@
 
   net::test_server::EmbeddedTestServer* test_server_;
   std::unique_ptr<Client> client_;
-  Report last_report_;
+  base::Optional<Report> last_report_;
   bool consented_ = true;
   base::RepeatingClosure on_report_;
 };
 
-#endif  // EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_MOCK_CRASH_ENDPOINT_H_
+#endif  // COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_MOCK_CRASH_ENDPOINT_H_
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report.cc b/components/crash/content/browser/error_reporting/send_javascript_error_report.cc
new file mode 100644
index 0000000..d39c58a
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/send_javascript_error_report.cc
@@ -0,0 +1,327 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/browser/error_reporting/send_javascript_error_report.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "build/build_config.h"
+#include "components/crash/content/browser/error_reporting/javascript_error_report.h"
+#include "components/crash/core/app/client_upload_info.h"
+#include "components/feedback/redaction_tool.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/base/escape.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace {
+
+#if defined(GOOGLE_CHROME_BUILD)
+constexpr char kCrashEndpointUrl[] = "https://clients2.google.com/cr/report";
+#else
+constexpr char kCrashEndpointUrl[] = "";
+#endif
+
+constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024;
+
+std::string& GetCrashEndpoint() {
+  static base::NoDestructor<std::string> crash_endpoint(kCrashEndpointUrl);
+  return *crash_endpoint;
+}
+
+struct OsVersionOverride {
+  OsVersionOverride(int32_t major_override,
+                    int32_t minor_override,
+                    int32_t bugfix_override)
+      : major(major_override), minor(minor_override), bugfix(bugfix_override) {}
+  int32_t major;
+  int32_t minor;
+  int32_t bugfix;
+};
+
+// If return value is set, use that as the major/minor/bugfix OS version
+// numbers. This is used as dependency injection during testing.
+base::Optional<OsVersionOverride>& GetOsVersionOverrides() {
+  static base::NoDestructor<base::Optional<OsVersionOverride>> testing_override;
+  return *testing_override;
+}
+
+void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
+                       base::ScopedClosureRunner callback_runner,
+                       std::unique_ptr<std::string> response_body) {
+  if (response_body) {
+    // TODO(iby): Update the crash log (uploads.log)
+    DVLOG(1) << "Uploaded crash report. ID: " << *response_body;
+  } else {
+    LOG(ERROR) << "Failed to upload crash report";
+  }
+  // callback_runner will implicitly run the callback when we reach this line.
+}
+
+// Sometimes, the stack trace will contain an error message as the first line,
+// which confuses the Crash server. This function deletes it if it is present.
+void RemoveErrorMessageFromStackTrace(const std::string& error_message,
+                                      std::string& stack_trace) {
+  // Keep the original stack trace if the error message is not present.
+  const auto error_message_index = stack_trace.find(error_message);
+  if (error_message_index == std::string::npos) {
+    return;
+  }
+
+  // If the stack trace only contains one line, then delete the whole trace.
+  const auto first_line_end_index = stack_trace.find('\n');
+  if (first_line_end_index == std::string::npos) {
+    stack_trace.clear();
+    return;
+  }
+
+  // Otherwise, delete the first line.
+  stack_trace = stack_trace.substr(first_line_end_index + 1);
+}
+
+std::string RedactErrorMessage(const std::string& message) {
+  return feedback::RedactionTool(/*first_party_extension_ids=*/nullptr)
+      .Redact(message);
+}
+
+// Returns the redacted, fixed-up error report if the user consented to have it
+// sent. Returns base::nullopt if the user did not consent or we otherwise
+// should not send the report. All the MayBlock work should be done in here.
+base::Optional<JavaScriptErrorReport> CheckConsentAndRedact(
+    JavaScriptErrorReport error_report) {
+  if (!crash_reporter::GetClientCollectStatsConsent()) {
+    return base::nullopt;
+  }
+
+  // Remove error message from stack trace before redaction, since redaction
+  // might change the error message enough that we don't find it.
+  if (error_report.stack_trace) {
+    RemoveErrorMessageFromStackTrace(error_report.message,
+                                     *error_report.stack_trace);
+  }
+
+  error_report.message = RedactErrorMessage(error_report.message);
+  // TODO(https://crbug.com/1121816): Also redact stack trace, but don't
+  // completely remove the URL (only query & fragment).
+  return error_report;
+}
+
+using ParameterMap = std::map<std::string, std::string>;
+
+std::string BuildPostRequestQueryString(const ParameterMap& params) {
+  std::vector<std::string> query_parts;
+  for (const auto& kv : params) {
+    query_parts.push_back(base::StrCat(
+        {kv.first, "=",
+         net::EscapeQueryParamValue(kv.second, /*use_plus=*/false)}));
+  }
+  return base::JoinString(query_parts, "&");
+}
+
+struct PlatformInfo {
+  std::string product_name;
+  std::string version;
+  std::string channel;
+  std::string os_version;
+};
+
+PlatformInfo GetPlatformInfo() {
+  PlatformInfo info;
+
+  // TODO(https://crbug.com/1121816): Get correct product_name for non-POSIX
+  // platforms.
+#if defined(OS_POSIX) && !defined(OS_APPLE)
+  crash_reporter::GetClientProductNameAndVersion(&info.product_name,
+                                                 &info.version, &info.channel);
+#endif
+  int32_t os_major_version = 0;
+  int32_t os_minor_version = 0;
+  int32_t os_bugfix_version = 0;
+  const base::Optional<OsVersionOverride>& version_override =
+      GetOsVersionOverrides();
+  if (version_override) {
+    os_major_version = version_override->major;
+    os_minor_version = version_override->minor;
+    os_bugfix_version = version_override->bugfix;
+  } else {
+    base::SysInfo::OperatingSystemVersionNumbers(
+        &os_major_version, &os_minor_version, &os_bugfix_version);
+  }
+
+  info.os_version = base::StringPrintf("%d.%d.%d", os_major_version,
+                                       os_minor_version, os_bugfix_version);
+  return info;
+}
+
+void SendReport(const GURL& url,
+                const std::string& body,
+                base::ScopedClosureRunner callback_runner,
+                network::SharedURLLoaderFactory* loader_factory) {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "POST";
+  resource_request->url = url;
+
+  const auto traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("javascript_report_error", R"(
+      semantics {
+        sender: "JavaScript error reporter"
+        description:
+          "Chrome can send JavaScript errors that occur within built-in "
+          "component extensions. If enabled, the error message, along "
+          "with information about Chrome and the operating system, is sent to "
+          "Google."
+        trigger:
+          "A JavaScript error occurs in a Chrome component extension (an "
+          "extension bundled with the Chrome browser, not downloaded "
+          "separately)."
+        data:
+          "The JavaScript error message, the version and channel of Chrome, "
+          "the URL of the extension, the line and column number where the "
+          "error occurred, and a stack trace of the error."
+        destination: GOOGLE_OWNED_SERVICE
+      }
+      policy {
+        cookies_allowed: NO
+        setting:
+          "You can enable or disable this feature via 'Automatically send "
+          "usage statistics and crash reports to Google' in Chromium's "
+          "settings under Advanced, Privacy. (This is in System Settings on "
+          "Chromebooks.) This feature is enabled by default."
+        chrome_policy {
+          MetricsReportingEnabled {
+            policy_options {mode: MANDATORY}
+            MetricsReportingEnabled: false
+          }
+        }
+      })");
+
+  DVLOG(1) << "Sending crash report: " << resource_request->url;
+
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+
+  if (!body.empty()) {
+    url_loader->AttachStringForUpload(body, "text/plain");
+  }
+
+  network::SimpleURLLoader* loader = url_loader.get();
+  loader->DownloadToString(
+      loader_factory,
+      base::BindOnce(&OnRequestComplete, std::move(url_loader),
+                     std::move(callback_runner)),
+      kCrashEndpointResponseMaxSizeInBytes);
+}
+
+// Finishes sending process once the MayBlock processing is done. On UI thread.
+void OnConsentCheckCompleted(
+    base::ScopedClosureRunner callback_runner,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+    base::Optional<JavaScriptErrorReport> error_report) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!error_report) {
+    // User didn't consent. This isn't an error so don't log an error.
+    return;
+  }
+
+  std::string& crash_endpoint_string = GetCrashEndpoint();
+  if (crash_endpoint_string.empty()) {
+    LOG(WARNING) << "Not sending error reports to Google for browsers that are "
+                    "not Google Chrome";
+    return;
+  }
+
+  // TODO(https://crbug.com/986166): Use crash_reporter for Chrome OS.
+  const auto platform = GetPlatformInfo();
+
+  const GURL source(error_report->url);
+  const auto product = error_report->product.empty() ? platform.product_name
+                                                     : error_report->product;
+  const auto version =
+      error_report->version.empty() ? platform.version : error_report->version;
+
+  ParameterMap params;
+  params["prod"] = net::EscapeQueryParamValue(product, /*use_plus=*/false);
+  params["ver"] = net::EscapeQueryParamValue(version, /*use_plus=*/false);
+  params["type"] = "JavascriptError";
+  params["error_message"] = error_report->message;
+  params["browser"] = "Chrome";
+  params["browser_version"] = platform.version;
+  params["channel"] = platform.channel;
+  // TODO(https://crbug.com/1121816): Handle non-ChromeOS platforms.
+  params["os"] = "ChromeOS";
+  params["os_version"] = platform.os_version;
+  params["full_url"] = source.spec();
+  params["url"] = source.path();
+  params["src"] = source.spec();
+  if (error_report->line_number)
+    params["line"] = base::NumberToString(*error_report->line_number);
+  if (error_report->column_number)
+    params["column"] = base::NumberToString(*error_report->column_number);
+
+  const GURL url(base::StrCat(
+      {crash_endpoint_string, "?", BuildPostRequestQueryString(params)}));
+  std::string body;
+  if (error_report->stack_trace) {
+    body = std::move(*error_report->stack_trace);
+  }
+
+  SendReport(url, body, std::move(callback_runner), loader_factory.get());
+}
+
+}  // namespace
+
+void SendJavaScriptErrorReport(JavaScriptErrorReport error_report,
+                               base::OnceClosure completion_callback,
+                               content::BrowserContext* browser_context) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  base::ScopedClosureRunner callback_runner(std::move(completion_callback));
+
+  // loader_factory must be created on UI thread. Get it now while we still
+  // know the browser_context pointer is valid.
+  scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
+      content::BrowserContext::GetDefaultStoragePartition(browser_context)
+          ->GetURLLoaderFactoryForBrowserProcess();
+
+  // Consent check needs to be done on a blockable thread. We must return to
+  // this thread (the UI thread) to use the loader_factory.
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&CheckConsentAndRedact, std::move(error_report)),
+      base::BindOnce(&OnConsentCheckCompleted, std::move(callback_runner),
+                     std::move(loader_factory)));
+}
+
+void SetCrashEndpointForTesting(const std::string& endpoint) {
+  GetCrashEndpoint() = endpoint;
+}
+
+// The weird "{" comment is to get the
+// CheckNoProductionCodeUsingTestOnlyFunctions PRESUBMIT to be quiet.
+void SetOsVersionForTesting(int32_t os_major_version,  // {
+                            int32_t os_minor_version,
+                            int32_t os_bugfix_version) {
+  GetOsVersionOverrides().emplace(os_major_version, os_minor_version,
+                                  os_bugfix_version);
+}
+
+void ClearOsVersionTestingOverride() {
+  GetOsVersionOverrides().reset();
+}
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report.h b/components/crash/content/browser/error_reporting/send_javascript_error_report.h
new file mode 100644
index 0000000..9eacc65
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/send_javascript_error_report.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_SEND_JAVASCRIPT_ERROR_REPORT_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_SEND_JAVASCRIPT_ERROR_REPORT_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace content {
+class BrowserContext;
+}
+struct JavaScriptErrorReport;
+
+// Sends a report of an error in JavaScript (such as an unhandled exception) to
+// Google's error collection service. This should be called on the UI thread;
+// it will return after the report sending is started. |completion_callback| is
+// called when the report send completes or fails.
+void SendJavaScriptErrorReport(JavaScriptErrorReport error_report,
+                               base::OnceClosure completion_callback,
+                               content::BrowserContext* browser_context);
+
+// Override the URL we send the crashes to.
+void SetCrashEndpointForTesting(const std::string& endpoint);
+
+// Override the OS Version.
+void SetOsVersionForTesting(int32_t os_major_version,
+                            int32_t os_minor_version,
+                            int32_t os_bugfix_version);
+// Go back to the default behavior of getting the OS version from the OS.
+void ClearOsVersionTestingOverride();
+
+#endif  // COMPONENTS_CRASH_CONTENT_BROWSER_ERROR_REPORTING_SEND_JAVASCRIPT_ERROR_REPORT_H_
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc b/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc
new file mode 100644
index 0000000..7e18c8a
--- /dev/null
+++ b/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc
@@ -0,0 +1,224 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/browser/error_reporting/send_javascript_error_report.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "build/build_config.h"
+#include "components/crash/content/browser/error_reporting/javascript_error_report.h"
+#include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AllOf;
+using ::testing::HasSubstr;
+
+class SendJavaScriptErrorReportTest : public ::testing::Test {
+ public:
+  SendJavaScriptErrorReportTest()
+      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+        run_loop_quit_(run_loop_.QuitClosure()) {}
+
+  void SetUp() override {
+    test_server_ = std::make_unique<net::test_server::EmbeddedTestServer>();
+    endpoint_ = std::make_unique<MockCrashEndpoint>(test_server_.get());
+    SetOsVersionForTesting(7, 20, 1);
+  }
+
+  void TearDown() override { ClearOsVersionTestingOverride(); }
+
+  void FinishCallback() {
+    finish_callback_was_called_ = true;
+    run_loop_quit_.Run();
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+  base::RunLoop run_loop_;
+  base::RepeatingClosure run_loop_quit_;
+  content::TestBrowserContext browser_context_;
+  std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_;
+  std::unique_ptr<MockCrashEndpoint> endpoint_;
+  bool finish_callback_was_called_ = false;
+};
+
+TEST_F(SendJavaScriptErrorReportTest, Basic) {
+  JavaScriptErrorReport report;
+  report.message = "Hello World";
+  report.url = "https://www.chromium.org/Home";
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  const base::Optional<MockCrashEndpoint::Report>& actual_report =
+      endpoint_->last_report();
+  ASSERT_TRUE(actual_report);
+  EXPECT_THAT(actual_report->query, HasSubstr("error_message=Hello%20World"));
+  EXPECT_THAT(actual_report->query, HasSubstr("type=JavascriptError"));
+  // TODO(iby) research why URL is repeated...
+  EXPECT_THAT(actual_report->query,
+              HasSubstr("src=https%3A%2F%2Fwww.chromium.org%2FHome"));
+  EXPECT_THAT(actual_report->query,
+              HasSubstr("full_url=https%3A%2F%2Fwww.chromium.org%2FHome"));
+  EXPECT_THAT(actual_report->query, HasSubstr("url=%2FHome"));
+  // This is from SetOsVersionForTesting(7, 20, 1) in SetUp().
+  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
+  EXPECT_THAT(actual_report->query, HasSubstr("browser=Chrome"));
+  // These are from MockCrashEndpoint::Client::GetProductNameAndVersion, which
+  // is only defined for non-MAC POSIX systems. TODO(https://crbug.com/1121816):
+  // Get this info for non-POSIX platforms.
+#if defined(OS_POSIX) && !defined(OS_MAC)
+  EXPECT_THAT(actual_report->query, HasSubstr("prod=Chrome_ChromeOS"));
+  EXPECT_THAT(actual_report->query, HasSubstr("ver=1.2.3.4"));
+  EXPECT_THAT(actual_report->query, HasSubstr("browser_version=1.2.3.4"));
+  EXPECT_THAT(actual_report->query, HasSubstr("channel=Stable"));
+#endif
+  EXPECT_EQ(actual_report->content, "");
+}
+
+TEST_F(SendJavaScriptErrorReportTest, AllFields) {
+  JavaScriptErrorReport report;
+  report.message = "Hello World";
+  report.url = "https://www.chromium.org/Home";
+  report.product = "Unit test";
+  report.version = "6.2.3.4";
+  report.line_number = 83;
+  report.column_number = 14;
+  report.stack_trace = "bad_func(1, 2)\nonclick()\n";
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  const base::Optional<MockCrashEndpoint::Report>& actual_report =
+      endpoint_->last_report();
+  ASSERT_TRUE(actual_report);
+  EXPECT_THAT(actual_report->query, HasSubstr("error_message=Hello%20World"));
+  EXPECT_THAT(actual_report->query, HasSubstr("type=JavascriptError"));
+  // TODO(iby) research why URL is repeated...
+  EXPECT_THAT(actual_report->query,
+              HasSubstr("src=https%3A%2F%2Fwww.chromium.org%2FHome"));
+  EXPECT_THAT(actual_report->query,
+              HasSubstr("full_url=https%3A%2F%2Fwww.chromium.org%2FHome"));
+  EXPECT_THAT(actual_report->query, HasSubstr("url=%2FHome"));
+  // This is from SetOsVersionForTesting(7, 20, 1) in SetUp().
+  EXPECT_THAT(actual_report->query, HasSubstr("os_version=7.20.1"));
+  EXPECT_THAT(actual_report->query, HasSubstr("browser=Chrome"));
+  // product is double-escaped. The first time, it transforms to Unit%20test,
+  // then the % is turned into %25.
+  EXPECT_THAT(actual_report->query, HasSubstr("prod=Unit%2520test"));
+  EXPECT_THAT(actual_report->query, HasSubstr("ver=6.2.3.4"));
+  EXPECT_THAT(actual_report->query, HasSubstr("line=83"));
+  EXPECT_THAT(actual_report->query, HasSubstr("column=14"));
+  // These are from MockCrashEndpoint::Client::GetProductNameAndVersion, which
+  // is only defined for non-MAC POSIX systems. TODO(https://crbug.com/1121816):
+  // Get this info for non-POSIX platforms.
+#if defined(OS_POSIX) && !defined(OS_MAC)
+  EXPECT_THAT(actual_report->query, HasSubstr("browser_version=1.2.3.4"));
+  EXPECT_THAT(actual_report->query, HasSubstr("channel=Stable"));
+#endif
+  EXPECT_EQ(actual_report->content, "bad_func(1, 2)\nonclick()\n");
+}
+
+TEST_F(SendJavaScriptErrorReportTest, NoConsent) {
+  endpoint_->set_consented(false);
+  JavaScriptErrorReport report;
+  report.message = "Hello World";
+  report.url = "https://www.chromium.org/Home";
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  EXPECT_FALSE(endpoint_->last_report());
+}
+
+TEST_F(SendJavaScriptErrorReportTest, StackTraceWithErrorMessage) {
+  JavaScriptErrorReport report;
+  report.message = "Hello World";
+  report.url = "https://www.chromium.org/Home";
+  report.stack_trace = "Hello World\nbad_func(1, 2)\nonclick()\n";
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  const base::Optional<MockCrashEndpoint::Report>& actual_report =
+      endpoint_->last_report();
+  ASSERT_TRUE(actual_report);
+  EXPECT_THAT(actual_report->query, HasSubstr("error_message=Hello%20World"));
+  EXPECT_EQ(actual_report->content, "bad_func(1, 2)\nonclick()\n");
+}
+
+TEST_F(SendJavaScriptErrorReportTest, RedactMessage) {
+  JavaScriptErrorReport report;
+  report.message = "alpha@beta.org says hi to gamma@omega.co.uk";
+  report.url = "https://www.chromium.org/Home";
+  report.stack_trace =
+      "alpha@beta.org says hi to gamma@omega.co.uk\n"
+      "bad_func(1, 2)\nonclick()\n";
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  const base::Optional<MockCrashEndpoint::Report>& actual_report =
+      endpoint_->last_report();
+  ASSERT_TRUE(actual_report);
+  // Escaped version of "<email: 1> says hi to <email: 2>"
+  EXPECT_THAT(actual_report->query,
+              HasSubstr("error_message=%3Cemail%3A%201%3E%20says%20hi%20to%20"
+                        "%3Cemail%3A%202%3E"));
+  // Redacted messages still need to be removed from stack trace.
+  EXPECT_EQ(actual_report->content, "bad_func(1, 2)\nonclick()\n");
+}
+
+TEST_F(SendJavaScriptErrorReportTest, NonGoogleChrome) {
+  JavaScriptErrorReport report;
+  report.message = "Hello World";
+  report.url = "https://www.chromium.org/Home";
+  // We use a blank URL in non-GOOGLE_CHROME_BUILDs to avoid uploading reports
+  // from those browsers.
+  SetCrashEndpointForTesting("");
+
+  SendJavaScriptErrorReport(
+      std::move(report),
+      base::BindOnce(&SendJavaScriptErrorReportTest::FinishCallback,
+                     base::Unretained(this)),
+      &browser_context_);
+  run_loop_.Run();
+  EXPECT_TRUE(finish_callback_was_called_);
+
+  const base::Optional<MockCrashEndpoint::Report>& actual_report =
+      endpoint_->last_report();
+  EXPECT_FALSE(actual_report);
+}
diff --git a/components/exo/drag_drop_operation.cc b/components/exo/drag_drop_operation.cc
index 5224320..bd95b7d 100644
--- a/components/exo/drag_drop_operation.cc
+++ b/components/exo/drag_drop_operation.cc
@@ -17,6 +17,9 @@
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/transform_util.h"
 
 #if defined(OS_CHROMEOS)
@@ -79,7 +82,7 @@
     DataSource* source,
     Surface* origin,
     Surface* icon,
-    const gfx::Point& drag_start_point,
+    const gfx::PointF& drag_start_point,
     ui::mojom::DragEventSource event_source) {
   auto* dnd_op = new DragDropOperation(source, origin, icon, drag_start_point,
                                        event_source);
@@ -89,7 +92,7 @@
 DragDropOperation::DragDropOperation(DataSource* source,
                                      Surface* origin,
                                      Surface* icon,
-                                     const gfx::Point& drag_start_point,
+                                     const gfx::PointF& drag_start_point,
                                      ui::mojom::DragEventSource event_source)
     : SurfaceTreeHost("ExoDragDropOperation"),
       source_(std::make_unique<ScopedDataSource>(source, this)),
@@ -242,11 +245,13 @@
   base::WeakPtr<DragDropOperation> weak_ptr = weak_ptr_factory_.GetWeakPtr();
 
   started_by_this_object_ = true;
+  gfx::Point drag_start_point = gfx::ToFlooredPoint(drag_start_point_);
+
   // This triggers a nested run loop that terminates when the drag and drop
   // operation is completed.
   int op = drag_drop_controller_->StartDragAndDrop(
       std::move(os_exchange_data_), origin_->get()->window()->GetRootWindow(),
-      origin_->get()->window(), drag_start_point_, dnd_operations,
+      origin_->get()->window(), drag_start_point, dnd_operations,
       event_source_);
 
   // The instance deleted during StartDragAndDrop's nested RunLoop.
diff --git a/components/exo/drag_drop_operation.h b/components/exo/drag_drop_operation.h
index 436cec3..cf6da01 100644
--- a/components/exo/drag_drop_operation.h
+++ b/components/exo/drag_drop_operation.h
@@ -13,6 +13,7 @@
 #include "components/exo/wm_helper.h"
 #include "ui/aura/client/drag_drop_client_observer.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
+#include "ui/gfx/geometry/point_f.h"
 
 namespace ash {
 class DragDropController;
@@ -50,7 +51,7 @@
       DataSource* source,
       Surface* origin,
       Surface* icon,
-      const gfx::Point& drag_start_point,
+      const gfx::PointF& drag_start_point,
       ui::mojom::DragEventSource event_source);
 
   // Abort the operation if it hasn't been started yet, otherwise do nothing.
@@ -78,7 +79,7 @@
   DragDropOperation(DataSource* source,
                     Surface* origin,
                     Surface* icon,
-                    const gfx::Point& drag_start_point,
+                    const gfx::PointF& drag_start_point,
                     ui::mojom::DragEventSource event_source);
   ~DragDropOperation() override;
 
@@ -97,7 +98,7 @@
   std::unique_ptr<ScopedDataSource> source_;
   std::unique_ptr<ScopedSurface> icon_;
   std::unique_ptr<ScopedSurface> origin_;
-  gfx::Point drag_start_point_;
+  gfx::PointF drag_start_point_;
   std::unique_ptr<ui::OSExchangeData> os_exchange_data_;
 #if defined(OS_CHROMEOS)
   ash::DragDropController* drag_drop_controller_;
diff --git a/components/exo/drag_drop_operation_unittest.cc b/components/exo/drag_drop_operation_unittest.cc
index 4cccc4c..e0e30b8 100644
--- a/components/exo/drag_drop_operation_unittest.cc
+++ b/components/exo/drag_drop_operation_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/exo/test/exo_test_base.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
+#include "ui/gfx/geometry/point_f.h"
 
 namespace exo {
 namespace {
@@ -103,8 +104,8 @@
   icon_surface->Attach(buffer.get());
 
   auto operation = DragDropOperation::Create(
-      data_source.get(), origin_surface.get(), icon_surface.get(), gfx::Point(),
-      ui::mojom::DragEventSource::kMouse);
+      data_source.get(), origin_surface.get(), icon_surface.get(),
+      gfx::PointF(), ui::mojom::DragEventSource::kMouse);
   icon_surface->Commit();
 
   base::RunLoop run_loop;
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index f66f306e..6c446e3 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -346,7 +346,7 @@
   if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED)
     return;
 
-  seat_->SetLastLocation(event->root_location());
+  seat_->SetLastPointerLocation(event->root_location_f());
 
   Surface* target = GetEffectiveTargetForEvent(event);
   gfx::PointF location_in_target = event->location_f();
diff --git a/components/exo/seat.cc b/components/exo/seat.cc
index 209c3fef..c7881a30 100644
--- a/components/exo/seat.cc
+++ b/components/exo/seat.cc
@@ -5,6 +5,7 @@
 #include "components/exo/seat.h"
 
 #include <memory>
+#include "ui/gfx/geometry/point_f.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/shell.h"
@@ -98,11 +99,11 @@
                      ui::mojom::DragEventSource event_source) {
   // DragDropOperation manages its own lifetime.
   drag_drop_operation_ = DragDropOperation::Create(
-      source, origin, icon, last_location_, event_source);
+      source, origin, icon, last_pointer_location_, event_source);
 }
 
-void Seat::SetLastLocation(const gfx::Point& last_location) {
-  last_location_ = last_location;
+void Seat::SetLastPointerLocation(const gfx::PointF& last_pointer_location) {
+  last_pointer_location_ = last_pointer_location;
 }
 
 void Seat::AbortPendingDragOperation() {
diff --git a/components/exo/seat.h b/components/exo/seat.h
index 32ca40c9..2a6940e5 100644
--- a/components/exo/seat.h
+++ b/components/exo/seat.h
@@ -80,7 +80,7 @@
 
   // Sets the last location in screen coordinates, irrespective of mouse or
   // touch.
-  void SetLastLocation(const gfx::Point& last_location);
+  void SetLastPointerLocation(const gfx::PointF& last_pointer_location);
 
   // Abort any drag operations that haven't been started yet.
   void AbortPendingDragOperation();
@@ -159,7 +159,7 @@
   // True while Seat is updating clipboard data to selection source.
   bool changing_clipboard_data_to_selection_source_;
 
-  gfx::Point last_location_;
+  gfx::PointF last_pointer_location_;
 
   bool shutdown_ = false;
 
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index 1be1612..6c14def 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_state_type.h"
+#include "ash/scoped_animation_disabler.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/toplevel_window_event_handler.h"
@@ -37,43 +38,6 @@
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
-// ShellSurface, ScopedAnimationsDisabled:
-
-// Helper class used to temporarily disable animations. Restores the
-// animations disabled property when instance is destroyed.
-class ShellSurface::ScopedAnimationsDisabled {
- public:
-  explicit ScopedAnimationsDisabled(ShellSurface* shell_surface);
-  ~ScopedAnimationsDisabled();
-
- private:
-  ShellSurface* const shell_surface_;
-  bool saved_animations_disabled_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedAnimationsDisabled);
-};
-
-ShellSurface::ScopedAnimationsDisabled::ScopedAnimationsDisabled(
-    ShellSurface* shell_surface)
-    : shell_surface_(shell_surface) {
-  if (shell_surface_->widget_) {
-    aura::Window* window = shell_surface_->widget_->GetNativeWindow();
-    saved_animations_disabled_ =
-        window->GetProperty(aura::client::kAnimationsDisabledKey);
-    window->SetProperty(aura::client::kAnimationsDisabledKey, true);
-  }
-}
-
-ShellSurface::ScopedAnimationsDisabled::~ScopedAnimationsDisabled() {
-  if (shell_surface_->widget_) {
-    aura::Window* window = shell_surface_->widget_->GetNativeWindow();
-    DCHECK_EQ(window->GetProperty(aura::client::kAnimationsDisabledKey), true);
-    window->SetProperty(aura::client::kAnimationsDisabledKey,
-                        saved_animations_disabled_);
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // ShellSurface, Config:
 
 // Surface state associated with each configure request.
@@ -448,8 +412,8 @@
           nullptr, base::TimeDelta::FromMilliseconds(
                        kMaximizedOrFullscreenOrPinnedLockTimeoutMs));
     } else {
-      scoped_animations_disabled_ =
-          std::make_unique<ScopedAnimationsDisabled>(this);
+      animations_disabler_ = std::make_unique<ash::ScopedAnimationDisabler>(
+          widget_->GetNativeWindow());
     }
   }
 }
@@ -467,7 +431,7 @@
   }
 
   // Re-enable animations if they were disabled in pre state change handler.
-  scoped_animations_disabled_.reset();
+  animations_disabler_.reset();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index bbaeb51..c04db233 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -12,6 +12,10 @@
 #include "components/exo/shell_surface_base.h"
 #include "ui/base/ui_base_types.h"
 
+namespace ash {
+class ScopedAnimationDisabler;
+}  // namespace ash
+
 namespace ui {
 class CompositorLock;
 }  // namespace ui
@@ -122,7 +126,6 @@
   bool OnPreWidgetCommit() override;
 
  private:
-  class ScopedAnimationsDisabled;
   struct Config;
 
   // Helper class used to coalesce a number of changes into one "configure"
@@ -161,7 +164,7 @@
 
   void EndDrag();
 
-  std::unique_ptr<ScopedAnimationsDisabled> scoped_animations_disabled_;
+  std::unique_ptr<ash::ScopedAnimationDisabler> animations_disabler_;
 
   std::unique_ptr<ui::CompositorLock> configure_compositor_lock_;
   ConfigureCallback configure_callback_;
diff --git a/components/exo/touch.cc b/components/exo/touch.cc
index c1dc6b3..e5174f4 100644
--- a/components/exo/touch.cc
+++ b/components/exo/touch.cc
@@ -60,7 +60,7 @@
 // ui::EventHandler overrides:
 
 void Touch::OnTouchEvent(ui::TouchEvent* event) {
-  seat_->SetLastLocation(event->root_location());
+  seat_->SetLastPointerLocation(event->root_location_f());
 
   bool send_details = false;
 
diff --git a/components/favicon/core/favicon_handler_unittest.cc b/components/favicon/core/favicon_handler_unittest.cc
index d87b86dc..94bb249 100644
--- a/components/favicon/core/favicon_handler_unittest.cc
+++ b/components/favicon/core/favicon_handler_unittest.cc
@@ -13,7 +13,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -129,8 +128,9 @@
   };
 
   // |downloads| must not be nullptr and must outlive this object.
-  explicit FakeImageDownloader(URLVector* downloads)
-      : downloads_(downloads), next_download_id_(1) {}
+  explicit FakeImageDownloader(URLVector* downloads) : downloads_(downloads) {}
+  FakeImageDownloader(const FakeImageDownloader&) = delete;
+  FakeImageDownloader& operator=(const FakeImageDownloader&) = delete;
 
   // Implementation of FaviconHalder::Delegate's DownloadImage(). If a given
   // URL is not known (i.e. not previously added via Add()), it produces 404s.
@@ -204,7 +204,7 @@
 
  private:
   URLVector* downloads_;
-  int next_download_id_;
+  int next_download_id_ = 1;
 
   // URL to disable automatic callbacks for.
   GURL manual_callback_url_;
@@ -214,8 +214,6 @@
 
   // Registered responses.
   std::map<GURL, Response> responses_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeImageDownloader);
 };
 
 // Fake that implements the calls to FaviconHandler::Delegate's
@@ -229,6 +227,8 @@
   // |downloads| must not be nullptr and must outlive this object.
   explicit FakeManifestDownloader(URLVector* downloads)
       : downloads_(downloads) {}
+  FakeManifestDownloader(const FakeManifestDownloader&) = delete;
+  FakeManifestDownloader& operator=(const FakeManifestDownloader&) = delete;
 
   // Implementation of FaviconHalder::Delegate's DownloadManifest(). If a given
   // URL is not known (i.e. not previously added via Add()), it produces 404s.
@@ -267,7 +267,7 @@
 
   // Returns whether an ongoing download exists for a url previously selected
   // via SetRunCallbackManuallyForUrl().
-  bool HasPendingManualCallback() { return !manual_callbacks_.empty(); }
+  bool HasPendingManualCallback() const { return !manual_callbacks_.empty(); }
 
   // Triggers responses for downloads previously selected for manual triggering
   // via SetRunCallbackManuallyForUrl().
@@ -290,8 +290,6 @@
 
   // Registered responses.
   std::map<GURL, Response> responses_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeManifestDownloader);
 };
 
 class MockDelegate : public FaviconHandler::Delegate {
@@ -352,6 +350,8 @@
  public:
   FakeFaviconService()
       : manual_callback_task_runner_(new base::TestSimpleTaskRunner()) {}
+  FakeFaviconService(const FakeFaviconService&) = delete;
+  FakeFaviconService& operator=(const FakeFaviconService&) = delete;
 
   // Stores favicon with bitmap data in |results| at |page_url| and |icon_url|.
   void Store(const GURL& page_url,
@@ -452,8 +452,6 @@
   // Callback for GetFaviconForPageOrIconURL() request for
   // |manual_callback_url_|.
   scoped_refptr<base::TestSimpleTaskRunner> manual_callback_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeFaviconService);
 };
 
 // MockFaviconService subclass that delegates DB reads to FakeFaviconService.
@@ -470,13 +468,14 @@
         .WillByDefault(
             Invoke(&fake_, &FakeFaviconService::UpdateFaviconMappingsAndFetch));
   }
+  MockFaviconServiceWithFake(const MockFaviconServiceWithFake&) = delete;
+  MockFaviconServiceWithFake& operator=(const MockFaviconServiceWithFake&) =
+      delete;
 
   FakeFaviconService* fake() { return &fake_; }
 
  private:
   FakeFaviconService fake_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockFaviconServiceWithFake);
 };
 
 class FaviconHandlerTest : public testing::Test {
@@ -1822,6 +1821,13 @@
 
   FaviconHandlerManifestsEnabledTest() = default;
 
+ public:
+  FaviconHandlerManifestsEnabledTest(
+      const FaviconHandlerManifestsEnabledTest&) = delete;
+  FaviconHandlerManifestsEnabledTest& operator=(
+      const FaviconHandlerManifestsEnabledTest&) = delete;
+
+ protected:
   // Exercises the handler for the simplest case where all types are kTouchIcon
   // and no sizes are provided, using a FaviconHandler of type TOUCH_LARGETS.
   std::unique_ptr<FaviconHandler> RunHandlerWithSimpleTouchIconCandidates(
@@ -1839,8 +1845,6 @@
   // Avoid accidental use of kFavicon type, since Web Manifests are handled by
   // the FaviconHandler of type TOUCH_LARGEST.
   using FaviconHandlerTest::RunHandlerWithSimpleFaviconCandidates;
-
-  DISALLOW_COPY_AND_ASSIGN(FaviconHandlerManifestsEnabledTest);
 };
 
 // Test that favicon mappings are deleted when a manifest previously cached in
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index 58f773e..7bd5bc07 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -627,10 +627,14 @@
   delegate_->ClearAll();
 }
 
-void FeedStream::FetchImage(
+ImageFetchId FeedStream::FetchImage(
     const GURL& url,
     base::OnceCallback<void(NetworkResponse)> callback) {
-  image_fetcher_->Fetch(url, std::move(callback));
+  return image_fetcher_->Fetch(url, std::move(callback));
+}
+
+void FeedStream::CancelImageFetch(ImageFetchId id) {
+  image_fetcher_->Cancel(id);
 }
 
 void FeedStream::UploadAction(
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h
index 59390e3..b3c2690 100644
--- a/components/feed/core/v2/feed_stream.h
+++ b/components/feed/core/v2/feed_stream.h
@@ -129,8 +129,10 @@
   bool IsArticlesListVisible() override;
   std::string GetClientInstanceId() override;
   void ExecuteRefreshTask() override;
-  void FetchImage(const GURL& url,
-                  base::OnceCallback<void(NetworkResponse)> callback) override;
+  ImageFetchId FetchImage(
+      const GURL& url,
+      base::OnceCallback<void(NetworkResponse)> callback) override;
+  void CancelImageFetch(ImageFetchId id) override;
   void LoadMore(SurfaceId surface_id,
                 base::OnceCallback<void(bool)> callback) override;
   void ExecuteOperations(
diff --git a/components/feed/core/v2/feed_stream_unittest.cc b/components/feed/core/v2/feed_stream_unittest.cc
index 9439fd7..96fc8a13 100644
--- a/components/feed/core/v2/feed_stream_unittest.cc
+++ b/components/feed/core/v2/feed_stream_unittest.cc
@@ -264,12 +264,18 @@
   explicit TestImageFetcher(
       scoped_refptr<::network::SharedURLLoaderFactory> url_loader_factory)
       : ImageFetcher(url_loader_factory) {}
-  void Fetch(const GURL& url,
-             base::OnceCallback<void(NetworkResponse)> callback) override {
+  ImageFetchId Fetch(
+      const GURL& url,
+      base::OnceCallback<void(NetworkResponse)> callback) override {
     // Emulate a response.
     NetworkResponse response = {"dummyresponse", 200};
     std::move(callback).Run(std::move(response));
+    return id_generator_.GenerateNextId();
   }
+  void Cancel(ImageFetchId id) override {}
+
+ private:
+  ImageFetchId::Generator id_generator_;
 };
 
 class TestFeedNetwork : public FeedNetwork {
diff --git a/components/feed/core/v2/image_fetcher.cc b/components/feed/core/v2/image_fetcher.cc
index 6315070..49c4bc60 100644
--- a/components/feed/core/v2/image_fetcher.cc
+++ b/components/feed/core/v2/image_fetcher.cc
@@ -6,6 +6,7 @@
 
 #include "components/feed/core/v2/metrics_reporter.h"
 #include "components/feed/core/v2/public/types.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -18,7 +19,7 @@
 
 ImageFetcher::~ImageFetcher() = default;
 
-void ImageFetcher::Fetch(const GURL& url, ImageCallback callback) {
+ImageFetchId ImageFetcher::Fetch(const GURL& url, ImageCallback callback) {
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("interest_feedv2_image_send", R"(
         semantics {
@@ -48,29 +49,73 @@
   auto simple_loader = network::SimpleURLLoader::Create(
       std::move(resource_request), traffic_annotation);
   auto* const simple_loader_ptr = simple_loader.get();
+
+  ImageFetchId id = id_generator_.GenerateNextId();
+  bool inserted =
+      pending_requests_
+          .try_emplace(id, std::move(simple_loader), std::move(callback))
+          .second;
+  DCHECK(inserted);
+
   simple_loader_ptr->DownloadToString(
       url_loader_factory_.get(),
       base::BindOnce(&ImageFetcher::OnFetchComplete, weak_factory_.GetWeakPtr(),
-                     std::move(simple_loader), std::move(callback)),
+                     id),
       network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+  return id;
 }
 
-void ImageFetcher::OnFetchComplete(
-    std::unique_ptr<network::SimpleURLLoader> simple_loader,
-    ImageCallback callback,
-    std::unique_ptr<std::string> response_data) {
-  MetricsReporter::OnImageFetched(
-      response_data ? simple_loader->ResponseInfo()->headers->response_code()
-                    : simple_loader->NetError());
-  NetworkResponse response{std::string(), simple_loader->NetError()};
-  if (simple_loader->ResponseInfo() && simple_loader->ResponseInfo()->headers) {
+void ImageFetcher::OnFetchComplete(ImageFetchId id,
+                                   std::unique_ptr<std::string> response_data) {
+  base::Optional<PendingRequest> request = RemovePending(id);
+  if (!request)
+    return;
+
+  NetworkResponse response;
+  if (request->loader->ResponseInfo() &&
+      request->loader->ResponseInfo()->headers) {
     response.status_code =
-        simple_loader->ResponseInfo()->headers->response_code();
+        request->loader->ResponseInfo()->headers->response_code();
+  } else {
+    response.status_code = request->loader->NetError();
   }
+  MetricsReporter::OnImageFetched(response.status_code);
 
   if (response_data)
     response.response_bytes = std::move(*response_data);
-  std::move(callback).Run(std::move(response));
+  std::move(request->callback).Run(std::move(response));
 }
 
+void ImageFetcher::Cancel(ImageFetchId id) {
+  base::Optional<PendingRequest> request = RemovePending(id);
+  if (!request)
+    return;
+
+  // Cancel the fetch before running the callback.
+  request->loader.reset();
+  std::move(request->callback)
+      .Run({/*response_bytes=*/std::string(), net::Error::ERR_ABORTED});
+}
+
+base::Optional<ImageFetcher::PendingRequest> ImageFetcher::RemovePending(
+    ImageFetchId id) {
+  auto iterator = pending_requests_.find(id);
+  if (iterator == pending_requests_.end())
+    return base::nullopt;
+
+  auto request = base::make_optional(std::move(iterator->second));
+  pending_requests_.erase(iterator);
+  return request;
+}
+
+ImageFetcher::PendingRequest::PendingRequest(
+    std::unique_ptr<network::SimpleURLLoader> loader,
+    ImageCallback callback)
+    : loader(std::move(loader)), callback(std::move(callback)) {}
+ImageFetcher::PendingRequest::PendingRequest(
+    ImageFetcher::PendingRequest&& other) = default;
+ImageFetcher::PendingRequest& ImageFetcher::PendingRequest::operator=(
+    ImageFetcher::PendingRequest&& other) = default;
+ImageFetcher::PendingRequest::~PendingRequest() = default;
+
 }  // namespace feed
diff --git a/components/feed/core/v2/image_fetcher.h b/components/feed/core/v2/image_fetcher.h
index e1fa77a..0d333321 100644
--- a/components/feed/core/v2/image_fetcher.h
+++ b/components/feed/core/v2/image_fetcher.h
@@ -6,8 +6,11 @@
 #define COMPONENTS_FEED_CORE_V2_IMAGE_FETCHER_H_
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/feed/core/v2/public/types.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -29,14 +32,29 @@
   ImageFetcher(const ImageFetcher&) = delete;
   ImageFetcher& operator=(const ImageFetcher&) = delete;
 
-  virtual void Fetch(const GURL& url, ImageCallback callback);
+  virtual ImageFetchId Fetch(const GURL& url, ImageCallback callback);
+  virtual void Cancel(ImageFetchId id);
 
  private:
+  struct PendingRequest {
+    PendingRequest(std::unique_ptr<network::SimpleURLLoader> loader,
+                   ImageCallback callback);
+    PendingRequest(PendingRequest&& other);
+    PendingRequest& operator=(PendingRequest&& other);
+    ~PendingRequest();
+
+    std::unique_ptr<network::SimpleURLLoader> loader;
+    ImageCallback callback;
+  };
+
   // Called when fetch request completes.
-  void OnFetchComplete(std::unique_ptr<network::SimpleURLLoader> simple_loader,
-                       ImageCallback callback,
+  void OnFetchComplete(ImageFetchId id,
                        std::unique_ptr<std::string> response_data);
 
+  base::Optional<PendingRequest> RemovePending(ImageFetchId id);
+
+  ImageFetchId::Generator id_generator_;
+  base::flat_map<ImageFetchId, PendingRequest> pending_requests_;
   const scoped_refptr<::network::SharedURLLoaderFactory> url_loader_factory_;
   base::WeakPtrFactory<ImageFetcher> weak_factory_{this};
 };
diff --git a/components/feed/core/v2/image_fetcher_unittest.cc b/components/feed/core/v2/image_fetcher_unittest.cc
index 22be032..ba2dbe4 100644
--- a/components/feed/core/v2/image_fetcher_unittest.cc
+++ b/components/feed/core/v2/image_fetcher_unittest.cc
@@ -151,5 +151,39 @@
               HasSubstr("example2_response"));
 }
 
+TEST_F(ImageFetcherTest, CancelRunsCallback) {
+  CallbackReceiver<NetworkResponse> receiver;
+  ImageFetchId id =
+      image_fetcher()->Fetch(GURL("https://example.com"), receiver.Bind());
+
+  image_fetcher()->Cancel(id);
+  ASSERT_TRUE(receiver.GetResult());
+  EXPECT_EQ(std::string(), receiver.GetResult()->response_bytes);
+  EXPECT_EQ(net::Error::ERR_ABORTED, receiver.GetResult()->status_code);
+}
+
+TEST_F(ImageFetcherTest, CancelThenRespond) {
+  CallbackReceiver<NetworkResponse> receiver;
+  ImageFetchId id =
+      image_fetcher()->Fetch(GURL("https://example.com"), receiver.Bind());
+
+  image_fetcher()->Cancel(id);
+  Respond("example1_response", net::HTTP_OK);
+  ASSERT_TRUE(receiver.GetResult());
+  EXPECT_EQ(std::string(), receiver.GetResult()->response_bytes);
+  EXPECT_EQ(net::Error::ERR_ABORTED, receiver.GetResult()->status_code);
+}
+
+TEST_F(ImageFetcherTest, CallbackCallsCancel) {
+  // Ensure nothing terrible happens if cancel is called from the callback.
+  ImageFetchId id;
+  id = image_fetcher()->Fetch(
+      GURL("https://example.com"),
+      base::BindLambdaForTesting(
+          [&](NetworkResponse response) { image_fetcher()->Cancel(id); }));
+
+  image_fetcher()->Cancel(id);
+}
+
 }  // namespace
 }  // namespace feed
diff --git a/components/feed/core/v2/public/feed_stream_api.h b/components/feed/core/v2/public/feed_stream_api.h
index d2adbda..d3bfabc3 100644
--- a/components/feed/core/v2/public/feed_stream_api.h
+++ b/components/feed/core/v2/public/feed_stream_api.h
@@ -78,10 +78,15 @@
                         base::OnceCallback<void(bool)> callback) = 0;
 
   // Request to fetch and image for use in the feed. Calls |callback|
-  // with the network response when complete.
-  virtual void FetchImage(
+  // with the network response when complete. The returned ImageFetchId can be
+  // passed to CancelImageFetch() to cancel the request.
+  virtual ImageFetchId FetchImage(
       const GURL& url,
       base::OnceCallback<void(NetworkResponse)> callback) = 0;
+  // If |id| matches an active fetch, cancels the fetch and runs the callback
+  // with an empty response body and status_code=net::Error::ERR_ABORTED. If
+  // |id| doesn't match an active fetch, nothing happens.
+  virtual void CancelImageFetch(ImageFetchId id) = 0;
 
   // Apply |operations| to the stream model. Does nothing if the model is not
   // yet loaded.
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 64fa531..7680ef7 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -32,6 +32,7 @@
 // A unique ID for an ephemeral change.
 using EphemeralChangeId = util::IdTypeU32<class EphemeralChangeIdClass>;
 using SurfaceId = util::IdTypeU32<class SurfaceIdClass>;
+using ImageFetchId = util::IdTypeU32<class ImageFetchIdClass>;
 
 struct NetworkResponseInfo {
   NetworkResponseInfo();
@@ -55,6 +56,7 @@
   // HTTP status code if available, or net::Error otherwise.
   int status_code;
 
+  NetworkResponse() = default;
   NetworkResponse(NetworkResponse&& other) = default;
   NetworkResponse& operator=(NetworkResponse&& other) = default;
 };
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index d613929..dcdedf72 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -127,6 +127,9 @@
  public:
   explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test)
       : test_(test) {}
+  HistoryBackendTestDelegate(const HistoryBackendTestDelegate&) = delete;
+  HistoryBackendTestDelegate& operator=(const HistoryBackendTestDelegate&) =
+      delete;
 
   void NotifyProfileError(sql::InitStatus init_status,
                           const std::string& diagnostics) override {}
@@ -149,8 +152,6 @@
  private:
   // Not owned by us.
   HistoryBackendTestBase* test_;
-
-  DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
 };
 
 class HistoryBackendTestBase : public testing::Test {
@@ -159,10 +160,10 @@
   typedef std::vector<URLRows> URLsModifiedList;
   typedef std::vector<std::pair<bool, bool>> URLsDeletedList;
 
-  HistoryBackendTestBase()
-      : loaded_(false) {}
-
-  ~HistoryBackendTestBase() override {}
+  HistoryBackendTestBase() = default;
+  HistoryBackendTestBase(const HistoryBackendTestBase&) = delete;
+  HistoryBackendTestBase& operator=(const HistoryBackendTestBase&) = delete;
+  ~HistoryBackendTestBase() override = default;
 
  protected:
   std::vector<GURL> favicon_changed_notifications_page_urls() const {
@@ -247,7 +248,7 @@
   history::HistoryClientFakeBookmarks history_client_;
   scoped_refptr<HistoryBackend> backend_;  // Will be NULL on init failure.
   std::unique_ptr<InMemoryHistoryBackend> mem_backend_;
-  bool loaded_;
+  bool loaded_ = false;
 
  private:
   friend class HistoryBackendTestDelegate;
@@ -286,8 +287,6 @@
   URLsDeletedList urls_deleted_notifications_;
 
   base::FilePath test_dir_;
-
-  DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase);
 };
 
 void HistoryBackendTestDelegate::SetInMemoryBackend(
@@ -334,8 +333,10 @@
 
 class HistoryBackendTest : public HistoryBackendTestBase {
  public:
-  HistoryBackendTest() {}
-  ~HistoryBackendTest() override {}
+  HistoryBackendTest() = default;
+  HistoryBackendTest(const HistoryBackendTest&) = delete;
+  HistoryBackendTest& operator=(const HistoryBackendTest&) = delete;
+  ~HistoryBackendTest() override = default;
 
  protected:
   favicon::FaviconDatabase* favicon_db() {
@@ -469,15 +470,15 @@
            bitmap_data->size() == 1u &&
            *bitmap_data->front() == expected_data;
   }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest);
 };
 
 class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
  public:
-  InMemoryHistoryBackendTest() {}
-  ~InMemoryHistoryBackendTest() override {}
+  InMemoryHistoryBackendTest() = default;
+  InMemoryHistoryBackendTest(const InMemoryHistoryBackendTest&) = delete;
+  InMemoryHistoryBackendTest& operator=(const InMemoryHistoryBackendTest&) =
+      delete;
+  ~InMemoryHistoryBackendTest() override = default;
 
  protected:
   void SimulateNotificationURLsDeleted(const URLRow* row1,
@@ -539,9 +540,6 @@
   static const KeywordID kTestKeywordId;
   static const char kTestSearchTerm1[];
   static const char kTestSearchTerm2[];
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest);
 };
 
 const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42;
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 1281360..c0bd6c4 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
 #include "base/json/json_reader.h"
-#include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -266,6 +265,10 @@
     RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry());
   }
 
+  RemoteSuggestionsProviderImplTest(const RemoteSuggestionsProviderImplTest&) =
+      delete;
+  RemoteSuggestionsProviderImplTest& operator=(
+      const RemoteSuggestionsProviderImplTest&) = delete;
   ~RemoteSuggestionsProviderImplTest() override {
     provider_.reset();
     observer_.reset();
@@ -609,8 +612,6 @@
   FakeDB<SnippetImageProto>* image_db_;
 
   scoped_refptr<TestMockTimeTaskRunner> timer_mock_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImplTest);
 };
 
 TEST_F(RemoteSuggestionsProviderImplTest, Full) {
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 2a5e338..6967a42 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -222,7 +222,7 @@
 
   DeduplicateMatches(&matches_);
 
-  // Sort the matches.
+  // Sort the matches by relevance and demotions.
   std::sort(matches_.begin(), matches_.end(), comparing_object);
 
   // Find the best match and rotate it to the front to become the default match.
@@ -270,6 +270,7 @@
       CalculateNumMatches(is_zero_suggest, matches_, comparing_object);
   matches_.resize(num_matches);
 
+  // Group search suggestions above URL suggestions.
 #if defined(OS_ANDROID)
   if (matches_.size() > 2 &&
       !base::FeatureList::IsEnabled(omnibox::kAdaptiveSuggestionsCount)) {
@@ -282,11 +283,13 @@
             matches_.front().type)) {
       while (next != matches_.end() &&
              (AutocompleteMatch::ShouldBeSkippedForGroupBySearchVsUrl(
-                 matches_.front().type))) {
+                 next->type))) {
         next = std::next(next);
       }
     }
-    GroupSuggestionsBySearchVsURL(next, matches_.end());
+    auto begin_url = GroupSuggestionsBySearchVsURL(next, matches_.end());
+    if (base::FeatureList::IsEnabled(omnibox::kBubbleUrlSuggestions))
+      BubbleURLSuggestions(next, begin_url, matches_);
   }
 
   // If we have a default match, run some sanity checks. Skip these checks if
@@ -1012,9 +1015,60 @@
 }
 
 // static
-void AutocompleteResult::GroupSuggestionsBySearchVsURL(iterator begin,
-                                                       iterator end) {
-  std::stable_partition(begin, end, [](const AutocompleteMatch& match) {
-    return match.IsSearchType(match.type);
+AutocompleteResult::iterator AutocompleteResult::GroupSuggestionsBySearchVsURL(
+    iterator begin,
+    iterator end) {
+  return std::stable_partition(begin, end, [](const AutocompleteMatch& match) {
+    return AutocompleteMatch::IsSearchType(match.type);
   });
 }
+
+// static
+void AutocompleteResult::BubbleURLSuggestions(iterator begin_search,
+                                              iterator begin_url,
+                                              ACMatches& matches) {
+  auto absolute_gap = base::GetFieldTrialParamByFeatureAsInt(
+      omnibox::kBubbleUrlSuggestions,
+      OmniboxFieldTrial::kBubbleUrlSuggestionsAbsoluteGapParam, 200);
+  auto relative_gap = base::GetFieldTrialParamByFeatureAsDouble(
+      omnibox::kBubbleUrlSuggestions,
+      OmniboxFieldTrial::kBubbleUrlSuggestionsRelativeGapParam, 1);
+  auto absolute_buffer = base::GetFieldTrialParamByFeatureAsInt(
+      omnibox::kBubbleUrlSuggestions,
+      OmniboxFieldTrial::kBubbleUrlSuggestionsAbsoluteBufferParam, 100);
+  auto relative_buffer = base::GetFieldTrialParamByFeatureAsDouble(
+      omnibox::kBubbleUrlSuggestions,
+      OmniboxFieldTrial::kBubbleUrlSuggestionsRelativeBufferParam, 1);
+
+  // |next_url| tracks the first (i.e. highest scoring) yet unbubbled URL
+  // suggestion.
+  auto next_url = begin_url;
+
+  for (auto next_search = begin_search;
+       next_search != next_url && next_url != matches.end();
+       next_search = std::next(next_search)) {
+    // Only bubble if there's a sufficient score gap between adjacent searches.
+    if (next_search != begin_search &&
+        std::prev(next_search)->relevance <
+            std::max(next_search->relevance + absolute_gap * 1.,
+                     next_search->relevance * relative_gap))
+      continue;
+    // Only bubble if there's a sufficient buffer between the URL and search.
+    if (next_url->relevance <
+        std::max(next_search->relevance + absolute_buffer * 1.,
+                 next_search->relevance * relative_buffer))
+      continue;
+
+    // Find the series of URLs to bubble: [next_url, last_bubble_url).
+    // Although |next_url| must score higher than the |next_search| by at least
+    // the buffer amount, the remaining URls in the series need to score only
+    // as high as |next_search|.
+    auto last_bubble_url = std::find_if(
+        std::next(next_url), matches.end(),
+        [&](auto& match) { return match.relevance < next_search->relevance; });
+
+    // Bubble [next_url, last_bubble_url) above |next_search|.
+    next_search = std::rotate(next_search, next_url, last_bubble_url);
+    next_url = last_bubble_url;
+  }
+}
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index b23112b..6f24875 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -217,6 +217,7 @@
                            TestGroupSuggestionsBySearchVsURL);
   FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest,
                            DemoteOnDeviceSearchSuggestions);
+  FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest, BubbleURLSuggestions);
   friend class HistoryURLProviderTest;
 
   typedef std::map<AutocompleteProvider*, ACMatches> ProviderToMatches;
@@ -277,7 +278,17 @@
   // search types, and their submatches regardless of type, are shifted
   // earlier in the range, while non-search types and their submatches
   // are shifted later.
-  static void GroupSuggestionsBySearchVsURL(iterator begin, iterator end);
+  static iterator GroupSuggestionsBySearchVsURL(iterator begin, iterator end);
+
+  // Bubbles groups of high scoring URLs into gaps between searches. |matches|
+  // should already be grouped (see |GroupSuggestionsBySearchVsURL()|) such that
+  // search suggestions are ordered before URL suggestions. |begin_search|
+  // refers to the first search suggestion to be considered (e.g. excluding the
+  // default or clipboard suggestions). |begin_url| refers to the first URL
+  // suggestion.
+  static void BubbleURLSuggestions(iterator begin_search,
+                                   iterator begin_url,
+                                   ACMatches& matches);
 
   // If we have SearchProvider search suggestions, demote OnDeviceProvider
   // search suggestions, since, which in general have lower quality than
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 2ab93268..a41d8ff 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -56,10 +56,12 @@
 };
 
 // Adds |count| AutocompleteMatches to |matches|.
-void PopulateAutocompleteMatchesFromTestData(
-    const AutocompleteMatchTestData* data,
-    size_t count,
-    ACMatches* matches) {
+template <typename T>
+void PopulateAutocompleteMatchesFromTestData(const T* data,
+                                             size_t count,
+                                             ACMatches* matches) {
+  static_assert(std::is_base_of<AutocompleteMatchTestData, T>::value,
+                "T must derive from AutocompleteMatchTestData");
   ASSERT_TRUE(matches != nullptr);
   for (size_t i = 0; i < count; ++i) {
     AutocompleteMatch match;
@@ -145,6 +147,10 @@
                            const TestData* expected,
                            size_t expected_count);
 
+  void AssertMatch(AutocompleteMatch match,
+                   TestData expected_match_data,
+                   int i);
+
   // Creates an AutocompleteResult from |last| and |current|. The two are
   // merged by |TransferOldMatches| and compared by |AssertResultMatches|.
   void RunTransferOldMatchesTest(const TestData* last,
@@ -205,17 +211,22 @@
     const TestData* expected,
     size_t expected_count) {
   ASSERT_EQ(expected_count, result.size());
-  for (size_t i = 0; i < expected_count; ++i) {
-    AutocompleteMatch expected_match;
-    PopulateAutocompleteMatch(expected[i], &expected_match);
-    const AutocompleteMatch& match = *(result.begin() + i);
-    EXPECT_EQ(expected_match.provider, match.provider) << i;
-    EXPECT_EQ(expected_match.relevance, match.relevance) << i;
-    EXPECT_EQ(expected_match.allowed_to_be_default_match,
-              match.allowed_to_be_default_match) << i;
-    EXPECT_EQ(expected_match.destination_url.spec(),
-              match.destination_url.spec()) << i;
-  }
+  for (size_t i = 0; i < expected_count; ++i)
+    AssertMatch(*(result.begin() + i), expected[i], i);
+}
+
+void AutocompleteResultTest::AssertMatch(AutocompleteMatch match,
+                                         TestData expected_match_data,
+                                         int i) {
+  AutocompleteMatch expected_match;
+  PopulateAutocompleteMatch(expected_match_data, &expected_match);
+  EXPECT_EQ(expected_match.provider, match.provider) << i;
+  EXPECT_EQ(expected_match.relevance, match.relevance) << i;
+  EXPECT_EQ(expected_match.allowed_to_be_default_match,
+            match.allowed_to_be_default_match)
+      << i;
+  EXPECT_EQ(expected_match.destination_url.spec(), match.destination_url.spec())
+      << i;
 }
 
 void AutocompleteResultTest::RunTransferOldMatchesTest(const TestData* last,
@@ -1694,6 +1705,69 @@
   EXPECT_FALSE(result.match_at(2)->has_tab_match);
 }
 
+TEST_F(AutocompleteResultTest, AttachesPedals) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {omnibox::kOmniboxPedalSuggestions, omnibox::kOmniboxSuggestionButtonRow},
+      {});
+  EXPECT_TRUE(OmniboxFieldTrial::IsPedalSuggestionsEnabled());
+  FakeAutocompleteProviderClient client;
+  EXPECT_NE(nullptr, client.GetPedalProvider());
+
+  AutocompleteResult result;
+
+  // Populate result with test matches.
+  {
+    ACMatches matches;
+    struct TestData : AutocompleteMatchTestData {
+      std::string contents;
+      TestData(std::string url,
+               AutocompleteMatch::Type type,
+               std::string contents)
+          : AutocompleteMatchTestData{url, type}, contents(contents) {}
+    };
+    const TestData data[] = {
+        {"http://search-what-you-typed/",
+         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "search what you typed"},
+        {"http://search-history/", AutocompleteMatchType::SEARCH_HISTORY,
+         "search history"},
+        {"http://history-url/", AutocompleteMatchType::HISTORY_URL,
+         "history url"},
+        {"http://history-title/", AutocompleteMatchType::HISTORY_TITLE,
+         "history title"},
+        {"http://url-what-you-typed/",
+         AutocompleteMatchType::URL_WHAT_YOU_TYPED, "url what you typed"},
+        {"http://clipboard-url/", AutocompleteMatchType::CLIPBOARD_URL,
+         "clipboard url"},
+        {"http://bookmark-title/", AutocompleteMatchType::BOOKMARK_TITLE,
+         "bookmark title"},
+        {"http://entity-clear-history/",
+         AutocompleteMatchType::SEARCH_SUGGEST_ENTITY, "clear history"},
+        {"http://clear-history/", AutocompleteMatchType::SEARCH_SUGGEST,
+         "clear history"},
+    };
+    PopulateAutocompleteMatchesFromTestData(data, base::size(data), &matches);
+    for (size_t i = 0; i < base::size(data); i++) {
+      matches[i].contents = base::UTF8ToUTF16(data[i].contents);
+    }
+
+    AutocompleteInput input(base::ASCIIToUTF16("a"),
+                            metrics::OmniboxEventProto::OTHER,
+                            TestSchemeClassifier());
+    result.AppendMatches(input, matches);
+  }
+
+  // Attach |pedal| to result matches where appropriate.
+  result.ConvertInSuggestionPedalMatches(&client);
+
+  // Ensure the entity suggestion doesn't get a pedal even though its contents
+  // form a concept match.
+  EXPECT_EQ(nullptr, std::prev(std::prev(result.end()))->pedal);
+
+  // The same concept-matching contents on a non-entity suggestion gets a pedal.
+  EXPECT_NE(nullptr, std::prev(result.end())->pedal);
+}
+
 TEST_F(AutocompleteResultTest, DocumentSuggestionsCanMergeButNotToDefault) {
   // Types are populated below to avoid introducing a new test data creation
   // process.
@@ -1839,3 +1913,75 @@
   EXPECT_EQ(result.match_at(0)->relevance, 1500);
   EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_URL, result.match_at(0)->type);
 }
+
+TEST_F(AutocompleteResultTest, BubbleURLSuggestions) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(omnibox::kBubbleUrlSuggestions);
+
+  auto test = [&](const std::vector<int>& scores, size_t begin_search,
+                  size_t begin_url, const std::vector<size_t>& expected_order,
+                  const std::string& trace_string) {
+    SCOPED_TRACE(trace_string);
+    std::vector<TestData> data;
+    for (size_t i = 0; i < scores.size(); ++i)
+      data.push_back(TestData{i, 0, scores[i]});
+    ACMatches matches;
+    PopulateAutocompleteMatches(data.data(), scores.size(), &matches);
+    AutocompleteResult::BubbleURLSuggestions(
+        matches.begin() + begin_search, matches.begin() + begin_url, matches);
+    ASSERT_EQ(matches.size(), scores.size());
+    for (size_t i = 0; i < matches.size(); ++i)
+      AssertMatch(matches[i], data[expected_order[i]], i);
+  };
+
+  // Regardless of scores, in the trivial cases with only either searches or
+  // URLs, the matches should not be reordered.
+  test({500, 1100, 1000, 1300, 1200}, 0, 5, {0, 1, 2, 3, 4}, "Only searches");
+  test({500, 1100, 1000, 1300, 1200}, 0, 0, {0, 1, 2, 3, 4}, "Only URLs");
+  test({500, 1100, 1000, 1300, 1200}, 3, 5, {0, 1, 2, 3, 4},
+       "Only skipped suggestions & searches");
+  test({500, 1100, 1000, 1300, 1200}, 3, 3, {0, 1, 2, 3, 4},
+       "Only skipped suggestions & URLs");
+
+  // URLs are bubbled above a search suggestion if 2 conditions are met:
+  // 1) There must be a sufficient score gap between the adjacent searches. E.g.
+  // for (S1, U1, S2), the difference in scores of S1 and S2 must be larger than
+  // some threshold.
+  // 2) There must be a sufficient buffer between the URL and search scores.
+  // This only applies to the first URL suggestion in a group. E.g. for
+  // (S1, U1, U2, S2, U3, S3), U1 & U3 must score higher than S2 + threshold &
+  // S3 + threshold respectively, but U2 need only score higher than S2.
+  test({600, 400, /*URL*/ 500}, 0, 2, {0, 2, 1}, "Bubble 1 URL");
+  test({599, 400, /*URL*/ 500}, 0, 2, {0, 1, 2}, "Insufficient gap");
+  test({600, 400, /*URL*/ 499}, 0, 2, {0, 1, 2}, "Insufficient buffer");
+
+  // No buffer is necessary for subsequent URLs in a group, but is necessary
+  // for the 1st URL of each group.
+  test({600, 400, 200, /*URL group 1*/ 500, 450, /*URL group 2*/ 300, 250}, 0,
+       3, {0, 3, 4, 1, 5, 6, 2}, "Bubble 2 URL groups");
+  test({600, 400, 200, /*URL group 1*/ 500, 450, /*URL group 2*/ 299, 250}, 0,
+       3, {0, 3, 4, 1, 2, 5, 6},
+       "Bubble 1st of 2 URL groups; insufficient buffer for 2nd group");
+  test({600, 399, 200, /*URL group 1*/ 500, 450, /*URL group 2*/ 300, 250}, 0,
+       3, {0, 3, 4, 1, 2, 5, 6},
+       "Bubble 1st of 2 URL groups; insufficient gap for 2nd group");
+  test({600, 400, 200, /*URL group 1*/ 499, 450, /*URL group 2*/ 300, 250}, 0,
+       3, {0, 1, 3, 4, 5, 6, 2},
+       "Bubble 2nd of 2 URL groups; insufficient buffer for 1st group");
+  test({599, 400, 200, /*URL group 1*/ 500, 450, /*URL group 2*/ 300, 250}, 0,
+       3, {0, 1, 3, 4, 5, 6, 2},
+       "Bubble 2nd of 2 URL groups; insufficient gap for 1st group");
+
+  // No gap is necessary when bubbling into the top position.
+  test({600, /*URLs*/ 700, 650}, 0, 1, {1, 2, 0},
+       "Bubble 1 URL group into top position");
+  test({600, /*URL*/ 650}, 0, 1, {0, 1},
+       "Insufficient buffer for top position");
+
+  // Skipped suggestions (e.g. default or clipboard suggestions) should not
+  // affect ordering.
+  test({/*skipped*/ 900, /*search*/ 600, /*URL*/ 700}, 1, 2, {0, 2, 1},
+       "Skipped suggestion");
+  test({/*skipped*/ 600, /*search*/ 600, /*URL*/ 700}, 1, 2, {0, 2, 1},
+       "Skipped suggestion should not affect gap");
+}
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index ad102fe..0874807a 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -947,6 +947,15 @@
 const char OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam[] =
     "OmniboxDynamicMaxAutocompleteIncreasedLimit";
 
+const char OmniboxFieldTrial::kBubbleUrlSuggestionsAbsoluteGapParam[] =
+    "OmniboxBubbleUrlSuggestionsAbsoluteGap";
+const char OmniboxFieldTrial::kBubbleUrlSuggestionsRelativeGapParam[] =
+    "OmniboxBubbleUrlSuggestionsRelativeGap";
+const char OmniboxFieldTrial::kBubbleUrlSuggestionsAbsoluteBufferParam[] =
+    "OmniboxBubbleUrlSuggestionsAbsoluteBuffer";
+const char OmniboxFieldTrial::kBubbleUrlSuggestionsRelativeBufferParam[] =
+    "OmniboxBubbleUrlSuggestionsRelativeBuffer";
+
 const char OmniboxFieldTrial::kOnDeviceHeadModelLocaleConstraint[] =
     "ForceModelLocaleConstraint";
 const char OmniboxFieldTrial::kOnDeviceHeadSuggestMaxScoreForNonUrlInput[] =
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index e93b2f3..8252ed3 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -522,6 +522,12 @@
 extern const char kDynamicMaxAutocompleteUrlCutoffParam[];
 extern const char kDynamicMaxAutocompleteIncreasedLimitParam[];
 
+// Parameters used for ranking.
+extern const char kBubbleUrlSuggestionsAbsoluteGapParam[];
+extern const char kBubbleUrlSuggestionsRelativeGapParam[];
+extern const char kBubbleUrlSuggestionsAbsoluteBufferParam[];
+extern const char kBubbleUrlSuggestionsRelativeBufferParam[];
+
 // Parameter names used by on device head provider.
 // These four parameters are shared by both non-incognito and incognito.
 extern const char kOnDeviceHeadModelLocaleConstraint[];
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 68e1a00..39787dba 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -203,6 +203,13 @@
 const base::Feature kDynamicMaxAutocomplete{"OmniboxDynamicMaxAutocomplete",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Feature used to enable bubbling URL suggestions above search suggestions
+// after grouping if 2 conditions are met:
+// 1) There must be a sufficient score gap between the adjacent searches.
+// 2) There must be a sufficient buffer between the URL and search scores.
+const base::Feature kBubbleUrlSuggestions{"OmniboxBubbleUrlSuggestions",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, when the user clears the whole omnibox text (i.e. via Backspace),
 // Chrome will request remote ZeroSuggest suggestions for the OTHER page
 // classification (contextual web).
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 20a0d26..d47a5d19 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -46,7 +46,7 @@
 
 // Num suggestions - these affect how many suggestions are shown based on e.g.
 // focus, page context, provider, or URL v non-URL.
-// Note that all of these are overriden and default values used instead if
+// Note that all of these are overridden and default values used instead if
 // kNewSearchFeatures is disabled.
 extern const base::Feature kMaxZeroSuggestMatches;
 extern const base::Feature kUIExperimentMaxAutocompleteMatches;
@@ -56,6 +56,9 @@
 extern const base::Feature kOmniboxMaxURLMatches;
 extern const base::Feature kDynamicMaxAutocomplete;
 
+// Ranking
+extern const base::Feature kBubbleUrlSuggestions;
+
 // On-Focus Suggestions a.k.a. ZeroSuggest.
 extern const base::Feature kClobberTriggersContextualWebZeroSuggest;
 extern const base::Feature kOmniboxLocalZeroSuggestAgeThreshold;
diff --git a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index b066dd9..1a317fa7 100644
--- a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -12,7 +12,7 @@
 // (or highly likely be) rare, e.g. <1% of page views as measured by UMA.
 //
 // UKM-based UseCounter should be used to cover the case when UMA UseCounter
-// data shows a behaviour that is rare but too common to bindly change.
+// data shows a behaviour that is rare but too common to blindly change.
 // UKM-based UseCounter would allow use to find specific pages to reason about
 // either a breaking change is acceptable or not.
 
@@ -179,6 +179,7 @@
           WebFeature::kSchemelesslySameSitePostMessage,
           WebFeature::kSchemelesslySameSitePostMessageSecureToInsecure,
           WebFeature::kSchemelesslySameSitePostMessageInsecureToSecure,
+          WebFeature::kElementAttachInternalsBeforeConstructor,
       }));
   return *opt_in_features;
 }
\ No newline at end of file
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 41e54fb..3fa26a7 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -2318,6 +2318,13 @@
  protected:
   LoginDatabaseUndecryptableLoginsTest() = default;
 
+ public:
+  LoginDatabaseUndecryptableLoginsTest(
+      const LoginDatabaseUndecryptableLoginsTest&) = delete;
+  LoginDatabaseUndecryptableLoginsTest& operator=(
+      const LoginDatabaseUndecryptableLoginsTest&) = delete;
+
+ protected:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     database_path_ = temp_dir_.GetPath().AppendASCII("test.db");
@@ -2346,8 +2353,6 @@
   base::ScopedTempDir temp_dir_;
   base::test::TaskEnvironment task_environment_;
   TestingPrefServiceSimple testing_local_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoginDatabaseUndecryptableLoginsTest);
 };
 
 PasswordForm LoginDatabaseUndecryptableLoginsTest::AddDummyLogin(
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 7293d5c..b19766a 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -121,6 +121,9 @@
  public:
   MockAutofillDownloadManager()
       : AutofillDownloadManager(nullptr, &fake_observer) {}
+  MockAutofillDownloadManager(const MockAutofillDownloadManager&) = delete;
+  MockAutofillDownloadManager& operator=(const MockAutofillDownloadManager&) =
+      delete;
 
   MOCK_METHOD6(StartUploadRequest,
                bool(const FormStructure&,
@@ -138,7 +141,6 @@
   };
 
   StubObserver fake_observer;
-  DISALLOW_COPY_AND_ASSIGN(MockAutofillDownloadManager);
 };
 
 class MockPasswordManagerClient : public StubPasswordManagerClient {
@@ -241,7 +243,8 @@
 class MockFormSaver : public StubFormSaver {
  public:
   MockFormSaver() = default;
-
+  MockFormSaver(const MockFormSaver&) = delete;
+  MockFormSaver& operator=(const MockFormSaver&) = delete;
   ~MockFormSaver() override = default;
 
   // FormSaver:
@@ -269,9 +272,6 @@
   static MockFormSaver& Get(PasswordFormManager* form_manager) {
     return *static_cast<MockFormSaver*>(form_manager->form_saver());
   }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockFormSaver);
 };
 
 class MockFieldInfoManager : public FieldInfoManager {
@@ -2369,6 +2369,8 @@
 class MockPasswordSaveManager : public PasswordSaveManager {
  public:
   MockPasswordSaveManager() = default;
+  MockPasswordSaveManager(const MockPasswordSaveManager&) = delete;
+  MockPasswordSaveManager& operator=(const MockPasswordSaveManager&) = delete;
   ~MockPasswordSaveManager() override = default;
   MOCK_METHOD4(Init,
                void(PasswordManagerClient*,
@@ -2407,14 +2409,15 @@
   MOCK_METHOD1(MoveCredentialsToAccountStore,
                void(metrics_util::MoveToAccountStoreTrigger));
   MOCK_METHOD1(BlockMovingToAccountStoreFor, void(const autofill::GaiaIdHash&));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockPasswordSaveManager);
 };
 
 class PasswordFormManagerTestWithMockedSaver : public PasswordFormManagerTest {
  public:
   PasswordFormManagerTestWithMockedSaver() = default;
+  PasswordFormManagerTestWithMockedSaver(
+      const PasswordFormManagerTestWithMockedSaver&) = delete;
+  PasswordFormManagerTestWithMockedSaver& operator=(
+      const PasswordFormManagerTestWithMockedSaver&) = delete;
 
   MockPasswordSaveManager* mock_password_save_manager() {
     return mock_password_save_manager_;
@@ -2449,7 +2452,6 @@
 
  private:
   NiceMock<MockPasswordSaveManager>* mock_password_save_manager_;
-  DISALLOW_COPY_AND_ASSIGN(PasswordFormManagerTestWithMockedSaver);
 };
 
 TEST_F(
@@ -2648,8 +2650,7 @@
 TEST_F(PasswordFormManagerTestWithMockedSaver, PresaveGeneratedPassword) {
   fetcher_->NotifyFetchCompleted();
   EXPECT_FALSE(form_manager_->HasGeneratedPassword());
-  form_manager_->SetGenerationPopupWasShown(false /* is_manual_generation
-  */);
+  form_manager_->SetGenerationPopupWasShown(/*is_manual_generation=*/false);
   PasswordForm form_with_generated_password = parsed_submitted_form_;
   FormData& form_data = form_with_generated_password.form_data;
   // Check that the generated password is forwarded to the save manager.
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index f4a3b27..55833cea4 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -278,11 +278,8 @@
           blink::mojom::FrameWidgetHostInterfaceBase>(),
       blink::CrossVariantMojoAssociatedReceiver<
           blink::mojom::FrameWidgetInterfaceBase>(),
-      // TODO(dtapuska): https://crbug.com/1085031. Have the suffix ForTesting
-      // removed.
-      blink_widget_host_receiver_
-          .BindNewEndpointAndPassDedicatedRemoteForTesting(),
-      blink_widget_.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      blink_widget_host_receiver_.BindNewEndpointAndPassDedicatedRemote(),
+      blink_widget_.BindNewEndpointAndPassDedicatedReceiver());
 
   // The WebFrame created here was already attached to the Page as its main
   // frame, and the WebFrameWidget has been initialized, so we can call
diff --git a/components/policy/android/BUILD.gn b/components/policy/android/BUILD.gn
index 111aabe..5c7251c 100644
--- a/components/policy/android/BUILD.gn
+++ b/components/policy/android/BUILD.gn
@@ -5,9 +5,9 @@
 import("//build/config/android/rules.gni")
 
 _jni_sources = [
-  "java/src/org/chromium/policy/CombinedPolicyProvider.java",
-  "java/src/org/chromium/policy/PolicyConverter.java",
-  "java/src/org/chromium/policy/PolicyService.java",
+  "java/src/org/chromium/components/policy/CombinedPolicyProvider.java",
+  "java/src/org/chromium/components/policy/PolicyConverter.java",
+  "java/src/org/chromium/components/policy/PolicyService.java",
 ]
 
 java_cpp_strings("java_switches_srcjar") {
@@ -26,12 +26,14 @@
   srcjar_deps = [ ":java_switches_srcjar" ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
-  sources =
-      _jni_sources + [
-        "java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java",
-        "java/src/org/chromium/policy/AppRestrictionsProvider.java",
-        "java/src/org/chromium/policy/PolicyProvider.java",
-      ]
+  sources = _jni_sources + [
+              "java/src/org/chromium/policy/AppRestrictionsProvider.java",
+              "java/src/org/chromium/policy/CombinedPolicyProvider.java",
+              "java/src/org/chromium/policy/PolicyProvider.java",
+              "java/src/org/chromium/components/policy/AbstractAppRestrictionsProvider.java",
+              "java/src/org/chromium/components/policy/AppRestrictionsProvider.java",
+              "java/src/org/chromium/components/policy/PolicyProvider.java",
+            ]
 }
 
 android_library("policy_java_test_support") {
@@ -44,8 +46,8 @@
     "//third_party/junit",
   ]
   sources = [
-    "javatests/src/org/chromium/policy/test/PolicyData.java",
-    "javatests/src/org/chromium/policy/test/annotations/Policies.java",
+    "javatests/src/org/chromium/components/policy/test/PolicyData.java",
+    "javatests/src/org/chromium/components/policy/test/annotations/Policies.java",
   ]
 }
 
@@ -59,10 +61,10 @@
   bypass_platform_checks = true
   testonly = true
   sources = [
-    "junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java",
-    "junit/src/org/chromium/policy/CombinedPolicyProviderTest.java",
-    "junit/src/org/chromium/policy/PolicyConverterTest.java",
-    "junit/src/org/chromium/policy/test/annotations/PoliciesTest.java",
+    "junit/src/org/chromium/components/policy/AbstractAppRestrictionsProviderTest.java",
+    "junit/src/org/chromium/components/policy/CombinedPolicyProviderTest.java",
+    "junit/src/org/chromium/components/policy/PolicyConverterTest.java",
+    "junit/src/org/chromium/components/policy/test/annotations/PoliciesTest.java",
   ]
   deps = [
     ":policy_java",
@@ -77,8 +79,7 @@
   ]
 }
 
-_test_jni_sources =
-    [ "javatests/src/org/chromium/policy/test/PolicyServiceTestSupporter.java" ]
+_test_jni_sources = [ "javatests/src/org/chromium/components/policy/test/PolicyServiceTestSupporter.java" ]
 
 android_library("native_test_support_java") {
   testonly = true
diff --git a/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java b/components/policy/android/java/src/org/chromium/components/policy/AbstractAppRestrictionsProvider.java
similarity index 98%
rename from components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java
rename to components/policy/android/java/src/org/chromium/components/policy/AbstractAppRestrictionsProvider.java
index f076c81..ce6bbb20 100644
--- a/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java
+++ b/components/policy/android/java/src/org/chromium/components/policy/AbstractAppRestrictionsProvider.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
diff --git a/components/policy/android/java/src/org/chromium/components/policy/AppRestrictionsProvider.java b/components/policy/android/java/src/org/chromium/components/policy/AppRestrictionsProvider.java
new file mode 100644
index 0000000..103d37d
--- /dev/null
+++ b/components/policy/android/java/src/org/chromium/components/policy/AppRestrictionsProvider.java
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.policy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.UserManager;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+/**
+ * Concrete app restriction provider, that uses the default android mechanism to retrieve the
+ * restrictions.
+ */
+public class AppRestrictionsProvider extends AbstractAppRestrictionsProvider {
+    /**
+     * Get the app restriction information from provided user manager, and record some timing
+     * metrics on its runtime.
+     * @param userManager UserManager service from Android System service
+     * @param packageName package name for target application.
+     * @return The restrictions for the provided package name, an empty bundle if they are not
+     *         available.
+     */
+    public static Bundle getApplicationRestrictionsFromUserManager(
+            UserManager userManager, String packageName) {
+        try {
+            return userManager.getApplicationRestrictions(packageName);
+        } catch (SecurityException e) {
+            // Android bug may throw SecurityException. See crbug.com/886814.
+            return new Bundle();
+        }
+    }
+
+    private final UserManager mUserManager;
+
+    public AppRestrictionsProvider(Context context) {
+        super(context);
+
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    protected Bundle getApplicationRestrictions(String packageName) {
+        long startTime = SystemClock.elapsedRealtime();
+        Bundle bundle = getApplicationRestrictionsFromUserManager(mUserManager, packageName);
+        long endTime = SystemClock.elapsedRealtime();
+        long duration = endTime - startTime;
+        RecordHistogram.recordTimesHistogram("Enterprise.AppRestrictionLoadTime2", duration);
+        if (bundle.isEmpty()) {
+            RecordHistogram.recordTimesHistogram(
+                    "Enterprise.AppRestrictionLoadTime2.EmptyBundle", duration);
+        } else {
+            RecordHistogram.recordTimesHistogram(
+                    "Enterprise.AppRestrictionLoadTime2.NonEmptyBundle", duration);
+        }
+        return bundle;
+    }
+
+    @Override
+    protected String getRestrictionChangeIntentAction() {
+        return Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED;
+    }
+}
diff --git a/components/policy/android/java/src/org/chromium/components/policy/CombinedPolicyProvider.java b/components/policy/android/java/src/org/chromium/components/policy/CombinedPolicyProvider.java
new file mode 100644
index 0000000..e66463f
--- /dev/null
+++ b/components/policy/android/java/src/org/chromium/components/policy/CombinedPolicyProvider.java
@@ -0,0 +1,145 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.policy;
+
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads enterprise policies from one or more policy providers and plumbs them through to the policy
+ * subsystem.
+ */
+@JNINamespace("policy::android")
+public class CombinedPolicyProvider {
+    private static CombinedPolicyProvider sInstance;
+
+    private long mNativeCombinedPolicyProvider;
+
+    private PolicyConverter mPolicyConverter;
+    private final List<PolicyProvider> mPolicyProviders = new ArrayList<>();
+    private final List<Bundle> mCachedPolicies = new ArrayList<>();
+    private final List<PolicyChangeListener> mPolicyChangeListeners = new ArrayList<>();
+
+    public static CombinedPolicyProvider get() {
+        if (sInstance == null) {
+            sInstance = new CombinedPolicyProvider();
+        }
+        return sInstance;
+    }
+
+    private void linkNativeInternal(
+            long nativeCombinedPolicyProvider, PolicyConverter policyConverter) {
+        mNativeCombinedPolicyProvider = nativeCombinedPolicyProvider;
+        mPolicyConverter = policyConverter;
+        if (nativeCombinedPolicyProvider != 0) {
+            for (PolicyProvider provider : mPolicyProviders) {
+                provider.refresh();
+            }
+        }
+    }
+
+    @CalledByNative
+    public static CombinedPolicyProvider linkNative(
+            long nativeCombinedPolicyProvider, PolicyConverter policyConverter) {
+        ThreadUtils.assertOnUiThread();
+        get().linkNativeInternal(nativeCombinedPolicyProvider, policyConverter);
+        return get();
+    }
+
+    /**
+     * PolicyProviders are assigned a unique precedence based on their order of registration. Later
+     * Registration -> Higher Precedence. This precedence is also used as a 'source' tag for
+     * disambiguating updates.
+     */
+    public void registerProvider(PolicyProvider provider) {
+        mPolicyProviders.add(provider);
+        mCachedPolicies.add(null);
+        provider.setManagerAndSource(this, mPolicyProviders.size() - 1);
+        if (mNativeCombinedPolicyProvider != 0) provider.refresh();
+    }
+
+    public void destroy() {
+        // All the activities registered should have been destroyed by then.
+        assert mPolicyChangeListeners.isEmpty();
+
+        for (PolicyProvider provider : mPolicyProviders) {
+            provider.destroy();
+        }
+        mPolicyProviders.clear();
+        mPolicyConverter = null;
+    }
+
+    void onSettingsAvailable(int source, Bundle newSettings) {
+        mCachedPolicies.set(source, newSettings);
+        // Check if we have policies from all the providers before applying them.
+        for (Bundle settings : mCachedPolicies) {
+            if (settings == null) return;
+        }
+
+        if (mNativeCombinedPolicyProvider == 0) return;
+
+        for (Bundle settings : mCachedPolicies) {
+            for (String key : settings.keySet()) {
+                mPolicyConverter.setPolicy(key, settings.get(key));
+            }
+        }
+        CombinedPolicyProviderJni.get().flushPolicies(mNativeCombinedPolicyProvider, get());
+    }
+
+    void terminateIncognitoSession() {
+        for (PolicyChangeListener listener : mPolicyChangeListeners) {
+            listener.terminateIncognitoSession();
+        }
+    }
+
+    public void addPolicyChangeListener(PolicyChangeListener listener) {
+        mPolicyChangeListeners.add(listener);
+    }
+
+    public void removePolicyChangeListener(PolicyChangeListener listener) {
+        assert mPolicyChangeListeners.contains(listener);
+        mPolicyChangeListeners.remove(listener);
+    }
+
+    @VisibleForTesting
+    @CalledByNative
+    void refreshPolicies() {
+        assert mPolicyProviders.size() == mCachedPolicies.size();
+        for (int i = 0; i < mCachedPolicies.size(); ++i) {
+            mCachedPolicies.set(i, null);
+        }
+        for (PolicyProvider provider : mPolicyProviders) {
+            provider.refresh();
+        }
+    }
+
+    /**
+     * Interface to handle actions related with policy changes.
+     */
+    public interface PolicyChangeListener {
+        /**
+         * Call to notify the listener that incognito browsing is unavailable due to policy.
+         */
+        void terminateIncognitoSession();
+    }
+
+    static void setForTesting(CombinedPolicyProvider p) {
+        sInstance = p;
+    }
+
+    @NativeMethods
+    interface Natives {
+        void flushPolicies(long nativeAndroidCombinedPolicyProvider, CombinedPolicyProvider caller);
+    }
+}
diff --git a/components/policy/android/java/src/org/chromium/policy/PolicyConverter.java b/components/policy/android/java/src/org/chromium/components/policy/PolicyConverter.java
similarity index 99%
rename from components/policy/android/java/src/org/chromium/policy/PolicyConverter.java
rename to components/policy/android/java/src/org/chromium/components/policy/PolicyConverter.java
index 957b709f..032a5f8 100644
--- a/components/policy/android/java/src/org/chromium/policy/PolicyConverter.java
+++ b/components/policy/android/java/src/org/chromium/components/policy/PolicyConverter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import android.os.Bundle;
 
diff --git a/components/policy/android/java/src/org/chromium/components/policy/PolicyProvider.java b/components/policy/android/java/src/org/chromium/components/policy/PolicyProvider.java
new file mode 100644
index 0000000..bdbbf8f
--- /dev/null
+++ b/components/policy/android/java/src/org/chromium/components/policy/PolicyProvider.java
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.policy;
+
+import android.os.Bundle;
+
+import org.chromium.base.ThreadUtils;
+
+/**
+ * Base class for Policy providers.
+ */
+public abstract class PolicyProvider {
+    private CombinedPolicyProvider mCombinedPolicyProvider;
+    private int mSource = -1;
+
+    protected PolicyProvider() {}
+
+    public void notifySettingsAvailable(Bundle settings) {
+        ThreadUtils.assertOnUiThread();
+        mCombinedPolicyProvider.onSettingsAvailable(mSource, settings);
+    }
+
+    protected void terminateIncognitoSession() {
+        mCombinedPolicyProvider.terminateIncognitoSession();
+    }
+
+    /**
+     * Called to request a refreshed set of policies. This method must handle notifying the
+     * CombinedPolicyProvider whenever applicable.
+     */
+    public abstract void refresh();
+
+    /**
+     * Register the PolicyProvider for receiving policy changes.
+     */
+    protected void startListeningForPolicyChanges() {}
+
+    /**
+     * Called by the {@link CombinedPolicyProvider} to correctly hook it with the Policy system.
+     *
+     * @param combinedPolicyProvider reference to the CombinedPolicyProvider to be used like a
+     *            delegate.
+     * @param source tags the PolicyProvider with a source.
+     */
+    final void setManagerAndSource(CombinedPolicyProvider combinedPolicyProvider, int source) {
+        assert mSource < 0;
+        assert source >= 0;
+        mSource = source;
+        assert mCombinedPolicyProvider == null;
+        mCombinedPolicyProvider = combinedPolicyProvider;
+        startListeningForPolicyChanges();
+    }
+
+    /** Called when the provider is unregistered */
+    public void destroy() {}
+}
diff --git a/components/policy/android/java/src/org/chromium/policy/PolicyService.java b/components/policy/android/java/src/org/chromium/components/policy/PolicyService.java
similarity index 98%
rename from components/policy/android/java/src/org/chromium/policy/PolicyService.java
rename to components/policy/android/java/src/org/chromium/components/policy/PolicyService.java
index 64d099bf..71390cf 100644
--- a/components/policy/android/java/src/org/chromium/policy/PolicyService.java
+++ b/components/policy/android/java/src/org/chromium/components/policy/PolicyService.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.annotations.CalledByNative;
diff --git a/components/policy/android/java/src/org/chromium/policy/AppRestrictionsProvider.java b/components/policy/android/java/src/org/chromium/policy/AppRestrictionsProvider.java
index 70ff4f5..ab14e31 100644
--- a/components/policy/android/java/src/org/chromium/policy/AppRestrictionsProvider.java
+++ b/components/policy/android/java/src/org/chromium/policy/AppRestrictionsProvider.java
@@ -5,63 +5,15 @@
 package org.chromium.policy;
 
 import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.os.UserManager;
-
-import org.chromium.base.metrics.RecordHistogram;
 
 /**
  * Concrete app restriction provider, that uses the default android mechanism to retrieve the
  * restrictions.
+ * TODO(zmin): Delete once the internal code is migrated.
  */
-public class AppRestrictionsProvider extends AbstractAppRestrictionsProvider {
-    /**
-     * Get the app restriction information from provided user manager, and record some timing
-     * metrics on its runtime.
-     * @param userManager UserManager service from Android System service
-     * @param packageName package name for target application.
-     * @return The restrictions for the provided package name, an empty bundle if they are not
-     *         available.
-     */
-    public static Bundle getApplicationRestrictionsFromUserManager(
-            UserManager userManager, String packageName) {
-        try {
-            return userManager.getApplicationRestrictions(packageName);
-        } catch (SecurityException e) {
-            // Android bug may throw SecurityException. See crbug.com/886814.
-            return new Bundle();
-        }
-    }
-
-    private final UserManager mUserManager;
-
+public class AppRestrictionsProvider
+        extends org.chromium.components.policy.AppRestrictionsProvider {
     public AppRestrictionsProvider(Context context) {
         super(context);
-
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-    }
-
-    @Override
-    protected Bundle getApplicationRestrictions(String packageName) {
-        long startTime = SystemClock.elapsedRealtime();
-        Bundle bundle = getApplicationRestrictionsFromUserManager(mUserManager, packageName);
-        long endTime = SystemClock.elapsedRealtime();
-        long duration = endTime - startTime;
-        RecordHistogram.recordTimesHistogram("Enterprise.AppRestrictionLoadTime2", duration);
-        if (bundle.isEmpty()) {
-            RecordHistogram.recordTimesHistogram(
-                    "Enterprise.AppRestrictionLoadTime2.EmptyBundle", duration);
-        } else {
-            RecordHistogram.recordTimesHistogram(
-                    "Enterprise.AppRestrictionLoadTime2.NonEmptyBundle", duration);
-        }
-        return bundle;
-    }
-
-    @Override
-    protected String getRestrictionChangeIntentAction() {
-        return Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED;
     }
 }
diff --git a/components/policy/android/java/src/org/chromium/policy/CombinedPolicyProvider.java b/components/policy/android/java/src/org/chromium/policy/CombinedPolicyProvider.java
index 9ffb5706..7294fc8 100644
--- a/components/policy/android/java/src/org/chromium/policy/CombinedPolicyProvider.java
+++ b/components/policy/android/java/src/org/chromium/policy/CombinedPolicyProvider.java
@@ -4,142 +4,10 @@
 
 package org.chromium.policy;
 
-import android.os.Bundle;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
-
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Reads enterprise policies from one or more policy providers and plumbs them through to the policy
  * subsystem.
+ *
+ * TODO(zmin): Delete once the internal code is migrated.
  */
-@JNINamespace("policy::android")
-public class CombinedPolicyProvider {
-    private static CombinedPolicyProvider sInstance;
-
-    private long mNativeCombinedPolicyProvider;
-
-    private PolicyConverter mPolicyConverter;
-    private final List<PolicyProvider> mPolicyProviders = new ArrayList<>();
-    private final List<Bundle> mCachedPolicies = new ArrayList<>();
-    private final List<PolicyChangeListener> mPolicyChangeListeners = new ArrayList<>();
-
-    public static CombinedPolicyProvider get() {
-        if (sInstance == null) {
-            sInstance = new CombinedPolicyProvider();
-        }
-        return sInstance;
-    }
-
-    private void linkNativeInternal(
-            long nativeCombinedPolicyProvider, PolicyConverter policyConverter) {
-        mNativeCombinedPolicyProvider = nativeCombinedPolicyProvider;
-        mPolicyConverter = policyConverter;
-        if (nativeCombinedPolicyProvider != 0) {
-            for (PolicyProvider provider : mPolicyProviders) {
-                provider.refresh();
-            }
-        }
-    }
-
-    @CalledByNative
-    public static CombinedPolicyProvider linkNative(
-            long nativeCombinedPolicyProvider, PolicyConverter policyConverter) {
-        ThreadUtils.assertOnUiThread();
-        get().linkNativeInternal(nativeCombinedPolicyProvider, policyConverter);
-        return get();
-    }
-
-    /**
-     * PolicyProviders are assigned a unique precedence based on their order of registration. Later
-     * Registration -> Higher Precedence. This precedence is also used as a 'source' tag for
-     * disambiguating updates.
-     */
-    public void registerProvider(PolicyProvider provider) {
-        mPolicyProviders.add(provider);
-        mCachedPolicies.add(null);
-        provider.setManagerAndSource(this, mPolicyProviders.size() - 1);
-        if (mNativeCombinedPolicyProvider != 0) provider.refresh();
-    }
-
-    public void destroy() {
-        // All the activities registered should have been destroyed by then.
-        assert mPolicyChangeListeners.isEmpty();
-
-        for (PolicyProvider provider : mPolicyProviders) {
-            provider.destroy();
-        }
-        mPolicyProviders.clear();
-        mPolicyConverter = null;
-    }
-
-    void onSettingsAvailable(int source, Bundle newSettings) {
-        mCachedPolicies.set(source, newSettings);
-        // Check if we have policies from all the providers before applying them.
-        for (Bundle settings : mCachedPolicies) {
-            if (settings == null) return;
-        }
-
-        if (mNativeCombinedPolicyProvider == 0) return;
-
-        for (Bundle settings : mCachedPolicies) {
-            for (String key : settings.keySet()) {
-                mPolicyConverter.setPolicy(key, settings.get(key));
-            }
-        }
-        CombinedPolicyProviderJni.get().flushPolicies(mNativeCombinedPolicyProvider, get());
-    }
-
-    void terminateIncognitoSession() {
-        for (PolicyChangeListener listener : mPolicyChangeListeners) {
-            listener.terminateIncognitoSession();
-        }
-    }
-
-    public void addPolicyChangeListener(PolicyChangeListener listener) {
-        mPolicyChangeListeners.add(listener);
-    }
-
-    public void removePolicyChangeListener(PolicyChangeListener listener) {
-        assert mPolicyChangeListeners.contains(listener);
-        mPolicyChangeListeners.remove(listener);
-    }
-
-    @VisibleForTesting
-    @CalledByNative
-    void refreshPolicies() {
-        assert mPolicyProviders.size() == mCachedPolicies.size();
-        for (int i = 0; i < mCachedPolicies.size(); ++i) {
-            mCachedPolicies.set(i, null);
-        }
-        for (PolicyProvider provider : mPolicyProviders) {
-            provider.refresh();
-        }
-    }
-
-    /**
-     * Interface to handle actions related with policy changes.
-     */
-    public interface PolicyChangeListener {
-        /**
-         * Call to notify the listener that incognito browsing is unavailable due to policy.
-         */
-        void terminateIncognitoSession();
-    }
-
-    static void setForTesting(CombinedPolicyProvider p) {
-        sInstance = p;
-    }
-
-    @NativeMethods
-    interface Natives {
-        void flushPolicies(long nativeAndroidCombinedPolicyProvider, CombinedPolicyProvider caller);
-    }
-}
+public class CombinedPolicyProvider extends org.chromium.components.policy.CombinedPolicyProvider {}
diff --git a/components/policy/android/java/src/org/chromium/policy/PolicyProvider.java b/components/policy/android/java/src/org/chromium/policy/PolicyProvider.java
index a626dd7..1df6db7 100644
--- a/components/policy/android/java/src/org/chromium/policy/PolicyProvider.java
+++ b/components/policy/android/java/src/org/chromium/policy/PolicyProvider.java
@@ -4,55 +4,8 @@
 
 package org.chromium.policy;
 
-import android.os.Bundle;
-
-import org.chromium.base.ThreadUtils;
-
 /**
  * Base class for Policy providers.
+ * TODO(zmin): Delete once the internal code is migrated.
  */
-public abstract class PolicyProvider {
-    private CombinedPolicyProvider mCombinedPolicyProvider;
-    private int mSource = -1;
-
-    protected PolicyProvider() {}
-
-    public void notifySettingsAvailable(Bundle settings) {
-        ThreadUtils.assertOnUiThread();
-        mCombinedPolicyProvider.onSettingsAvailable(mSource, settings);
-    }
-
-    protected void terminateIncognitoSession() {
-        mCombinedPolicyProvider.terminateIncognitoSession();
-    }
-
-    /**
-     * Called to request a refreshed set of policies. This method must handle notifying the
-     * CombinedPolicyProvider whenever applicable.
-     */
-    public abstract void refresh();
-
-    /**
-     * Register the PolicyProvider for receiving policy changes.
-     */
-    protected void startListeningForPolicyChanges() {}
-
-    /**
-     * Called by the {@link CombinedPolicyProvider} to correctly hook it with the Policy system.
-     *
-     * @param combinedPolicyProvider reference to the CombinedPolicyProvider to be used like a
-     *            delegate.
-     * @param source tags the PolicyProvider with a source.
-     */
-    final void setManagerAndSource(CombinedPolicyProvider combinedPolicyProvider, int source) {
-        assert mSource < 0;
-        assert source >= 0;
-        mSource = source;
-        assert mCombinedPolicyProvider == null;
-        mCombinedPolicyProvider = combinedPolicyProvider;
-        startListeningForPolicyChanges();
-    }
-
-    /** Called when the provider is unregistered */
-    public void destroy() {}
-}
+public abstract class PolicyProvider extends org.chromium.components.policy.PolicyProvider {}
diff --git a/components/policy/android/java_templates/PolicySwitches.java.tmpl b/components/policy/android/java_templates/PolicySwitches.java.tmpl
index 0c49a772..621e6f6 100644
--- a/components/policy/android/java_templates/PolicySwitches.java.tmpl
+++ b/components/policy/android/java_templates/PolicySwitches.java.tmpl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 /**
  * Contains command line switches that are specific to the policy component.
diff --git a/components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyData.java
similarity index 98%
rename from components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java
rename to components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyData.java
index 5a1fa70..57a6e9e 100644
--- a/components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java
+++ b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyData.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy.test;
+package org.chromium.components.policy.test;
 
 import android.os.Bundle;
 
diff --git a/components/policy/android/javatests/src/org/chromium/policy/test/PolicyServiceTestSupporter.java b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyServiceTestSupporter.java
similarity index 94%
rename from components/policy/android/javatests/src/org/chromium/policy/test/PolicyServiceTestSupporter.java
rename to components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyServiceTestSupporter.java
index 0da9713..4f9021fed 100644
--- a/components/policy/android/javatests/src/org/chromium/policy/test/PolicyServiceTestSupporter.java
+++ b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyServiceTestSupporter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy.test;
+package org.chromium.components.policy.test;
 
 import static org.mockito.Mockito.times;
 
@@ -11,7 +11,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
-import org.chromium.policy.PolicyService;
+import org.chromium.components.policy.PolicyService;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java b/components/policy/android/javatests/src/org/chromium/components/policy/test/annotations/Policies.java
similarity index 93%
rename from components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java
rename to components/policy/android/javatests/src/org/chromium/components/policy/test/annotations/Policies.java
index cc08192..bfe25cb9 100644
--- a/components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java
+++ b/components/policy/android/javatests/src/org/chromium/components/policy/test/annotations/Policies.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy.test.annotations;
+package org.chromium.components.policy.test.annotations;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -13,8 +13,8 @@
 import org.junit.runners.model.FrameworkMethod;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner.TestHook;
-import org.chromium.policy.AbstractAppRestrictionsProvider;
-import org.chromium.policy.test.PolicyData;
+import org.chromium.components.policy.AbstractAppRestrictionsProvider;
+import org.chromium.components.policy.test.PolicyData;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
@@ -117,9 +117,8 @@
         AnnotatedElement parent = (element instanceof Method)
                 ? ((Method) element).getDeclaringClass()
                 : ((Class<?>) element).getSuperclass();
-        Map<String, PolicyData> flags = (parent == null)
-                ? new HashMap<String, PolicyData>()
-                : getPolicies(parent);
+        Map<String, PolicyData> flags =
+                (parent == null) ? new HashMap<String, PolicyData>() : getPolicies(parent);
 
         if (element.isAnnotationPresent(Policies.Add.class)) {
             flags.putAll(fromItems(element.getAnnotation(Policies.Add.class).value()));
diff --git a/components/policy/android/junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java b/components/policy/android/junit/src/org/chromium/components/policy/AbstractAppRestrictionsProviderTest.java
similarity index 98%
rename from components/policy/android/junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java
rename to components/policy/android/junit/src/org/chromium/components/policy/AbstractAppRestrictionsProviderTest.java
index 90ccb81..bd2dfe8 100644
--- a/components/policy/android/junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java
+++ b/components/policy/android/junit/src/org/chromium/components/policy/AbstractAppRestrictionsProviderTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -37,7 +37,6 @@
      * Minimal concrete class implementing AbstractAppRestrictionsProvider.
      */
     private class DummyAppRestrictionsProvider extends AbstractAppRestrictionsProvider {
-
         public DummyAppRestrictionsProvider(Context context) {
             super(context);
         }
@@ -51,7 +50,6 @@
         protected String getRestrictionChangeIntentAction() {
             return null;
         }
-
     }
 
     /**
@@ -132,5 +130,4 @@
         provider.stopListening();
         Assert.assertFalse(shadowApplication.hasReceiverForIntent(intent));
     }
-
 }
diff --git a/components/policy/android/junit/src/org/chromium/policy/CombinedPolicyProviderTest.java b/components/policy/android/junit/src/org/chromium/components/policy/CombinedPolicyProviderTest.java
similarity index 99%
rename from components/policy/android/junit/src/org/chromium/policy/CombinedPolicyProviderTest.java
rename to components/policy/android/junit/src/org/chromium/components/policy/CombinedPolicyProviderTest.java
index e19bfd9..cbf86182 100644
--- a/components/policy/android/junit/src/org/chromium/policy/CombinedPolicyProviderTest.java
+++ b/components/policy/android/junit/src/org/chromium/components/policy/CombinedPolicyProviderTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
diff --git a/components/policy/android/junit/src/org/chromium/policy/PolicyConverterTest.java b/components/policy/android/junit/src/org/chromium/components/policy/PolicyConverterTest.java
similarity index 94%
rename from components/policy/android/junit/src/org/chromium/policy/PolicyConverterTest.java
rename to components/policy/android/junit/src/org/chromium/components/policy/PolicyConverterTest.java
index 5fddb6f..a76e27f4 100644
--- a/components/policy/android/junit/src/org/chromium/policy/PolicyConverterTest.java
+++ b/components/policy/android/junit/src/org/chromium/components/policy/PolicyConverterTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy;
+package org.chromium.components.policy;
 
 import static org.mockito.Mockito.verify;
 
@@ -40,7 +40,8 @@
 
     /**
      * Test method for
-     * {@link org.chromium.policy.PolicyConverter#setPolicy(java.lang.String, java.lang.Object)}.
+     * {@link org.chromium.components.policy.PolicyConverter#setPolicy(java.lang.String,
+     * java.lang.Object)}.
      */
     @Test
     public void testSetPolicy() {
diff --git a/components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java b/components/policy/android/junit/src/org/chromium/components/policy/test/annotations/PoliciesTest.java
similarity index 96%
rename from components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java
rename to components/policy/android/junit/src/org/chromium/components/policy/test/annotations/PoliciesTest.java
index 9cc56fe..879e0a0 100644
--- a/components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java
+++ b/components/policy/android/junit/src/org/chromium/components/policy/test/annotations/PoliciesTest.java
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.policy.test.annotations;
+package org.chromium.components.policy.test.annotations;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
-import org.chromium.policy.test.PolicyData;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
 
+import org.chromium.components.policy.test.PolicyData;
+
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.HashSet;
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 70051a1..d56d4eb3 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -64,16 +64,6 @@
     "//url",
   ]
 
-  if (is_android) {
-    sources += [
-      "android/android_combined_policy_provider.cc",
-      "android/android_combined_policy_provider.h",
-      "android/policy_converter.cc",
-      "android/policy_converter.h",
-    ]
-    deps += [ "//components/policy/android:jni_headers" ]
-  }
-
   public_deps += [ "//components/policy/core/common:internal" ]
   deps += [
     "//components/proxy_config",
@@ -118,8 +108,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "android/android_combined_policy_provider_unittest.cc",
-    "android/policy_converter_unittest.cc",
     "browser_policy_connector_unittest.cc",
     "configuration_policy_handler_list_unittest.cc",
     "configuration_policy_handler_unittest.cc",
diff --git a/components/policy/core/browser/android/DEPS b/components/policy/core/browser/android/DEPS
deleted file mode 100644
index 8e5f7d57..0000000
--- a/components/policy/core/browser/android/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+components/policy/android/jni_headers",
-]
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index e9fa1c0..b7c6b274 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -240,6 +240,10 @@
   }
   if (is_android) {
     sources += [
+      "android/android_combined_policy_provider.cc",
+      "android/android_combined_policy_provider.h",
+      "android/policy_converter.cc",
+      "android/policy_converter.h",
       "android/policy_service_android.cc",
       "android/policy_service_android.h",
     ]
@@ -449,7 +453,11 @@
   }
 
   if (is_android) {
-    sources += [ "android/policy_service_android_unittest.cc" ]
+    sources += [
+      "android/android_combined_policy_provider_unittest.cc",
+      "android/policy_converter_unittest.cc",
+      "android/policy_service_android_unittest.cc",
+    ]
   } else {
     sources += [ "async_policy_provider_unittest.cc" ]
   }
diff --git a/components/policy/core/browser/android/android_combined_policy_provider.cc b/components/policy/core/common/android/android_combined_policy_provider.cc
similarity index 92%
rename from components/policy/core/browser/android/android_combined_policy_provider.cc
rename to components/policy/core/common/android/android_combined_policy_provider.cc
index 316a8ef5..ddfcd4ee 100644
--- a/components/policy/core/browser/android/android_combined_policy_provider.cc
+++ b/components/policy/core/common/android/android_combined_policy_provider.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/policy/core/browser/android/android_combined_policy_provider.h"
+#include "components/policy/core/common/android/android_combined_policy_provider.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "components/policy/android/jni_headers/CombinedPolicyProvider_jni.h"
-#include "components/policy/core/browser/android/policy_converter.h"
+#include "components/policy/core/common/android/policy_converter.h"
 
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
diff --git a/components/policy/core/browser/android/android_combined_policy_provider.h b/components/policy/core/common/android/android_combined_policy_provider.h
similarity index 87%
rename from components/policy/core/browser/android/android_combined_policy_provider.h
rename to components/policy/core/common/android/android_combined_policy_provider.h
index 8bbdfc1..bafa14a 100644
--- a/components/policy/core/browser/android/android_combined_policy_provider.h
+++ b/components/policy/core/common/android/android_combined_policy_provider.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 COMPONENTS_POLICY_CORE_BROWSER_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
-#define COMPONENTS_POLICY_CORE_BROWSER_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
+#ifndef COMPONENTS_POLICY_CORE_COMMON_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
 
 #include <jni.h>
 
@@ -55,7 +55,6 @@
   std::unique_ptr<policy::android::PolicyConverter> policy_converter_;
   base::android::ScopedJavaGlobalRef<jobject> java_combined_policy_provider_;
 
-
   DISALLOW_COPY_AND_ASSIGN(AndroidCombinedPolicyProvider);
 };
 
@@ -63,4 +62,4 @@
 
 }  // namespace policy
 
-#endif  // COMPONENTS_POLICY_CORE_BROWSER_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
+#endif  // COMPONENTS_POLICY_CORE_COMMON_ANDROID_ANDROID_COMBINED_POLICY_PROVIDER_H_
diff --git a/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc b/components/policy/core/common/android/android_combined_policy_provider_unittest.cc
similarity index 95%
rename from components/policy/core/browser/android/android_combined_policy_provider_unittest.cc
rename to components/policy/core/common/android/android_combined_policy_provider_unittest.cc
index 87d45e9..1cee5bc 100644
--- a/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc
+++ b/components/policy/core/common/android/android_combined_policy_provider_unittest.cc
@@ -8,15 +8,15 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/values.h"
-#include "components/policy/core/browser/android/android_combined_policy_provider.h"
-#include "components/policy/core/browser/android/policy_converter.h"
+#include "components/policy/core/common/android/android_combined_policy_provider.h"
+#include "components/policy/core/common/android/policy_converter.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/core/common/schema.h"
 #include "components/policy/core/common/schema_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::android::ConvertUTF8ToJavaString;
 using base::android::AttachCurrentThread;
+using base::android::ConvertUTF8ToJavaString;
 using base::android::ScopedJavaLocalRef;
 
 namespace policy {
diff --git a/components/policy/core/browser/android/policy_converter.cc b/components/policy/core/common/android/policy_converter.cc
similarity index 98%
rename from components/policy/core/browser/android/policy_converter.cc
rename to components/policy/core/common/android/policy_converter.cc
index 18dd904..566eb42f 100644
--- a/components/policy/core/browser/android/policy_converter.cc
+++ b/components/policy/core/common/android/policy_converter.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 "components/policy/core/browser/android/policy_converter.h"
+#include "components/policy/core/common/android/policy_converter.h"
 
 #include <utility>
 #include <vector>
diff --git a/components/policy/core/browser/android/policy_converter.h b/components/policy/core/common/android/policy_converter.h
similarity index 93%
rename from components/policy/core/browser/android/policy_converter.h
rename to components/policy/core/common/android/policy_converter.h
index 3d6bebe..e4473a8c 100644
--- a/components/policy/core/browser/android/policy_converter.h
+++ b/components/policy/core/common/android/policy_converter.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 COMPONENTS_POLICY_CORE_BROWSER_ANDROID_POLICY_CONVERTER_H
-#define COMPONENTS_POLICY_CORE_BROWSER_ANDROID_POLICY_CONVERTER_H
+#ifndef COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_CONVERTER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_CONVERTER_H_
 
 #include <jni.h>
 
@@ -89,4 +89,4 @@
 }  // namespace android
 }  // namespace policy
 
-#endif  // COMPONENTS_POLICY_CORE_BROWSER_ANDROID_POLICY_CONVERTER_H
+#endif  // COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_CONVERTER_H_
diff --git a/components/policy/core/browser/android/policy_converter_unittest.cc b/components/policy/core/common/android/policy_converter_unittest.cc
similarity index 98%
rename from components/policy/core/browser/android/policy_converter_unittest.cc
rename to components/policy/core/common/android/policy_converter_unittest.cc
index 4c0487ff..6d76986 100644
--- a/components/policy/core/browser/android/policy_converter_unittest.cc
+++ b/components/policy/core/common/android/policy_converter_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/android/jni_string.h"
 #include "base/json/json_writer.h"
 #include "base/values.h"
-#include "components/policy/core/browser/android/policy_converter.h"
+#include "components/policy/core/common/android/policy_converter.h"
 #include "components/policy/core/common/schema.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index ed14aaf..3aa5aa8 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -167,7 +167,7 @@
   ~FakePrintPreviewUI() override = default;
 
   mojo::PendingAssociatedRemote<mojom::PrintPreviewUI> BindReceiver() {
-    return receiver_.BindNewEndpointAndPassDedicatedRemoteForTesting();
+    return receiver_.BindNewEndpointAndPassDedicatedRemote();
   }
   void SetQuitClosure(base::OnceClosure quit_closure) {
     quit_closure_ = std::move(quit_closure);
diff --git a/components/security_state/core/features.cc b/components/security_state/core/features.cc
index 9711685..9dcd8b9 100644
--- a/components/security_state/core/features.cc
+++ b/components/security_state/core/features.cc
@@ -21,8 +21,5 @@
 const base::Feature kSafetyTipUI{"SafetyTip",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kPassiveMixedContentWarning{
-    "PassiveMixedContentWarning", base::FEATURE_ENABLED_BY_DEFAULT};
-
 }  // namespace features
 }  // namespace security_state
diff --git a/components/security_state/core/features.h b/components/security_state/core/features.h
index 263a2ac..59be999 100644
--- a/components/security_state/core/features.h
+++ b/components/security_state/core/features.h
@@ -44,11 +44,6 @@
 COMPONENT_EXPORT(SECURITY_STATE_FEATURES)
 extern const base::Feature kSafetyTipUI;
 
-// This feature causes passive mixed content to show the not secure warning
-// chip. Does not affect mixed forms.
-COMPONENT_EXPORT(SECURITY_STATE_FEATURES)
-extern const base::Feature kPassiveMixedContentWarning;
-
 }  // namespace features
 }  // namespace security_state
 
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 03699da3..af0b7c5 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -210,10 +210,7 @@
   DCHECK(!visible_security_state.ran_content_with_cert_errors);
 
   if (visible_security_state.displayed_mixed_content) {
-    if (base::FeatureList::IsEnabled(features::kPassiveMixedContentWarning)) {
-      return kDisplayedInsecureContentWarningLevel;
-    }
-    return kDisplayedInsecureContentLevel;
+    return kDisplayedInsecureContentWarningLevel;
   }
 
   if ((visible_security_state.contained_mixed_form &&
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index bc631f2..33132a55 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -302,10 +302,7 @@
   // Verify that passive mixed content downgrades the security level.
   helper.set_contained_mixed_form(false);
   helper.set_displayed_mixed_content(true);
-  SecurityLevel expected_passive_level =
-      base::FeatureList::IsEnabled(features::kPassiveMixedContentWarning)
-          ? WARNING
-          : NONE;
+  SecurityLevel expected_passive_level = WARNING;
   EXPECT_EQ(expected_passive_level, helper.GetSecurityLevel());
 
   // Ensure that active mixed content downgrades the security level.
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index 2be1e951..c815d89 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -167,6 +167,15 @@
   return intent;
 }
 
+apps::mojom::IntentPtr CreateShareIntentFromText(
+    const std::string& share_text) {
+  auto intent = apps::mojom::Intent::New();
+  intent->action = kIntentActionSend;
+  intent->mime_type = "text/plain";
+  intent->share_text = share_text;
+  return intent;
+}
+
 bool ConditionValueMatches(
     const std::string& value,
     const apps::mojom::ConditionValuePtr& condition_value) {
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h
index 753ba06..fbb97bb 100644
--- a/components/services/app_service/public/cpp/intent_util.h
+++ b/components/services/app_service/public/cpp/intent_util.h
@@ -35,6 +35,9 @@
     const std::string& mime_type,
     const GURL& drive_share_url);
 
+// Create an intent struct from URL.
+apps::mojom::IntentPtr CreateShareIntentFromText(const std::string& share_text);
+
 // Return true if |value| matches with the |condition_value|, based on the
 // pattern match type in the |condition_value|.
 bool ConditionValueMatches(
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index d8381d0..f327cf9 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -329,6 +329,7 @@
   // The Drive share URL, this is only filled if the intent contains a file
   // from Google Drive.
   url.mojom.Url? drive_share_url;
+  string? share_text; // Text to share. e.g. Share link to other app.
 };
 
 // Represents a group of |app_ids| that is no longer preferred app of their
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index bbee181..65ae5b3 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -309,6 +309,9 @@
       ActivationDecision* decision) override {
     return effective_activation_level;
   }
+  void OnAdsViolationTriggered(
+      content::RenderFrameHost* rfh,
+      mojom::AdsViolation triggered_violation) override {}
 
   ContentSubresourceFilterThrottleManager* throttle_manager() {
     return throttle_manager_.get();
diff --git a/components/subresource_filter/content/browser/subresource_filter_client.h b/components/subresource_filter/content/browser/subresource_filter_client.h
index ce82afe..4567548 100644
--- a/components/subresource_filter/content/browser/subresource_filter_client.h
+++ b/components/subresource_filter/content/browser/subresource_filter_client.h
@@ -8,6 +8,7 @@
 #include "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
 namespace content {
@@ -37,6 +38,11 @@
       content::NavigationHandle* navigation_handle,
       mojom::ActivationLevel initial_activation_level,
       subresource_filter::ActivationDecision* decision) = 0;
+
+  // Called on the subresource filter client when an ads violation is detected.
+  virtual void OnAdsViolationTriggered(
+      content::RenderFrameHost* rfh,
+      mojom::AdsViolation triggered_violation) = 0;
 };
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index b1b0d48..4435878e 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -87,6 +87,9 @@
 
   MOCK_METHOD0(ShowNotification, void());
   MOCK_METHOD0(ForceActivationInCurrentWebContents, bool());
+  MOCK_METHOD2(OnAdsViolationTriggered,
+               void(content::RenderFrameHost*,
+                    subresource_filter::mojom::AdsViolation));
 
   void AllowlistInCurrentWebContents(const GURL& url) {
     ASSERT_TRUE(url.SchemeIsHTTPOrHTTPS());
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index f465de7..60f1eaf 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -236,6 +236,13 @@
 const base::Feature kFilterAdsOnAbusiveSites{"FilterAdsOnAbusiveSites",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kAdsInterventionsEnforced{
+    "AdsInterventionsEnforced", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<base::TimeDelta> kAdsInterventionDuration = {
+    &kAdsInterventionsEnforced, "kAdsInterventionDuration",
+    base::TimeDelta::FromDays(3)};
+
 // Legacy name `activation_state` is used in variation parameters.
 const char kActivationLevelParameterName[] = "activation_state";
 const char kActivationLevelDryRun[] = "dryrun";
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.h b/components/subresource_filter/core/browser/subresource_filter_features.h
index 0bcc52b4..cc73b58 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -186,6 +186,12 @@
 // Enables the blocking of ads on sites that are abusive.
 extern const base::Feature kFilterAdsOnAbusiveSites;
 
+// Enables the blocking of ads on sites that have ads violations.
+extern const base::Feature kAdsInterventionsEnforced;
+
+// The duration that an ads intervention is active for.
+extern const base::FeatureParam<base::TimeDelta> kAdsInterventionDuration;
+
 // Name/values of the variation parameter controlling maximum activation level.
 extern const char kActivationLevelParameterName[];
 extern const char kActivationLevelDryRun[];
diff --git a/components/subresource_filter/core/mojom/subresource_filter.mojom b/components/subresource_filter/core/mojom/subresource_filter.mojom
index 187170fb..7231e4a0 100644
--- a/components/subresource_filter/core/mojom/subresource_filter.mojom
+++ b/components/subresource_filter/core/mojom/subresource_filter.mojom
@@ -12,6 +12,11 @@
   kEnabled
 };
 
+// Enumerates which ads violations are recorded for a page.
+enum AdsViolation {
+    kMobileAdDensityByHeightAbove30,
+};
+
 struct ActivationState {
   // The degree to which subresource filtering is activated for the page load.
   ActivationLevel activation_level = kDisabled;
diff --git a/components/sync/engine/engine_components_factory_impl.cc b/components/sync/engine/engine_components_factory_impl.cc
index 98be1f5..c035cf5 100644
--- a/components/sync/engine/engine_components_factory_impl.cc
+++ b/components/sync/engine/engine_components_factory_impl.cc
@@ -18,8 +18,6 @@
     const Switches& switches)
     : switches_(switches) {}
 
-EngineComponentsFactoryImpl::~EngineComponentsFactoryImpl() {}
-
 std::unique_ptr<SyncScheduler> EngineComponentsFactoryImpl::BuildScheduler(
     const std::string& name,
     SyncCycleContext* context,
diff --git a/components/sync/engine/engine_components_factory_impl.h b/components/sync/engine/engine_components_factory_impl.h
index 74e3b737..b02d23e 100644
--- a/components/sync/engine/engine_components_factory_impl.h
+++ b/components/sync/engine/engine_components_factory_impl.h
@@ -9,7 +9,6 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "components/sync/engine/engine_components_factory.h"
 
 namespace syncer {
@@ -19,7 +18,10 @@
 class EngineComponentsFactoryImpl : public EngineComponentsFactory {
  public:
   explicit EngineComponentsFactoryImpl(const Switches& switches);
-  ~EngineComponentsFactoryImpl() override;
+  EngineComponentsFactoryImpl(const EngineComponentsFactoryImpl&) = delete;
+  EngineComponentsFactoryImpl& operator=(const EngineComponentsFactoryImpl&) =
+      delete;
+  ~EngineComponentsFactoryImpl() override = default;
 
   std::unique_ptr<SyncScheduler> BuildScheduler(
       const std::string& name,
@@ -41,7 +43,6 @@
 
  private:
   const Switches switches_;
-  DISALLOW_COPY_AND_ASSIGN(EngineComponentsFactoryImpl);
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/test_engine_components_factory.cc b/components/sync/engine/test_engine_components_factory.cc
index 00fec435..2532a44d 100644
--- a/components/sync/engine/test_engine_components_factory.cc
+++ b/components/sync/engine/test_engine_components_factory.cc
@@ -9,10 +9,6 @@
 
 namespace syncer {
 
-TestEngineComponentsFactory::TestEngineComponentsFactory() {}
-
-TestEngineComponentsFactory::~TestEngineComponentsFactory() {}
-
 std::unique_ptr<SyncScheduler> TestEngineComponentsFactory::BuildScheduler(
     const std::string& name,
     SyncCycleContext* context,
diff --git a/components/sync/engine/test_engine_components_factory.h b/components/sync/engine/test_engine_components_factory.h
index 982ec7a..1bd24e0 100644
--- a/components/sync/engine/test_engine_components_factory.h
+++ b/components/sync/engine/test_engine_components_factory.h
@@ -9,15 +9,17 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "components/sync/engine/engine_components_factory.h"
 
 namespace syncer {
 
 class TestEngineComponentsFactory : public EngineComponentsFactory {
  public:
-  TestEngineComponentsFactory();
-  ~TestEngineComponentsFactory() override;
+  TestEngineComponentsFactory() = default;
+  TestEngineComponentsFactory(const TestEngineComponentsFactory&) = delete;
+  TestEngineComponentsFactory& operator=(const TestEngineComponentsFactory&) =
+      delete;
+  ~TestEngineComponentsFactory() override = default;
 
   std::unique_ptr<SyncScheduler> BuildScheduler(
       const std::string& name,
@@ -36,9 +38,6 @@
       const std::string& store_birthday,
       const std::string& bag_of_chips,
       base::TimeDelta poll_interval) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestEngineComponentsFactory);
 };
 
 }  // namespace syncer
diff --git a/components/test/components_test_suite.cc b/components/test/components_test_suite.cc
index 0a1776f..b07c5c6 100644
--- a/components/test/components_test_suite.cc
+++ b/components/test/components_test_suite.cc
@@ -5,6 +5,7 @@
 #include "components/test/components_test_suite.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -49,6 +50,8 @@
 class ComponentsTestSuite : public base::TestSuite {
  public:
   ComponentsTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {}
+  ComponentsTestSuite(const ComponentsTestSuite&) = delete;
+  ComponentsTestSuite& operator=(const ComponentsTestSuite&) = delete;
 
  private:
   void Initialize() override {
@@ -111,14 +114,16 @@
 #if defined(OS_WIN)
   base::win::ScopedCOMInitializer com_initializer_;
 #endif
-
-  DISALLOW_COPY_AND_ASSIGN(ComponentsTestSuite);
 };
 
 class ComponentsUnitTestEventListener : public testing::EmptyTestEventListener {
  public:
-  ComponentsUnitTestEventListener() {}
-  ~ComponentsUnitTestEventListener() override {}
+  ComponentsUnitTestEventListener() = default;
+  ComponentsUnitTestEventListener(const ComponentsUnitTestEventListener&) =
+      delete;
+  ComponentsUnitTestEventListener& operator=(
+      const ComponentsUnitTestEventListener&) = delete;
+  ~ComponentsUnitTestEventListener() override = default;
 
   void OnTestStart(const testing::TestInfo& test_info) override {
 #if defined(OS_IOS)
@@ -142,8 +147,6 @@
 #else
   std::unique_ptr<content::TestContentClientInitializer> content_initializer_;
 #endif
-
-  DISALLOW_COPY_AND_ASSIGN(ComponentsUnitTestEventListener);
 };
 
 }  // namespace
diff --git a/components/test/ios_components_test_initializer.h b/components/test/ios_components_test_initializer.h
index 59bcc0038..3e49463 100644
--- a/components/test/ios_components_test_initializer.h
+++ b/components/test/ios_components_test_initializer.h
@@ -7,8 +7,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
-
 namespace network {
 class TestNetworkConnectionTracker;
 }
@@ -17,13 +15,14 @@
 class IosComponentsTestInitializer {
  public:
   IosComponentsTestInitializer();
+  IosComponentsTestInitializer(const IosComponentsTestInitializer&) = delete;
+  IosComponentsTestInitializer& operator=(const IosComponentsTestInitializer&) =
+      delete;
   virtual ~IosComponentsTestInitializer();
 
  private:
   std::unique_ptr<network::TestNetworkConnectionTracker>
       network_connection_tracker_;
-
-  DISALLOW_COPY_AND_ASSIGN(IosComponentsTestInitializer);
 };
 
 #endif  // COMPONENTS_TEST_IOS_COMPONENTS_TEST_INITIALIZER_H_
diff --git a/components/variations/android/BUILD.gn b/components/variations/android/BUILD.gn
index b913ba68..0897cd1 100644
--- a/components/variations/android/BUILD.gn
+++ b/components/variations/android/BUILD.gn
@@ -9,7 +9,6 @@
     "//base:base_java",
     "//base:jni_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_utils_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   sources = [
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index 33e1ccc..a34aeb1 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -64,9 +64,10 @@
   // transmitted via the X-Client-Data header. These IDs are transmitted in
   // first- and third-party contexts.
   GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
-  // Used when kRestrictGoogleWebVisibility is enabled. The IDs in this
-  // collection are used by Google web properties and are transmitted via the
-  // X-Client-Data header. These IDs are transmitted in first-party contexts.
+  // The IDs in this collection are used by Google web properties and are
+  // transmitted via the X-Client-Data header. When kRestrictGoogleWebVisibility
+  // is enabled, these IDs are transmitted in only first-party contexts;
+  // otherwise, these IDs are transmitted in first- and third-party contexts.
   GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
   // This collection is used by Google web properties for signed in users only,
   // transmitted through the X-Client-Data header.
@@ -75,10 +76,11 @@
   // server-side experimental behavior and are transmitted via the X-Client-Data
   // header. These IDs are transmitted in first- and third-party contexts.
   GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
-  // Used when kRestrictGoogleWebVisibility is enabled. The IDs in this
-  // collection are used by Google web properties to trigger server-side
-  // experimental behavior and are transmitted via the X-Client-Data header.
-  // These IDs are transmitted in first-party contexts.
+  // The IDs in this collection are used by Google web properties to trigger
+  // server-side experimental behavior and are transmitted via the X-Client-Data
+  // header. When kRestrictGoogleWebVisibility is enabled, these IDs are
+  // transmitted in only first-party contexts; otherwise, these IDs are
+  // transmitted in first- and third-party contexts.
   GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY,
   // This collection is used by the Google App and is passed at the time
   // the cross-app communication is triggered.
diff --git a/components/variations/variations_ids_provider.cc b/components/variations/variations_ids_provider.cc
index 5f1a1d3..eff15a8 100644
--- a/components/variations/variations_ids_provider.cc
+++ b/components/variations/variations_ids_provider.cc
@@ -7,9 +7,6 @@
 #include <stddef.h>
 
 #include <algorithm>
-#include <set>
-#include <string>
-#include <vector>
 
 #include "base/base64.h"
 #include "base/memory/singleton.h"
@@ -55,44 +52,44 @@
   return variation_ids_header_copy;
 }
 
-std::string VariationsIdsProvider::GetVariationsString(IDCollectionKey key) {
-  InitVariationIDsCacheIfNeeded();
-
+std::string VariationsIdsProvider::GetVariationsString(
+    const std::set<IDCollectionKey>& keys) {
   // Construct a space-separated string with leading and trailing spaces from
-  // the variations set. Note: The ids in it will be in sorted order per the
+  // the VariationIDs set. The IDs in the string are in sorted order as per the
   // std::set contract.
   std::string ids_string = " ";
-  {
-    base::AutoLock scoped_lock(lock_);
-    for (const VariationIDEntry& entry : GetAllVariationIds()) {
-      if (entry.second == key) {
-        ids_string.append(base::NumberToString(entry.first));
-        ids_string.push_back(' ');
-      }
-    }
+
+  for (const VariationID& id : GetVariationsVector(keys)) {
+    ids_string.append(base::NumberToString(id));
+    ids_string.push_back(' ');
   }
+
   return ids_string;
 }
 
 std::string VariationsIdsProvider::GetGoogleAppVariationsString() {
-  return GetVariationsString(GOOGLE_APP);
+  return GetVariationsString({GOOGLE_APP});
 }
 
 std::string VariationsIdsProvider::GetVariationsString() {
-  return GetVariationsString(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT);
+  return GetVariationsString(
+      {GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, GOOGLE_WEB_PROPERTIES_FIRST_PARTY});
 }
 
 std::vector<VariationID> VariationsIdsProvider::GetVariationsVector(
-    IDCollectionKey key) {
-  return GetVariationsVectorImpl(std::set<IDCollectionKey>{key});
+    const std::set<IDCollectionKey>& keys) {
+  return GetVariationsVectorImpl(keys);
 }
 
 std::vector<VariationID>
 VariationsIdsProvider::GetVariationsVectorForWebPropertiesKeys() {
   const std::set<IDCollectionKey> web_properties_keys{
-      variations::GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
-      variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN,
-      variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT};
+      GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
+      GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
+      GOOGLE_WEB_PROPERTIES_SIGNED_IN,
+      GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+      GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY,
+  };
   return GetVariationsVectorImpl(web_properties_keys);
 }
 
@@ -168,11 +165,7 @@
     const std::string& group_name) {
   base::AutoLock scoped_lock(lock_);
   const size_t old_size = variation_ids_set_.size();
-  CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT);
-  CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN);
-  CacheVariationsId(trial_name, group_name,
-                    GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT);
-  CacheVariationsId(trial_name, group_name, GOOGLE_APP);
+  CacheVariationsId(trial_name, group_name);
   if (variation_ids_set_.size() != old_size)
     UpdateVariationIDsHeaderValue();
 }
@@ -215,13 +208,7 @@
   base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
 
   for (const auto& entry : initial_groups) {
-    CacheVariationsId(entry.trial_name, entry.group_name,
-                      GOOGLE_WEB_PROPERTIES_ANY_CONTEXT);
-    CacheVariationsId(entry.trial_name, entry.group_name,
-                      GOOGLE_WEB_PROPERTIES_SIGNED_IN);
-    CacheVariationsId(entry.trial_name, entry.group_name,
-                      GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT);
-    CacheVariationsId(entry.trial_name, entry.group_name, GOOGLE_APP);
+    CacheVariationsId(entry.trial_name, entry.group_name);
   }
   UpdateVariationIDsHeaderValue();
 
@@ -229,11 +216,13 @@
 }
 
 void VariationsIdsProvider::CacheVariationsId(const std::string& trial_name,
-                                              const std::string& group_name,
-                                              IDCollectionKey key) {
-  const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
-  if (id != EMPTY_ID)
-    variation_ids_set_.insert(VariationIDEntry(id, key));
+                                              const std::string& group_name) {
+  for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
+    IDCollectionKey key = static_cast<IDCollectionKey>(i);
+    const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
+    if (id != EMPTY_ID)
+      variation_ids_set_.insert(VariationIDEntry(id, key));
+  }
 }
 
 void VariationsIdsProvider::UpdateVariationIDsHeaderValue() {
@@ -270,16 +259,38 @@
       case GOOGLE_WEB_PROPERTIES_ANY_CONTEXT:
         proto.add_variation_id(entry.first);
         break;
+      case GOOGLE_WEB_PROPERTIES_FIRST_PARTY:
+        if (base::FeatureList::IsEnabled(
+                internal::kRestrictGoogleWebVisibility)) {
+          // TODO(crbug/1094303): Send fewer VariationIDs in third-party
+          // contexts by excluding IDs associated with
+          // GOOGLE_WEB_PROPERTIES_FIRST_PARTY.
+          break;
+        }
+        // When the feature is not enabled, treat VariationIDs associated with
+        // GOOGLE_WEB_PROPERTIES_FIRST_PARTY in the same way as those
+        // associated with GOOGLE_WEB_PROPERTIES_ANY_CONTEXT.
+        proto.add_variation_id(entry.first);
+        break;
       case GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT:
         proto.add_trigger_variation_id(entry.first);
         break;
+      case GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY:
+        if (base::FeatureList::IsEnabled(
+                internal::kRestrictGoogleWebVisibility)) {
+          // TODO(crbug/1094303): Send fewer VariationIDs in third-party
+          // contexts by excluding IDs associated with
+          // GOOGLE_WEB_PROPERTIES_FIRST_PARTY.
+          break;
+        }
+        // When the feature is not enabled, treat VariationIDs associated with
+        // GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY in the same way as those
+        // associated with GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT.
+        proto.add_trigger_variation_id(entry.first);
+        break;
       case GOOGLE_APP:
         // These IDs should not be added into Google Web headers.
         break;
-      case GOOGLE_WEB_PROPERTIES_FIRST_PARTY:
-      case GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY:
-        // TODO(crbug/1094303): Add support for the above IDCollectionKeys.
-        break;
       case ID_COLLECTION_COUNT:
         // This case included to get full enum coverage for switch, so that
         // new enums introduce compiler warnings. Nothing to do for this.
@@ -385,7 +396,7 @@
       result.push_back(entry.first);
   }
 
-  // Make sure each enry is unique. As a side-effect, the output will be sorted.
+  // Make sure each entry is unique. As a side effect, the output is sorted.
   std::sort(result.begin(), result.end());
   result.erase(std::unique(result.begin(), result.end()), result.end());
   return result;
diff --git a/components/variations/variations_ids_provider.h b/components/variations/variations_ids_provider.h
index 1743c9a..7be7567 100644
--- a/components/variations/variations_ids_provider.h
+++ b/components/variations/variations_ids_provider.h
@@ -64,9 +64,10 @@
   // apps.
   std::string GetGoogleAppVariationsString();
 
-  // Returns the collection of variation ids matching the given |key|. Each
-  // entry in the returned vector will be unique.
-  std::vector<VariationID> GetVariationsVector(IDCollectionKey key);
+  // Returns the collection of VariationIDs associated with |keys|. Each entry
+  // in the returned vector is unique.
+  std::vector<VariationID> GetVariationsVector(
+      const std::set<IDCollectionKey>& keys);
 
   // Returns the collection of variations ids for all Google Web Properties
   // related keys.
@@ -132,7 +133,7 @@
   // Returns a space-separated string containing the list of current active
   // variations (as would be reported in the |variation_id| repeated field of
   // the ClientVariations proto) for a given ID collection.
-  std::string GetVariationsString(IDCollectionKey key);
+  std::string GetVariationsString(const std::set<IDCollectionKey>& keys);
 
   // base::FieldTrialList::Observer:
   // This will add the variation ID associated with |trial_name| and
@@ -149,11 +150,10 @@
   // new variation IDs.
   void InitVariationIDsCacheIfNeeded();
 
-  // Looks up the associated id for the given trial/group and adds an entry for
-  // it to |variation_ids_set_| if found.
+  // Looks up the VariationID associated with |trial_name| and |group_name|, and
+  // if found, adds an entry for it to |variation_ids_set_|.
   void CacheVariationsId(const std::string& trial_name,
-                         const std::string& group_name,
-                         IDCollectionKey key);
+                         const std::string& group_name);
 
   // Takes whatever is currently in |variation_ids_set_| and recreates
   // |variation_ids_header_| with it.  Assumes the the |lock_| is currently
diff --git a/components/variations/variations_ids_provider_unittest.cc b/components/variations/variations_ids_provider_unittest.cc
index 9be52dc5..cabc214 100644
--- a/components/variations/variations_ids_provider_unittest.cc
+++ b/components/variations/variations_ids_provider_unittest.cc
@@ -165,17 +165,25 @@
 
   const std::string default_name = "default";
   scoped_refptr<base::FieldTrial> trial_1(CreateTrialAndAssociateId(
-      "t1", default_name, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 123));
+      "t1", default_name, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 11));
   ASSERT_EQ(default_name, trial_1->group_name());
 
   scoped_refptr<base::FieldTrial> trial_2(CreateTrialAndAssociateId(
-      "t2", default_name, GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 456));
+      "t2", default_name, GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 22));
   ASSERT_EQ(default_name, trial_2->group_name());
 
   scoped_refptr<base::FieldTrial> trial_3(CreateTrialAndAssociateId(
-      "t3", default_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN, 789));
+      "t3", default_name, GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 33));
   ASSERT_EQ(default_name, trial_3->group_name());
 
+  scoped_refptr<base::FieldTrial> trial_4(CreateTrialAndAssociateId(
+      "t4", default_name, GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 44));
+  ASSERT_EQ(default_name, trial_4->group_name());
+
+  scoped_refptr<base::FieldTrial> trial_5(CreateTrialAndAssociateId(
+      "t5", default_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN, 55));
+  ASSERT_EQ(default_name, trial_5->group_name());
+
   // Run the message loop to make sure OnFieldTrialGroupFinalized is called for
   // the two field trials.
   base::RunLoop().RunUntilIdle();
@@ -186,10 +194,12 @@
     std::set<VariationID> variation_ids;
     std::set<VariationID> trigger_ids;
     ASSERT_TRUE(ExtractVariationIds(variations, &variation_ids, &trigger_ids));
-    EXPECT_EQ(1U, variation_ids.size());
-    EXPECT_TRUE(variation_ids.find(123) != variation_ids.end());
-    EXPECT_EQ(1U, trigger_ids.size());
-    EXPECT_TRUE(trigger_ids.find(456) != trigger_ids.end());
+    EXPECT_EQ(2U, variation_ids.size());
+    EXPECT_TRUE(variation_ids.find(11) != variation_ids.end());
+    EXPECT_TRUE(variation_ids.find(22) != variation_ids.end());
+    EXPECT_EQ(2U, trigger_ids.size());
+    EXPECT_TRUE(trigger_ids.find(33) != trigger_ids.end());
+    EXPECT_TRUE(trigger_ids.find(44) != trigger_ids.end());
   }
 
   // Now, get signed-in ids.
@@ -198,22 +208,28 @@
     std::set<VariationID> variation_ids;
     std::set<VariationID> trigger_ids;
     ASSERT_TRUE(ExtractVariationIds(variations, &variation_ids, &trigger_ids));
-    EXPECT_EQ(2U, variation_ids.size());
-    EXPECT_TRUE(variation_ids.find(123) != variation_ids.end());
-    EXPECT_TRUE(variation_ids.find(789) != variation_ids.end());
-    EXPECT_EQ(1U, trigger_ids.size());
-    EXPECT_TRUE(trigger_ids.find(456) != trigger_ids.end());
+    EXPECT_EQ(3U, variation_ids.size());
+    EXPECT_TRUE(variation_ids.find(11) != variation_ids.end());
+    EXPECT_TRUE(variation_ids.find(22) != variation_ids.end());
+    EXPECT_TRUE(variation_ids.find(55) != variation_ids.end());
+    EXPECT_EQ(2U, trigger_ids.size());
+    EXPECT_TRUE(trigger_ids.find(33) != trigger_ids.end());
+    EXPECT_TRUE(trigger_ids.find(44) != trigger_ids.end());
   }
 }
 
 TEST_F(VariationsIdsProviderTest, GetGoogleAppVariationsString) {
-  // All GOOGLE_WEB_PROPERTIES(_X) ids shouldn't be included.
-  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 123);
-  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 124);
-  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
+  // No GOOGLE_WEB_PROPERTIES(_X) ids should be included.
+  CreateTrialAndAssociateId("t1", "g1",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 121);
+  CreateTrialAndAssociateId("t2", "g2",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 122);
+  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 123);
+  CreateTrialAndAssociateId("t4", "g4", GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 124);
+  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
 
   // GOOGLE_APP ids should be included.
-  CreateTrialAndAssociateId("t4", "g4", GOOGLE_APP, 126);
+  CreateTrialAndAssociateId("t6", "g6", GOOGLE_APP, 126);
 
   VariationsIdsProvider provider;
   provider.ForceVariationIds({"100", "200"}, "");
@@ -221,12 +237,21 @@
 }
 
 TEST_F(VariationsIdsProviderTest, GetVariationsString) {
-  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 123);
-  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 124);
-  // SIGNED_IN ids shouldn't be included.
-  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
+  // Trigger ids shouldn't be included.
+  CreateTrialAndAssociateId("t1", "g1",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 121);
+  CreateTrialAndAssociateId("t2", "g2",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 122);
+
+  // These ids should be included.
+  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 123);
+  CreateTrialAndAssociateId("t4", "g4", GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 124);
+
+  // Signed-in ids shouldn't be included.
+  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
+
   // GOOGLE_APP ids shouldn't be included.
-  CreateTrialAndAssociateId("t4", "g4", GOOGLE_APP, 126);
+  CreateTrialAndAssociateId("t6", "g6", GOOGLE_APP, 126);
 
   VariationsIdsProvider provider;
   provider.ForceVariationIds({"100", "200"}, "");
@@ -235,66 +260,82 @@
 
 TEST_F(VariationsIdsProviderTest, GetVariationsVector) {
   CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 121);
-  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 122);
-  CreateTrialAndAssociateId("t3", "g3",
-                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 123);
+  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 122);
   CreateTrialAndAssociateId("t4", "g4",
-                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 124);
-  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
-  CreateTrialAndAssociateId("t6", "g6", GOOGLE_APP, 126);
-
-  VariationsIdsProvider provider;
-  provider.ForceVariationIds({"100", "200", "t101"}, "");
-
-  EXPECT_EQ((std::vector<VariationID>{100, 121, 122, 200}),
-            provider.GetVariationsVector(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT));
-  EXPECT_EQ(
-      (std::vector<VariationID>{101, 123, 124}),
-      provider.GetVariationsVector(GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT));
-  EXPECT_EQ((std::vector<VariationID>{125}),
-            provider.GetVariationsVector(GOOGLE_WEB_PROPERTIES_SIGNED_IN));
-  EXPECT_EQ((std::vector<VariationID>{126}),
-            provider.GetVariationsVector(GOOGLE_APP));
-}
-
-TEST_F(VariationsIdsProviderTest, GetVariationsVectorForWebPropertiesKeys) {
-  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 121);
-  CreateTrialAndAssociateId("t2", "g2",
-                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 122);
-  CreateTrialAndAssociateId("t3", "g3", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 123);
-  CreateTrialAndAssociateId("t4", "g4", GOOGLE_APP, 124);  // Will be excluded.
-  VariationsIdsProvider provider;
-  provider.ForceVariationIds({"100", "t101"}, "");
-  EXPECT_EQ((std::vector<VariationID>{100, 101, 121, 122, 123}),
-            provider.GetVariationsVectorForWebPropertiesKeys());
-}
-
-TEST_F(VariationsIdsProviderTest, GetVariationsVectorImpl) {
-  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 121);
-  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 122);
-  CreateTrialAndAssociateId("t3", "g3",
                             GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 123);
-  CreateTrialAndAssociateId("t4", "g4",
-                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 124);
-  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
-  CreateTrialAndAssociateId("t6", "g6", GOOGLE_WEB_PROPERTIES_SIGNED_IN,
-                            124);  // Note: Duplicate.
+  CreateTrialAndAssociateId("t5", "g5",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 124);
+  CreateTrialAndAssociateId("t6", "g6", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
   CreateTrialAndAssociateId("t7", "g7", GOOGLE_APP, 126);
 
   VariationsIdsProvider provider;
   provider.ForceVariationIds({"100", "200", "t101"}, "");
 
-  EXPECT_EQ((std::vector<VariationID>{100, 101, 121, 122, 123, 124, 200}),
+  EXPECT_EQ((std::vector<VariationID>{100, 121, 200}),
+            provider.GetVariationsVector({GOOGLE_WEB_PROPERTIES_ANY_CONTEXT}));
+  EXPECT_EQ((std::vector<VariationID>{122}),
+            provider.GetVariationsVector({GOOGLE_WEB_PROPERTIES_FIRST_PARTY}));
+  EXPECT_EQ((std::vector<VariationID>{101, 123}),
+            provider.GetVariationsVector(
+                {GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT}));
+  EXPECT_EQ((std::vector<VariationID>{124}),
+            provider.GetVariationsVector(
+                {GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY}));
+  EXPECT_EQ((std::vector<VariationID>{125}),
+            provider.GetVariationsVector({GOOGLE_WEB_PROPERTIES_SIGNED_IN}));
+  EXPECT_EQ((std::vector<VariationID>{126}),
+            provider.GetVariationsVector({GOOGLE_APP}));
+  EXPECT_EQ(
+      (std::vector<VariationID>{100, 101, 121, 122, 123, 124, 125, 126, 200}),
+      provider.GetVariationsVector(
+          {GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
+           GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+           GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY,
+           GOOGLE_WEB_PROPERTIES_SIGNED_IN, GOOGLE_APP}));
+}
+
+TEST_F(VariationsIdsProviderTest, GetVariationsVectorForWebPropertiesKeys) {
+  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 121);
+  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 122);
+  CreateTrialAndAssociateId("t3", "g3",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 123);
+  CreateTrialAndAssociateId("t4", "g4",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 124);
+  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
+
+  // GOOGLE_APP ids shouldn't be included.
+  CreateTrialAndAssociateId("t6", "g6", GOOGLE_APP, 126);
+
+  VariationsIdsProvider provider;
+  provider.ForceVariationIds({"100", "t101"}, "");
+  EXPECT_EQ((std::vector<VariationID>{100, 101, 121, 122, 123, 124, 125}),
+            provider.GetVariationsVectorForWebPropertiesKeys());
+}
+
+TEST_F(VariationsIdsProviderTest, GetVariationsVectorImpl) {
+  CreateTrialAndAssociateId("t1", "g1", GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 121);
+  CreateTrialAndAssociateId("t2", "g2", GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 122);
+  CreateTrialAndAssociateId("t3", "g3",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 123);
+  CreateTrialAndAssociateId("t4", "g4",
+                            GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 124);
+  CreateTrialAndAssociateId("t5", "g5", GOOGLE_WEB_PROPERTIES_SIGNED_IN, 125);
+  CreateTrialAndAssociateId("t6", "g6", GOOGLE_APP, 125);  // Duplicate.
+
+  VariationsIdsProvider provider;
+  provider.ForceVariationIds({"100", "200", "t101"}, "");
+
+  EXPECT_EQ(
+      (std::vector<VariationID>{100, 121, 122, 200}),
+      provider.GetVariationsVectorImpl({GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
+                                        GOOGLE_WEB_PROPERTIES_FIRST_PARTY}));
+  EXPECT_EQ((std::vector<VariationID>{101, 123, 124}),
             provider.GetVariationsVectorImpl(
-                {GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
-                 GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT}));
-  EXPECT_EQ((std::vector<VariationID>{101, 123, 124, 125}),
+                {GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+                 GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY}));
+  EXPECT_EQ((std::vector<VariationID>{125}),
             provider.GetVariationsVectorImpl(
-                {GOOGLE_WEB_PROPERTIES_SIGNED_IN,
-                 GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT}));
-  EXPECT_EQ((std::vector<VariationID>{124, 125, 126}),
-            provider.GetVariationsVectorImpl(
-                {GOOGLE_APP, GOOGLE_WEB_PROPERTIES_SIGNED_IN}));
+                {GOOGLE_WEB_PROPERTIES_SIGNED_IN, GOOGLE_APP}));
 }
 
 }  // namespace variations
diff --git a/components/variations/variations_seed_processor.cc b/components/variations/variations_seed_processor.cc
index a0dc36d..140104c 100644
--- a/components/variations/variations_seed_processor.cc
+++ b/components/variations/variations_seed_processor.cc
@@ -13,6 +13,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/variations/client_filterable_state.h"
 #include "components/variations/processed_study.h"
@@ -35,31 +36,60 @@
     AssociateVariationParams(study.name(), experiment.name(), params);
 }
 
-// If there are variation ids associated with |experiment|, register the
-// variation ids.
+// Returns the IDCollectionKey with which |experiment| should be associated.
+// Returns nullopt when |experiment| doesn't have a Google web or Google web
+// trigger experiment ID.
+base::Optional<IDCollectionKey> GetKeyForWebExperiment(
+    const Study_Experiment& experiment) {
+  bool has_web_experiment_id = experiment.has_google_web_experiment_id();
+  bool has_web_trigger_experiment_id =
+      experiment.has_google_web_trigger_experiment_id();
+
+  if (!has_web_experiment_id && !has_web_trigger_experiment_id)
+    return base::nullopt;
+
+  // An experiment cannot have both |google_web_experiment_id| and
+  // |google_trigger_web_experiment_id|. This is enforced by the variations
+  // server before generating a variations seed.
+  DCHECK(!(has_web_experiment_id && has_web_trigger_experiment_id));
+
+  Study_GoogleWebVisibility visibility = experiment.google_web_visibility();
+  if (visibility == Study_GoogleWebVisibility_FIRST_PARTY) {
+    return has_web_trigger_experiment_id
+               ? GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY
+               : GOOGLE_WEB_PROPERTIES_FIRST_PARTY;
+  }
+  return has_web_trigger_experiment_id
+             ? GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT
+             : GOOGLE_WEB_PROPERTIES_ANY_CONTEXT;
+}
+
+// If there are VariationIDs associated with |experiment|, register the
+// VariationIDs.
 void RegisterVariationIds(const Study_Experiment& experiment,
                           const std::string& trial_name) {
-  if (experiment.has_google_web_experiment_id()) {
-    const VariationID variation_id =
-        static_cast<VariationID>(experiment.google_web_experiment_id());
-    AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
-                                    trial_name, experiment.name(),
-                                    variation_id);
-  }
-  if (experiment.has_google_web_trigger_experiment_id()) {
-    const VariationID variation_id =
-        static_cast<VariationID>(experiment.google_web_trigger_experiment_id());
-    AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
-                                    trial_name, experiment.name(),
-                                    variation_id);
-  }
-
   if (experiment.has_google_app_experiment_id()) {
     const VariationID variation_id =
         static_cast<VariationID>(experiment.google_app_experiment_id());
     AssociateGoogleVariationIDForce(GOOGLE_APP, trial_name, experiment.name(),
                                     variation_id);
   }
+
+  base::Optional<IDCollectionKey> key = GetKeyForWebExperiment(experiment);
+  if (!key.has_value())
+    return;
+
+  // An experiment cannot have both |google_web_experiment_id| and
+  // |google_trigger_web_experiment_id|. See GetKeyForWebExperiment() for more
+  // details.
+  const VariationID variation_id =
+      experiment.has_google_web_trigger_experiment_id()
+          ? static_cast<VariationID>(
+                experiment.google_web_trigger_experiment_id())
+          : static_cast<VariationID>(experiment.google_web_experiment_id());
+
+  AssociateGoogleVariationIDForce(key.value(), trial_name, experiment.name(),
+                                  variation_id);
 }
 
 // Executes |callback| on every override defined by |experiment|.
diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc
index 60a58dd..f6f268e5 100644
--- a/components/variations/variations_seed_processor_unittest.cc
+++ b/components/variations/variations_seed_processor_unittest.cc
@@ -27,6 +27,7 @@
 #include "base/test/scoped_field_trial_list_resetter.h"
 #include "components/variations/client_filterable_state.h"
 #include "components/variations/processed_study.h"
+#include "components/variations/proto/study.pb.h"
 #include "components/variations/study_filtering.h"
 #include "components/variations/variations_associated_data.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -182,6 +183,23 @@
   EXPECT_EQ(kExperimentId, id);
 }
 
+TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId_FirstParty) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
+
+  Study study = CreateStudyWithFlagGroups(100, 0, 0);
+  Study_Experiment* experiment1 = study.mutable_experiment(1);
+  experiment1->set_google_web_experiment_id(kExperimentId);
+  experiment1->set_google_web_visibility(Study_GoogleWebVisibility_FIRST_PARTY);
+
+  EXPECT_TRUE(CreateTrialFromStudy(study));
+  EXPECT_EQ(kFlagGroup1Name,
+            base::FieldTrialList::FindFullName(kFlagStudyName));
+
+  VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
+                                        kFlagStudyName, kFlagGroup1Name);
+  EXPECT_EQ(kExperimentId, id);
+}
+
 // Test that the group for kForcingFlag1 is forced.
 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
index 253542db..6136180 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
@@ -100,15 +100,15 @@
   params->gpu_compositing = false;
   params->compositor_frame_sink =
       root_compositor_frame_sink_remote_
-          .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          .BindNewEndpointAndPassDedicatedReceiver();
   params->compositor_frame_sink_client =
       root_compositor_frame_sink_client_.BindInterfaceRemote();
   params->display_private =
-      display_private_.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      display_private_.BindNewEndpointAndPassDedicatedReceiver();
   params->display_client = display_client_.BindRemote();
   params->external_begin_frame_controller =
       external_begin_frame_controller_remote_
-           .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          .BindNewEndpointAndPassDedicatedReceiver();
   return params;
 }
 
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index 5203eca..9db285f4 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -10,13 +10,9 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/message_loop/message_pump_type.h"
-#include "base/no_destructor.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/power_monitor/power_monitor_source.h"
 #include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
-#include "base/task/thread_pool.h"
-#include "base/time/time.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
 #include "components/ui_devtools/buildflags.h"
@@ -25,81 +21,12 @@
 #include "gpu/ipc/service/gpu_init.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "media/gpu/buildflags.h"
-#include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h"
-#include "mojo/public/cpp/system/functions.h"
 #include "services/metrics/public/cpp/delegating_ukm_recorder.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "third_party/skia/include/core/SkFontLCDConfig.h"
 
 namespace {
 
-// Singleton class to store error strings from global mojo error handler. It
-// will store error strings for kTimeout seconds and can be used to set a crash
-// key if the GPU process is going to crash due to a deserialization error.
-// TODO(kylechar): This can be removed after tracking down all outstanding
-// deserialization errors in messages sent from the browser to GPU on the viz
-// message pipe.
-class MojoErrorTracker {
- public:
-  static constexpr base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(5);
-
-  static MojoErrorTracker& Get() {
-    static base::NoDestructor<MojoErrorTracker> tracker;
-    return *tracker;
-  }
-
-  void OnError(const std::string& error) {
-    {
-      base::AutoLock locked(lock_);
-      error_ = error;
-      error_time_ = base::TimeTicks::Now();
-    }
-
-    // Once the error is old enough we will no longer use it in a crash key we
-    // can reset the string storage.
-    base::ThreadPool::PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&MojoErrorTracker::Reset, base::Unretained(this)),
-        kTimeout);
-  }
-
-  // This will initialize the scoped crash key in |key| with the last mojo
-  // error message if the last mojo error happened within kTimeout seconds.
-  void MaybeSetCrashKeyWithRecentError(
-      base::Optional<mojo::debug::ScopedMessageErrorCrashKey>& key) {
-    base::AutoLock locked(lock_);
-    if (!HasErrorTimedOut())
-      key.emplace(error_);
-  }
-
- private:
-  friend class base::NoDestructor<MojoErrorTracker>;
-  MojoErrorTracker() = default;
-
-  bool HasErrorTimedOut() const EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    return base::TimeTicks::Now() - error_time_ > kTimeout;
-  }
-
-  void Reset() {
-    base::AutoLock locked(lock_);
-
-    // If another mojo error happened since this task was scheduled we shouldn't
-    // reset the error string yet.
-    if (!HasErrorTimedOut())
-      return;
-
-    error_.clear();
-    error_.shrink_to_fit();
-  }
-
-  base::Lock lock_;
-  std::string error_ GUARDED_BY(lock_);
-  base::TimeTicks error_time_ GUARDED_BY(lock_);
-};
-
-// static
-constexpr base::TimeDelta MojoErrorTracker::kTimeout;
-
 std::unique_ptr<base::Thread> CreateAndStartIOThread() {
   // TODO(sad): We do not need the IO thread once gpu has a separate process.
   // It should be possible to use |main_task_runner_| for doing IO tasks.
@@ -136,12 +63,6 @@
       gpu_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
   DCHECK(gpu_init_);
 
-  if (!gpu_init_->gpu_info().in_process_gpu) {
-    mojo::SetDefaultProcessErrorHandler(
-        base::BindRepeating(&MojoErrorTracker::OnError,
-                            base::Unretained(&MojoErrorTracker::Get())));
-  }
-
   // TODO(crbug.com/609317): Remove this when Mus Window Server and GPU are
   // split into separate processes. Until then this is necessary to be able to
   // run Mushrome (chrome with mus) with Mus running in the browser process.
@@ -317,16 +238,8 @@
   // FrameSinkManagerImpl, so just do a hard CHECK rather than crashing down the
   // road so that all crash reports caused by this issue look the same and have
   // the same signature. https://crbug.com/928845
-  if (task_executor_) {
-    // If the global mojo error handler callback ran recently, we've cached the
-    // error string and will initialize |crash_key| to contain it before
-    // intentionally crashing. The deserialization error that caused the mojo
-    // error handler to run was probably, but not 100% guaranteed, the error
-    // that caused the main viz browser-to-GPU message pipe close.
-    base::Optional<mojo::debug::ScopedMessageErrorCrashKey> crash_key;
-    MojoErrorTracker::Get().MaybeSetCrashKeyWithRecentError(crash_key);
-    CHECK(!task_executor_);
-  }
+  CHECK(!task_executor_);
+
   task_executor_ = std::make_unique<gpu::GpuInProcessThreadService>(
       this, gpu_thread_task_runner_, gpu_service_->GetGpuScheduler(),
       gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
diff --git a/components/webdata/common/web_data_results.h b/components/webdata/common/web_data_results.h
index db1e907..9d9a6188 100644
--- a/components/webdata/common/web_data_results.h
+++ b/components/webdata/common/web_data_results.h
@@ -40,6 +40,8 @@
                                  //     std::unique_ptr<CreditCard>>>
   AUTOFILL_CUSTOMERDATA_RESULT,  // WDResult<std::unique_ptr<
                                  //     PaymentsCustomerData>>
+  AUTOFILL_OFFER_DATA,           // WDResult<std::vector<std::unique_ptr<
+                                 //     AutofillOfferData>>>
   AUTOFILL_UPI_RESULT,           // WDResult<std::string>
 #if !defined(OS_IOS)             //
   PAYMENT_WEB_APP_MANIFEST,      // WDResult<std::vector<
diff --git a/content/app/content_service_manager_main_delegate.cc b/content/app/content_service_manager_main_delegate.cc
index 1e6ad31..7cb0c25 100644
--- a/content/app/content_service_manager_main_delegate.cc
+++ b/content/app/content_service_manager_main_delegate.cc
@@ -9,7 +9,6 @@
 #include "content/common/mojo_core_library_support.h"
 #include "content/public/app/content_main_delegate.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/service_names.mojom.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/dynamic_library_support.h"
@@ -66,11 +65,6 @@
 #endif
 }
 
-service_manager::ProcessType
-ContentServiceManagerMainDelegate::OverrideProcessType() {
-  return content_main_params_.delegate->OverrideProcessType();
-}
-
 void ContentServiceManagerMainDelegate::InitializeMojo(
     mojo::core::Configuration* config) {
   // If this is the browser process and there's no Mojo invitation pipe on the
@@ -117,58 +111,6 @@
   CHECK_EQ(MOJO_RESULT_OK, result);
 }
 
-std::vector<service_manager::Manifest>
-ContentServiceManagerMainDelegate::GetServiceManifests() {
-  return std::vector<service_manager::Manifest>();
-}
-
-bool ContentServiceManagerMainDelegate::ShouldLaunchAsServiceProcess(
-    const service_manager::Identity& identity) {
-  return identity.name() != mojom::kPackagedServicesServiceName;
-}
-
-void ContentServiceManagerMainDelegate::AdjustServiceProcessCommandLine(
-    const service_manager::Identity& identity,
-    base::CommandLine* command_line) {
-  base::CommandLine::StringVector args_without_switches;
-  if (identity.name() == mojom::kPackagedServicesServiceName) {
-    // Ensure other arguments like URL are not lost.
-    args_without_switches = command_line->GetArgs();
-
-    // When launching the browser process, ensure that we don't inherit any
-    // process type flag. When content embeds Service Manager, a process with no
-    // type is launched as a browser process.
-    base::CommandLine::SwitchMap switches = command_line->GetSwitches();
-    switches.erase(switches::kProcessType);
-    *command_line = base::CommandLine(command_line->GetProgram());
-    for (const auto& sw : switches)
-      command_line->AppendSwitchNative(sw.first, sw.second);
-  }
-
-  content_main_params_.delegate->AdjustServiceProcessCommandLine(identity,
-                                                                 command_line);
-
-  // Append other arguments back to |command_line| after the second call to
-  // delegate as long as it can still remove all the arguments without switches.
-  for (const auto& arg : args_without_switches)
-    command_line->AppendArgNative(arg);
-}
-
-void ContentServiceManagerMainDelegate::OnServiceManagerInitialized(
-    base::OnceClosure quit_closure,
-    service_manager::BackgroundServiceManager* service_manager) {
-  return content_main_params_.delegate->OnServiceManagerInitialized(
-      std::move(quit_closure), service_manager);
-}
-
-std::unique_ptr<service_manager::Service>
-ContentServiceManagerMainDelegate::CreateEmbeddedService(
-    const std::string& service_name) {
-  // TODO
-
-  return nullptr;
-}
-
 void ContentServiceManagerMainDelegate::SetStartServiceManagerOnly(
     bool start_service_manager_only) {
   start_service_manager_only_ = start_service_manager_only;
diff --git a/content/app/content_service_manager_main_delegate.h b/content/app/content_service_manager_main_delegate.h
index ae97969..eb7768b 100644
--- a/content/app/content_service_manager_main_delegate.h
+++ b/content/app/content_service_manager_main_delegate.h
@@ -8,9 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/single_thread_task_runner.h"
-#include "build/build_config.h"
 #include "content/public/app/content_main.h"
 #include "services/service_manager/embedder/main_delegate.h"
 
@@ -28,19 +25,7 @@
   bool IsEmbedderSubprocess() override;
   int RunEmbedderProcess() override;
   void ShutDownEmbedderProcess() override;
-  service_manager::ProcessType OverrideProcessType() override;
   void InitializeMojo(mojo::core::Configuration* config) override;
-  std::vector<service_manager::Manifest> GetServiceManifests() override;
-  bool ShouldLaunchAsServiceProcess(
-      const service_manager::Identity& identity) override;
-  void AdjustServiceProcessCommandLine(
-      const service_manager::Identity& identity,
-      base::CommandLine* command_line) override;
-  void OnServiceManagerInitialized(
-      base::OnceClosure quit_closure,
-      service_manager::BackgroundServiceManager* service_manager) override;
-  std::unique_ptr<service_manager::Service> CreateEmbeddedService(
-      const std::string& service_name) override;
 
   // Sets the flag whether to start the Service Manager without starting the
   // full browser.
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 745affc..ca7aa3a 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -309,6 +309,8 @@
       return "succeeded";
     case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
       return "waitUntil rejected";
+    case blink::ServiceWorkerStatusCode::kErrorFailed:
+      return "failed";
     case blink::ServiceWorkerStatusCode::kErrorAbort:
       return "aborted";
     case blink::ServiceWorkerStatusCode::kErrorTimeout:
diff --git a/content/browser/bluetooth/frame_connected_bluetooth_devices_unittest.cc b/content/browser/bluetooth/frame_connected_bluetooth_devices_unittest.cc
index e24ed2b..018530f8 100644
--- a/content/browser/bluetooth/frame_connected_bluetooth_devices_unittest.cc
+++ b/content/browser/bluetooth/frame_connected_bluetooth_devices_unittest.cc
@@ -43,7 +43,7 @@
 mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient>
 CreateServerClient() {
   mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient> client;
-  ignore_result(client.BindNewEndpointAndPassDedicatedReceiverForTesting());
+  ignore_result(client.BindNewEndpointAndPassDedicatedReceiver());
   return client;
 }
 
diff --git a/content/browser/media/midi_host.cc b/content/browser/media/midi_host.cc
index 60ff8b2..c6e0e155 100644
--- a/content/browser/media/midi_host.cc
+++ b/content/browser/media/midi_host.cc
@@ -205,15 +205,13 @@
   // Check |has_sys_ex_permission_| first to avoid searching kSysExByte in large
   // bulk data transfers for correct uses.
   if (!has_sys_ex_permission_ && base::Contains(data, kSysExByte)) {
+    has_sys_ex_permission_ =
+        ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMidiSysExMessage(
+            renderer_process_id_);
     if (!has_sys_ex_permission_) {
-      has_sys_ex_permission_ =
-          ChildProcessSecurityPolicyImpl::GetInstance()
-              ->CanSendMidiSysExMessage(renderer_process_id_);
-      if (!has_sys_ex_permission_) {
-        bad_message::ReceivedBadMessage(renderer_process_id_,
-                                        bad_message::MH_SYS_EX_PERMISSION);
-        return;
-      }
+      bad_message::ReceivedBadMessage(renderer_process_id_,
+                                      bad_message::MH_SYS_EX_PERMISSION);
+      return;
     }
   }
 
diff --git a/content/browser/renderer_host/mock_render_widget_host.cc b/content/browser/renderer_host/mock_render_widget_host.cc
index 0441b9c2..fa6d0ca 100644
--- a/content/browser/renderer_host/mock_render_widget_host.cc
+++ b/content/browser/renderer_host/mock_render_widget_host.cc
@@ -44,7 +44,7 @@
     int32_t routing_id) {
   mojo::AssociatedRemote<blink::mojom::Widget> blink_widget;
   auto blink_widget_receiver =
-      blink_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      blink_widget.BindNewEndpointAndPassDedicatedReceiver();
   return new MockRenderWidgetHost(delegate, agent_scheduling_group, routing_id,
                                   blink_widget.Unbind());
 }
@@ -83,7 +83,7 @@
   acked_touch_event_type_ = blink::WebInputEvent::Type::kUndefined;
   mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
   BindWidgetInterfaces(
-      blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+      blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
       std::move(pending_blink_widget));
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 95637432d..245f0f8 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1699,8 +1699,8 @@
     } else {
       // The channel may not be initialized in some tests environments. In this
       // case we set up a dummy interface provider.
-      ignore_result(remote_interfaces
-                        .BindNewEndpointAndPassDedicatedReceiverForTesting());
+      ignore_result(
+          remote_interfaces.BindNewEndpointAndPassDedicatedReceiver());
     }
     remote_associated_interfaces_ =
         std::make_unique<blink::AssociatedInterfaceProvider>(
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 64d4487..b73b351 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -312,8 +312,8 @@
     } else {
       // The channel may not be initialized in some tests environments. In this
       // case we set up a dummy interface provider.
-      ignore_result(remote_interfaces
-                        .BindNewEndpointAndPassDedicatedReceiverForTesting());
+      ignore_result(
+          remote_interfaces.BindNewEndpointAndPassDedicatedReceiver());
     }
     remote_associated_interfaces_ =
         std::make_unique<blink::AssociatedInterfaceProvider>(
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
index 094d542..8e2552e 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
@@ -238,17 +238,17 @@
     mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
     mojo::AssociatedRemote<blink::mojom::Widget> blink_widget;
     auto blink_widget_receiver =
-        blink_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        blink_widget.BindNewEndpointAndPassDedicatedReceiver();
     widget_host_root_->BindWidgetInterfaces(
-        blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         blink_widget.Unbind());
 
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     widget_host_root_->BindFrameWidgetInterfaces(
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         frame_widget.Unbind());
 
     view_root_ =
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 09dc83f0..fff56d3 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -605,15 +605,15 @@
   void ReinitalizeHost() {
     mojo::AssociatedRemote<blink::mojom::WidgetHost> widget_host;
     host_->BindWidgetInterfaces(
-        widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         widget_.GetNewRemote());
 
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     host_->BindFrameWidgetInterfaces(
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         frame_widget.Unbind());
 
     host_->Init();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index c4ddc90..3b0bbf3a 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -432,7 +432,7 @@
     lastWheelOrTouchEventLatencyInfo = ui::LatencyInfo();
     mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
     BindWidgetInterfaces(
-        blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         widget_.GetNewRemote());
   }
 
@@ -556,11 +556,10 @@
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost>
         parent_frame_widget_host;
     auto parent_frame_widget_host_receiver =
-        parent_frame_widget_host
-            .BindNewEndpointAndPassDedicatedReceiverForTesting();
+        parent_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
     mojo::AssociatedRemote<blink::mojom::FrameWidget> parent_frame_widget;
     auto parent_frame_widget_receiver =
-        parent_frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        parent_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     parent_host_->BindFrameWidgetInterfaces(
         std::move(parent_frame_widget_host_receiver),
         parent_frame_widget.Unbind());
@@ -575,10 +574,10 @@
     widget_host_ = static_cast<MockRenderWidgetHostImpl*>(view_->host());
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     auto frame_widget_host_receiver =
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     widget_host_->BindFrameWidgetInterfaces(
         std::move(frame_widget_host_receiver), frame_widget.Unbind());
     // Set the mouse_wheel_phase_handler_ timer timeout to 100ms.
@@ -6132,11 +6131,10 @@
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost>
         blink_frame_widget_host;
     auto blink_frame_widget_host_receiver =
-        blink_frame_widget_host
-            .BindNewEndpointAndPassDedicatedReceiverForTesting();
+        blink_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
     mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
     auto blink_frame_widget_receiver =
-        blink_frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        blink_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
 
     static_cast<RenderWidgetHostImpl*>(views_[index]->GetRenderWidgetHost())
         ->BindFrameWidgetInterfaces(std::move(blink_frame_widget_host_receiver),
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
index 363f5e0..e51fa31 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -129,15 +129,15 @@
 
     mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
     widget_host_->BindWidgetInterfaces(
-        blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         widget_.GetNewRemote());
 
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     widget_host_->BindFrameWidgetInterfaces(
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         frame_widget.Unbind());
 
     blink::ScreenInfo screen_info;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 5c4f7ee..9955638 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -366,10 +366,10 @@
                              std::make_unique<FrameTokenMessageQueue>()) {
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     auto frame_widget_host_receiver =
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     BindFrameWidgetInterfaces(std::move(frame_widget_host_receiver),
                               frame_widget.Unbind());
 
@@ -500,10 +500,10 @@
         process_host_->GetNextRoutingID()));
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     auto frame_widget_host_receiver =
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     host_->BindFrameWidgetInterfaces(std::move(frame_widget_host_receiver),
                                      frame_widget.Unbind());
     host_->set_owner_delegate(&mock_owner_delegate_);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index d76fe04..77d1cd5 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4235,10 +4235,13 @@
     return;
 
   waiting_for_response_ = false;
-  if (delegate_)
-    delegate_->LoadingStateChanged(this, is_load_to_different_document_);
   observers_.ForEachObserver(
       [&](WebContentsObserver* observer) { observer->DidReceiveResponse(); });
+
+  // LoadingStateChanged must be called last in case it triggers deletion of
+  // |this| due to recursive message pumps.
+  if (delegate_)
+    delegate_->LoadingStateChanged(this, is_load_to_different_document_);
 }
 
 void WebContentsImpl::SendScreenRects() {
@@ -5309,6 +5312,8 @@
             : false);
   }
 
+  // LoadingStateChanged must be called last in case it triggers deletion of
+  // |this| due to recursive message pumps.
   SetNotWaitingForResponse();
 }
 
diff --git a/content/browser/web_contents_receiver_set_browsertest.cc b/content/browser/web_contents_receiver_set_browsertest.cc
index c40eeaec..00a73421 100644
--- a/content/browser/web_contents_receiver_set_browsertest.cc
+++ b/content/browser/web_contents_receiver_set_browsertest.cc
@@ -97,7 +97,7 @@
       ->OnAssociatedInterfaceRequest(
           web_contents->GetMainFrame(),
           mojom::BrowserAssociatedInterfaceTestDriver::Name_,
-          override_client.BindNewEndpointAndPassDedicatedReceiverForTesting()
+          override_client.BindNewEndpointAndPassDedicatedReceiver()
               .PassHandle());
   run_loop.Run();
 
@@ -119,7 +119,7 @@
       ->OnAssociatedInterfaceRequest(
           web_contents->GetMainFrame(),
           mojom::WebContentsFrameReceiverSetTest::Name_,
-          override_client.BindNewEndpointAndPassDedicatedReceiverForTesting()
+          override_client.BindNewEndpointAndPassDedicatedReceiver()
               .PassHandle());
 
   base::RunLoop run_loop;
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index eb0485f..571b3b14 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -12,6 +12,7 @@
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/memory/weak_ptr.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/power_monitor/power_monitor_device_source.h"
@@ -35,6 +36,8 @@
 #include "mojo/public/cpp/bindings/binder_map.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h"
+#include "mojo/public/cpp/system/functions.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "services/metrics/public/mojom/ukm_interface.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -49,6 +52,13 @@
 namespace content {
 namespace {
 
+// Called when the GPU process receives a bad IPC message.
+void HandleBadMessage(const std::string& error) {
+  LOG(ERROR) << "Mojo error in GPU process: " << error;
+  mojo::debug::ScopedMessageErrorCrashKey crash_key_value(error);
+  base::debug::DumpWithoutCrashing();
+}
+
 ChildThreadImpl::Options GetOptions() {
   ChildThreadImpl::Options::Builder builder;
 
@@ -118,6 +128,9 @@
 GpuChildThread::~GpuChildThread() = default;
 
 void GpuChildThread::Init(const base::Time& process_start_time) {
+  if (!in_process_gpu())
+    mojo::SetDefaultProcessErrorHandler(base::BindRepeating(&HandleBadMessage));
+
   viz_main_.gpu_service()->set_start_time(process_start_time);
 
   // When running in in-process mode, this has been set in the browser at
diff --git a/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java b/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
index 62ff4f75..b5618cf 100644
--- a/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
@@ -16,7 +16,6 @@
 import androidx.core.provider.FontsContractCompat.FontFamilyResult;
 import androidx.core.provider.FontsContractCompat.FontInfo;
 
-import org.chromium.base.Consumer;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.task.PostTask;
@@ -24,14 +23,18 @@
 import org.chromium.blink.mojom.AndroidFontLookup;
 import org.chromium.content.R;
 import org.chromium.mojo.bindings.ExecutorFactory;
+import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MojoException;
 import org.chromium.mojo.system.impl.CoreImpl;
 import org.chromium.mojo_base.mojom.File;
 import org.chromium.services.service_manager.InterfaceFactory;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -45,7 +48,10 @@
     private final Context mAppContext;
     private FontsContractWrapper mFontsContract = new FontsContractWrapper();
     /**
-     * Map from ICU case folded full font name to GMS Core font provider query format.
+     * Map from ICU case folded full font names to corresponding GMS Core font provider query.
+     *
+     * This collection of Android Downloadable fonts should match the fonts listed in
+     * preloaded_fonts.xml.
      */
     private Map<String, String> mFullFontNameToQuery = createFullFontNameToQueryMap();
 
@@ -54,86 +60,109 @@
     }
 
     /**
-     * Synchronously gets a list of unique font names that are available from GMS Core and can be
-     * fetched quickly.
+     * Verifies which fonts are available from GMS Core and can be fetched quickly, and
+     * asynchronously responds with that list. These fonts should have already been preloaded via
+     * the "preloaded_fonts" AndroidManifest directive. This second programmatic prefetch request is
+     * necessary to confirm whether those fonts were successfully downloaded and are now available.
      *
-     * @param callback The callback to be called with the list of font names.
+     * TODO(chouinard): Consider requiring the returned list to be sorted.
+     *
+     * @param callback The callback to be called with the list of available fonts.
      */
     @Override
     public void getUniqueNameLookupTable(GetUniqueNameLookupTableResponse callback) {
-        // TODO(crbug.com/1111148): Verify which fonts were successfully preloaded and are available
-        // on-device to populate this list.
-        String[] results = new String[0];
-        callback.call(results);
+        // Get executor associated with the current thread for running Mojo callback.
+        Executor executor = ExecutorFactory.getExecutorForCurrentThread(CoreImpl.getInstance());
+
+        PostTask.postTask(TaskTraits.BEST_EFFORT, () -> {
+            Set<String> expectedFonts = mFullFontNameToQuery.keySet();
+            List<String> availableFonts = new ArrayList<>();
+
+            for (String fontName : expectedFonts) {
+                if (tryFetchFont(fontName) != null) {
+                    availableFonts.add(fontName);
+                }
+            }
+
+            String[] results = availableFonts.toArray(new String[availableFonts.size()]);
+            executor.execute(() -> callback.call(results));
+        });
     }
 
     /**
      * Fetches the requested font from GMS Core on a background thread.
      *
      * @param fontUniqueName The unique full font name requested.
-     * @param callback The callback to be called with the resulting font file handle, or null if the
-     *         font file is not available.
+     * @param callback The callback to be called with the resulting opened font file handle, or null
+     *         if the font file is not available. Caller is responsible for closing file when done.
      */
     @Override
     public void matchLocalFontByUniqueName(
             String fontUniqueName, MatchLocalFontByUniqueNameResponse callback) {
+        // Get executor associated with the current thread for running Mojo callback.
+        Core core = CoreImpl.getInstance();
+        Executor executor = ExecutorFactory.getExecutorForCurrentThread(core);
+
+        // Post synchronous font request to background worker thread.
+        PostTask.postTask(TaskTraits.USER_BLOCKING, () -> {
+            ParcelFileDescriptor fileDescriptor = tryFetchFont(fontUniqueName);
+            if (fileDescriptor == null) {
+                executor.execute(() -> callback.call(null));
+                return;
+            }
+
+            // Wrap file descriptor as an opened Mojo file handle.
+            File file = new File();
+            file.fd = core.wrapFileDescriptor(fileDescriptor);
+            file.async = false;
+
+            executor.execute(() -> callback.call(file));
+        });
+    }
+
+    private ParcelFileDescriptor tryFetchFont(String fontUniqueName) {
         String query = mFullFontNameToQuery.get(fontUniqueName);
         if (query == null) {
-            Log.d(TAG, "Query format not found for full font name: %s.", fontUniqueName);
-            callback.call(null);
-            return;
+            Log.d(TAG, "Query format not found for full font name: %s", fontUniqueName);
+            return null;
         }
 
         FontRequest request = new FontRequest("com.google.android.gms.fonts",
                 "com.google.android.gms", query, R.array.com_google_android_gms_fonts_certs);
 
-        // Get executor associated with the current thread for running Mojo callback.
-        Executor executor = ExecutorFactory.getExecutorForCurrentThread(CoreImpl.getInstance());
-        Consumer<File> consumer = (File file) -> {
-            executor.execute(() -> callback.call(file));
-        };
-
-        // Execute synchronous font request on background worker thread.
-        PostTask.postTask(TaskTraits.USER_BLOCKING, () -> { tryFetchFont(request, consumer); });
-    }
-
-    private void tryFetchFont(FontRequest request, Consumer<File> consumer) {
         try {
             FontFamilyResult fontFamilyResult =
                     mFontsContract.fetchFonts(mAppContext, null, request);
 
             if (fontFamilyResult.getStatusCode() != FontFamilyResult.STATUS_OK) {
-                Log.d(TAG, "Font fetch failed with status code %d.",
+                Log.d(TAG, "Font fetch failed with status code: %d",
                         fontFamilyResult.getStatusCode());
-                consumer.accept(null);
-                return;
+                return null;
             }
 
             FontInfo[] fontInfos = fontFamilyResult.getFonts();
             if (fontInfos.length != 1) {
                 Log.d(TAG, "Font fetch did not return a unique result: length = %d",
                         fontInfos.length);
-                consumer.accept(null);
-                return;
+                return null;
             }
 
             FontInfo fontInfo = fontInfos[0];
             if (fontInfo.getResultCode() != FontsContractCompat.Columns.RESULT_CODE_OK) {
-                Log.d(TAG, "Returned font has failed status code: %d.", fontInfo.getResultCode());
-                consumer.accept(null);
-                return;
+                Log.d(TAG, "Returned font has failed status code: %d", fontInfo.getResultCode());
+                return null;
             }
 
             ContentResolver contentResolver = mAppContext.getContentResolver();
             ParcelFileDescriptor fileDescriptor =
                     contentResolver.openFileDescriptor(fontInfo.getUri(), READ_ONLY_MODE);
-            File file = new File();
-            file.fd = CoreImpl.getInstance().wrapFileDescriptor(fileDescriptor);
-            file.async = false;
-            consumer.accept(file);
+            if (fileDescriptor == null) {
+                Log.d(TAG, "Unable to open font file at: %s", fontInfo.getUri());
+            }
+            return fileDescriptor;
         } catch (NameNotFoundException | IOException | OutOfMemoryError e) {
             Log.d(TAG, "Failed to get font with: %s", e.toString());
-            consumer.accept(null);
+            return null;
         }
     }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java b/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
index 749ed5d1..55ff74c 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.content.browser.font;
 
-import static junit.framework.Assert.assertEquals;
-
+import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
@@ -43,6 +42,7 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.blink.mojom.AndroidFontLookup;
+import org.chromium.blink.mojom.AndroidFontLookup.GetUniqueNameLookupTableResponse;
 import org.chromium.blink.mojom.AndroidFontLookup.MatchLocalFontByUniqueNameResponse;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -57,7 +57,9 @@
     private static final String FONT_QUERY = "name=Foo&weight=400";
     private static final String AUTHORITY = "com.google.android.gms.fonts";
     private static final Uri URI = Uri.parse("content://com.google.android.gms.fonts/123");
+    private static final Uri URI2 = Uri.parse("content://com.google.android.gms.fonts/456");
     private static final int FD = 42;
+    private static final int FD2 = 43;
     private static final long RUN_LOOP_TIMEOUT_MS = 50;
 
     @Rule
@@ -67,9 +69,13 @@
     private FontsContractWrapper mMockFontsContractWrapper;
     @Mock
     private ParcelFileDescriptor mMockFileDescriptor;
+    @Mock
+    private ParcelFileDescriptor mMockFileDescriptor2;
     private Context mMockContext;
 
     @Mock
+    private GetUniqueNameLookupTableResponse mGetUniqueNameLookupTableCallback;
+    @Mock
     private MatchLocalFontByUniqueNameResponse mMatchLocalFontByUniqueNameCallback;
 
     private AndroidFontLookupImpl mAndroidFontLookup;
@@ -83,11 +89,17 @@
         MockContentResolver resolver = new MockContentResolver();
         MockContext mockContext = new MockContext();
         when(mMockFileDescriptor.detachFd()).thenReturn(FD);
+        when(mMockFileDescriptor2.detachFd()).thenReturn(FD2);
         resolver.addProvider(AUTHORITY, new MockContentProvider(mockContext) {
             @Override
             public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) {
-                assertEquals(URI, url);
-                return new AssetFileDescriptor(mMockFileDescriptor, 0, -1);
+                if (url.equals(URI)) {
+                    return new AssetFileDescriptor(mMockFileDescriptor, 0, -1);
+                } else if (url.equals(URI2)) {
+                    return new AssetFileDescriptor(mMockFileDescriptor2, 0, -1);
+                } else {
+                    return null;
+                }
             }
         });
         mMockContext = new IsolatedContext(resolver, mockContext);
@@ -100,8 +112,73 @@
 
     @SmallTest
     @Test
+    public void testGetUniqueNameLookupTable_Empty() throws NameNotFoundException {
+        String[] expected = new String[0];
+        FontFamilyResult result = new FontFamilyResult(FontFamilyResult.STATUS_OK, new FontInfo[0]);
+        whenFetchFontsWith(FONT_QUERY).thenReturn(result);
+
+        mAndroidFontLookup.getUniqueNameLookupTable(mGetUniqueNameLookupTableCallback);
+
+        mMojoTestRule.runLoop(RUN_LOOP_TIMEOUT_MS);
+        verify(mGetUniqueNameLookupTableCallback).call(aryEq(expected));
+    }
+
+    @SmallTest
+    @Test
+    public void testGetUniqueNameLookupTable_Available() throws NameNotFoundException {
+        String[] expected = new String[] {FULL_FONT_NAME};
+
+        FontInfo fontInfo = new FontInfo(URI, 0, 400, false, Columns.RESULT_CODE_OK);
+        FontFamilyResult result =
+                new FontFamilyResult(FontFamilyResult.STATUS_OK, new FontInfo[] {fontInfo});
+        whenFetchFontsWith(FONT_QUERY).thenReturn(result);
+
+        mAndroidFontLookup.getUniqueNameLookupTable(mGetUniqueNameLookupTableCallback);
+
+        mMojoTestRule.runLoop(RUN_LOOP_TIMEOUT_MS);
+        verify(mGetUniqueNameLookupTableCallback).call(aryEq(expected));
+    }
+
+    @SmallTest
+    @Test
+    public void testGetUniqueNameLookupTable_MultipleFonts() throws NameNotFoundException {
+        String fullFontName2 = "bar";
+        String fontQuery2 = "name=Bar&weight=400";
+        String fullFontName3 = "bar bold";
+        String fontQuery3 = "name=Bar&weight=700";
+
+        String[] expected = new String[] {FULL_FONT_NAME, fullFontName2};
+
+        mAndroidFontLookup.setFullFontNameToQueryMapForTest(ImmutableMap.of(
+                FULL_FONT_NAME, FONT_QUERY, fullFontName2, fontQuery2, fullFontName3, fontQuery3));
+
+        // Foo is available.
+        FontInfo fontInfo = new FontInfo(URI, 0, 400, false, Columns.RESULT_CODE_OK);
+        FontFamilyResult result =
+                new FontFamilyResult(FontFamilyResult.STATUS_OK, new FontInfo[] {fontInfo});
+        whenFetchFontsWith(FONT_QUERY).thenReturn(result);
+
+        // Bar is available.
+        FontInfo fontInfo2 = new FontInfo(URI2, 0, 400, false, Columns.RESULT_CODE_OK);
+        FontFamilyResult result2 =
+                new FontFamilyResult(FontFamilyResult.STATUS_OK, new FontInfo[] {fontInfo2});
+        whenFetchFontsWith(fontQuery2).thenReturn(result2);
+
+        // Bar Bold is not available.
+        FontFamilyResult result3 =
+                new FontFamilyResult(FontFamilyResult.STATUS_OK, new FontInfo[0]);
+        whenFetchFontsWith(fontQuery3).thenReturn(result3);
+
+        mAndroidFontLookup.getUniqueNameLookupTable(mGetUniqueNameLookupTableCallback);
+
+        mMojoTestRule.runLoop(RUN_LOOP_TIMEOUT_MS);
+        verify(mGetUniqueNameLookupTableCallback).call(aryEq(expected));
+    }
+
+    @SmallTest
+    @Test
     public void testMatchLocalFontByUniqueName_UnsupportedFontName() {
-        mAndroidFontLookup.matchLocalFontByUniqueName("Bar", mMatchLocalFontByUniqueNameCallback);
+        mAndroidFontLookup.matchLocalFontByUniqueName("bar", mMatchLocalFontByUniqueNameCallback);
 
         mMojoTestRule.runLoop(RUN_LOOP_TIMEOUT_MS);
         verify(mMatchLocalFontByUniqueNameCallback,
diff --git a/content/public/app/content_main_delegate.cc b/content/public/app/content_main_delegate.cc
index 57f1dcf..e6379748 100644
--- a/content/public/app/content_main_delegate.cc
+++ b/content/public/app/content_main_delegate.cc
@@ -36,18 +36,6 @@
   return 0;
 }
 
-service_manager::ProcessType ContentMainDelegate::OverrideProcessType() {
-  return service_manager::ProcessType::kDefault;
-}
-
-void ContentMainDelegate::AdjustServiceProcessCommandLine(
-    const service_manager::Identity& identity,
-    base::CommandLine* command_line) {}
-
-void ContentMainDelegate::OnServiceManagerInitialized(
-    base::OnceClosure quit_closure,
-    service_manager::BackgroundServiceManager* service_manager) {}
-
 bool ContentMainDelegate::ShouldCreateFeatureList() {
   return true;
 }
diff --git a/content/public/app/content_main_delegate.h b/content/public/app/content_main_delegate.h
index a384cd8..abb46995 100644
--- a/content/public/app/content_main_delegate.h
+++ b/content/public/app/content_main_delegate.h
@@ -9,19 +9,8 @@
 #include <string>
 #include <vector>
 
-#include "base/callback_forward.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
-#include "services/service_manager/embedder/process_type.h"
-
-namespace base {
-class CommandLine;
-}
-
-namespace service_manager {
-class BackgroundServiceManager;
-class Identity;
-}  // namespace service_manager
 
 namespace content {
 
@@ -76,25 +65,6 @@
   // returning initialization error code. Default behavior is CHECK(false).
   virtual int TerminateForFatalInitializationError();
 
-  // Overrides the Service Manager process type to use for the currently running
-  // process.
-  virtual service_manager::ProcessType OverrideProcessType();
-
-  // Allows the content embedder to adjust arbitrary command line arguments for
-  // any service process started by the Service Manager.
-  virtual void AdjustServiceProcessCommandLine(
-      const service_manager::Identity& identity,
-      base::CommandLine* command_line);
-
-  // Allows the embedder to perform arbitrary initialization within the Service
-  // Manager process immediately before the Service Manager runs its main loop.
-  //
-  // |quit_closure| is a callback the embedder may retain and invoke at any time
-  // to cleanly terminate Service Manager execution.
-  virtual void OnServiceManagerInitialized(
-      base::OnceClosure quit_closure,
-      service_manager::BackgroundServiceManager* service_manager);
-
   // Allows the embedder to perform platform-specific initialization before
   // creating the main message loop.
   virtual void PreCreateMainMessageLoop() {}
diff --git a/content/public/browser/dedicated_worker_service.h b/content/public/browser/dedicated_worker_service.h
index de8bec1..3f6cad0 100644
--- a/content/public/browser/dedicated_worker_service.h
+++ b/content/public/browser/dedicated_worker_service.h
@@ -33,11 +33,9 @@
 
     // Called when the final response URL (the URL after redirects) was
     // determined when fetching the worker's script.
-    //
-    // TODO(pmonette): Implement this in derived classes and make it pure.
     virtual void OnFinalResponseURLDetermined(
         const blink::DedicatedWorkerToken& worker_token,
-        const GURL& url) {}
+        const GURL& url) = 0;
   };
 
   // Adds/removes an observer.
diff --git a/content/public/browser/desktop_media_id.cc b/content/public/browser/desktop_media_id.cc
index c36a81c..31fb1bc 100644
--- a/content/public/browser/desktop_media_id.cc
+++ b/content/public/browser/desktop_media_id.cc
@@ -22,11 +22,6 @@
 const char kScreenPrefix[] = "screen";
 const char kWindowPrefix[] = "window";
 
-// static
-const DesktopMediaID::Id DesktopMediaID::kNullId = 0;
-// static
-const DesktopMediaID::Id DesktopMediaID::kFakeId = -3;
-
 #if defined(USE_AURA) || defined(OS_MAC)
 // static
 DesktopMediaID DesktopMediaID::RegisterNativeWindow(DesktopMediaID::Type type,
@@ -58,6 +53,10 @@
          audio_share == other.audio_share;
 }
 
+bool DesktopMediaID::operator!=(const DesktopMediaID& other) const {
+  return !(*this == other);
+}
+
 // static
 // Input string should in format:
 // for WebContents:
diff --git a/content/public/browser/desktop_media_id.h b/content/public/browser/desktop_media_id.h
index 43c1fa44..c68f089 100644
--- a/content/public/browser/desktop_media_id.h
+++ b/content/public/browser/desktop_media_id.h
@@ -24,9 +24,9 @@
   typedef intptr_t Id;
 
   // Represents an "unset" value for either |id| or |window_id|.
-  static const Id kNullId;
+  static constexpr Id kNullId = 0;
   // Represents a fake id to create a dummy capturer for autotests.
-  static const Id kFakeId;
+  static constexpr Id kFakeId = -3;
 
 #if defined(USE_AURA) || defined(OS_MAC)
   // Assigns integer identifier to the |window| and returns its DesktopMediaID.
@@ -53,6 +53,7 @@
   // Operators so that DesktopMediaID can be used with STL containers.
   bool operator<(const DesktopMediaID& other) const;
   bool operator==(const DesktopMediaID& other) const;
+  bool operator!=(const DesktopMediaID& other) const;
 
   bool is_null() const { return type == TYPE_NONE; }
   std::string ToString() const;
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 15b5686..1638afd 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -4,6 +4,7 @@
 
 #include "content/public/test/browser_test_base.h"
 
+#include <fcntl.h>
 #include <stddef.h>
 
 #include <iostream>
@@ -16,6 +17,8 @@
 #include "base/command_line.h"
 #include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
 #include "base/i18n/icu_util.h"
 #include "base/location.h"
 #include "base/macros.h"
@@ -33,6 +36,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/network_service_instance_impl.h"
@@ -62,6 +66,8 @@
 #include "media/base/media_switches.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/features.h"
@@ -109,6 +115,14 @@
 #include "ui/aura/test/event_generator_delegate_aura.h"  // nogncheck
 #endif
 
+#if BUILDFLAG(IS_LACROS)
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/socket_utils_posix.h"
+#endif
+
 namespace content {
 namespace {
 
@@ -338,6 +352,45 @@
     use_software_gl = false;
 #endif
 
+#if BUILDFLAG(IS_LACROS)
+  // If the test is running on the lacros environment, a file descriptor needs
+  // to be obtained and used to launch lacros-chrome so that a mojo connection
+  // between lacros-chrome and ash-chrome can be established.
+  // For more details, please see:
+  // //chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h.
+  {
+    // TODO(crbug.com/1127581): Switch to use |kLacrosMojoSocketForTesting| in
+    // //chromeos/constants/chromeos_switches.h.
+    // Please refer to the CL comments for why it can't be done now:
+    // http://crrev.com/c/2402580/2/content/public/test/browser_test_base.cc
+    std::string socket_path =
+        command_line->GetSwitchValueASCII("lacros-mojo-socket-for-testing");
+    if (!socket_path.empty()) {
+      auto channel = mojo::NamedPlatformChannel::ConnectToServer(socket_path);
+      base::ScopedFD socket_fd = channel.TakePlatformHandle().TakeFD();
+
+      // Mark the channel as blocking.
+      int flags = fcntl(socket_fd.get(), F_GETFL);
+      PCHECK(flags != -1);
+      fcntl(socket_fd.get(), F_SETFL, flags & ~O_NONBLOCK);
+
+      uint8_t buf[32];
+      std::vector<base::ScopedFD> descriptors;
+      auto size = mojo::SocketRecvmsg(socket_fd.get(), buf, sizeof(buf),
+                                      &descriptors, true /*block*/);
+      if (size < 0)
+        PLOG(ERROR) << "Error receiving message from the socket";
+      ASSERT_EQ(1, size);
+      EXPECT_EQ(0u, buf[0]);
+      ASSERT_EQ(1u, descriptors.size());
+      // It's OK to release the FD because lacros-chrome's code will consume it.
+      command_line->AppendSwitchASCII(
+          mojo::PlatformChannel::kHandleSwitch,
+          base::NumberToString(descriptors[0].release()));
+    }
+  }
+#endif
+
   if (use_software_gl && !use_software_compositing_)
     command_line->AppendSwitch(switches::kOverrideUseSoftwareGLForTests);
 
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 0f770ba..539cefca 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -688,12 +688,11 @@
 
   mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> blink_frame_widget_host;
   auto blink_frame_widget_host_receiver =
-      blink_frame_widget_host
-          .BindNewEndpointAndPassDedicatedReceiverForTesting();
+      blink_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
   auto blink_frame_widget_receiver =
-      blink_frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      blink_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
 
   render_widget_host_impl->BindFrameWidgetInterfaces(
       std::move(blink_frame_widget_host_receiver), blink_frame_widget.Unbind());
diff --git a/content/public/test/fake_render_widget_host.cc b/content/public/test/fake_render_widget_host.cc
index b0dbf39..c9cd06e1 100644
--- a/content/public/test/fake_render_widget_host.cc
+++ b/content/public/test/fake_render_widget_host.cc
@@ -18,9 +18,8 @@
   frame_widget_host_receiver_.reset();
   frame_widget_remote_.reset();
   return std::make_pair(
-      frame_widget_host_receiver_
-          .BindNewEndpointAndPassDedicatedRemoteForTesting(),
-      frame_widget_remote_.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      frame_widget_host_receiver_.BindNewEndpointAndPassDedicatedRemote(),
+      frame_widget_remote_.BindNewEndpointAndPassDedicatedReceiver());
 }
 
 std::pair<mojo::PendingAssociatedRemote<blink::mojom::WidgetHost>,
@@ -29,8 +28,8 @@
   widget_host_receiver_.reset();
   widget_remote_.reset();
   return std::make_pair(
-      widget_host_receiver_.BindNewEndpointAndPassDedicatedRemoteForTesting(),
-      widget_remote_.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      widget_host_receiver_.BindNewEndpointAndPassDedicatedRemote(),
+      widget_remote_.BindNewEndpointAndPassDedicatedReceiver());
 }
 
 void FakeRenderWidgetHost::AnimateDoubleTapZoomInMainFrame(
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 28f1fdc..e77f6c9 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -412,8 +412,8 @@
   if (!renderer_interface_) {
     renderer_interface_ =
         std::make_unique<mojo::AssociatedRemote<mojom::Renderer>>();
-    ignore_result(renderer_interface_
-                      ->BindNewEndpointAndPassDedicatedReceiverForTesting());
+    ignore_result(
+        renderer_interface_->BindNewEndpointAndPassDedicatedReceiver());
   }
   return renderer_interface_->get();
 }
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 2ed3635..a81284d3 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -335,14 +335,12 @@
   mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
   mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
       blink_frame_widget_receiver =
-          blink_frame_widget
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          blink_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> blink_frame_widget_host;
   mojo::PendingAssociatedReceiver<blink::mojom::FrameWidgetHost>
       blink_frame_widget_host_receiver =
-          blink_frame_widget_host
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          blink_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   reply->frame_widget = std::move(blink_frame_widget_receiver);
   reply->frame_widget_host = blink_frame_widget_host.Unbind();
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index e82a95a..e162bc5 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -1262,14 +1262,12 @@
   mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
   mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
       blink_frame_widget_receiver =
-          blink_frame_widget
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          blink_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> blink_frame_widget_host;
   mojo::PendingAssociatedReceiver<blink::mojom::FrameWidgetHost>
       blink_frame_widget_host_receiver =
-          blink_frame_widget_host
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          blink_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   widget_params->frame_widget = std::move(blink_frame_widget_receiver);
   widget_params->frame_widget_host = blink_frame_widget_host.Unbind();
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 1f6665da..1a1860b 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -210,23 +210,21 @@
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget_remote;
     mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
         frame_widget_receiver =
-            frame_widget_remote
-                .BindNewEndpointAndPassDedicatedReceiverForTesting();
+            frame_widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     mojo::PendingAssociatedReceiver<blink::mojom::FrameWidgetHost>
         frame_widget_host_receiver =
-            frame_widget_host
-                .BindNewEndpointAndPassDedicatedReceiverForTesting();
+            frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
     mojo::AssociatedRemote<blink::mojom::Widget> widget_remote;
     mojo::PendingAssociatedReceiver<blink::mojom::Widget> widget_receiver =
-        widget_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
     mojo::AssociatedRemote<blink::mojom::WidgetHost> widget_host;
     mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>
         widget_host_receiver =
-            widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+            widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
     web_view_ = blink::WebView::Create(/*client=*/&web_view_client_,
                                        /*is_hidden=*/false,
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index ebf0190..c26c0b5 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -332,8 +332,7 @@
     mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost>
         host_remote;
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-        host_receiver =
-            host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
 
     // (1) In the case there is no WebSWProviderClient but SWProviderContext for
     // the provider, the passed reference should be adopted and owned by the
@@ -341,7 +340,7 @@
     mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer>
         container_remote;
     auto container_receiver =
-        container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        container_remote.BindNewEndpointAndPassDedicatedReceiver();
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
         blink::mojom::ServiceWorkerContainerType::kForWindow,
         std::move(container_receiver), host_remote.Unbind(),
@@ -377,13 +376,12 @@
     mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost>
         host_remote;
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-        host_receiver =
-            host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
 
     mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer>
         container_remote;
     auto container_receiver =
-        container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        container_remote.BindNewEndpointAndPassDedicatedReceiver();
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
         blink::mojom::ServiceWorkerContainerType::kForWindow,
         std::move(container_receiver), host_remote.Unbind(),
@@ -411,12 +409,11 @@
 TEST_F(ServiceWorkerProviderContextTest, SetController_Null) {
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-      host_receiver =
-          host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
@@ -443,10 +440,10 @@
   // ServiceWorkerContainer request.
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   FakeServiceWorkerContainerHost host(
-      host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+      host_remote.BindNewEndpointAndPassDedicatedReceiver());
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   // (1) Test if setting the controller via the CTOR works.
 
@@ -659,7 +656,7 @@
 
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver),
@@ -682,12 +679,11 @@
 
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-      host_receiver =
-          host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
@@ -711,12 +707,11 @@
 TEST_F(ServiceWorkerProviderContextTest, CountFeature) {
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-      host_receiver =
-          host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       blink::mojom::ServiceWorkerContainerType::kForWindow,
       std::move(container_receiver), host_remote.Unbind(),
@@ -764,11 +759,10 @@
   // Make the container host and container pointers.
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-      host_receiver =
-          host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   // Make the provider context.
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
@@ -813,11 +807,10 @@
   // Make the container host and container pointers.
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainerHost> host_remote;
   mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
-      host_receiver =
-          host_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      host_receiver = host_remote.BindNewEndpointAndPassDedicatedReceiver();
   mojo::AssociatedRemote<blink::mojom::ServiceWorkerContainer> container_remote;
   auto container_receiver =
-      container_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      container_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   // Make the ServiceWorkerProviderContext, passing it the controller,
   // container, and container host.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 9f5dd16..444ee759 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -387,6 +387,7 @@
   deps = [
     ":content_test_mojo_bindings",
     "//base/third_party/dynamic_annotations",
+    "//build:chromeos_buildflags",
     "//cc:test_support",
     "//components/viz/client",
     "//components/viz/host",
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 85e4be0..00b788d2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -130,11 +130,13 @@
 crbug.com/1081973 [ win vulkan passthrough ] conformance2/textures/misc/compressed-tex-image.html [ Failure ]
 crbug.com/angleproject/4417 [ win d3d11 ] conformance2/rendering/framebuffer-render-to-layer.html [ Failure ]
 crbug.com/angleproject/4417 [ win d3d11 ] conformance2/rendering/framebuffer-render-to-layer-angle-issue.html [ Failure ]
+crbug.com/1127867 [ win d3d11 ] conformance2/textures/image_data/tex-3d-rgb565-rgb-unsigned_byte.html [ RetryOnFailure ]
 
 # Intel flaky issues
 crbug.com/1122744 [ win d3d11 intel ] conformance/textures/misc/texparameter-test.html [ RetryOnFailure ]
 crbug.com/912579 [ opengl win passthrough intel ] conformance2/rendering/out-of-bounds-index-buffers-after-copying.html [ RetryOnFailure ]
 crbug.com/1114780 [ win intel ] deqp/functional/gles3/multisample.html [ RetryOnFailure ]
+crbug.com/1127867 [ win d3d11 intel ] conformance2/textures/image_data/tex-3d-r16f-red-half_float.html [ RetryOnFailure ]
 # Flakily times out on driver 26.20.100.8141.
 crbug.com/1093482 [ win10 intel-0x3e92 ] deqp/functional/gles3/shadermatrix/div_dynamic.html [ RetryOnFailure ]
 crbug.com/1093482 [ win10 intel-0x5912 ] deqp/functional/gles3/shadermatrix/div_dynamic.html [ RetryOnFailure ]
diff --git a/content/test/mock_widget.cc b/content/test/mock_widget.cc
index 418be6b..e7e601d 100644
--- a/content/test/mock_widget.cc
+++ b/content/test/mock_widget.cc
@@ -12,7 +12,7 @@
 
 mojo::PendingAssociatedRemote<blink::mojom::Widget> MockWidget::GetNewRemote() {
   blink_widget_.reset();
-  return blink_widget_.BindNewEndpointAndPassDedicatedRemoteForTesting();
+  return blink_widget_.BindNewEndpointAndPassDedicatedRemote();
 }
 
 const std::vector<blink::VisualProperties>&
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 49b353a..1986f5ee 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -283,8 +283,7 @@
                                mojom::CommitNavigationParamsPtr commit_params) {
   mock_navigation_client_.reset();
   BindNavigationClient(
-      mock_navigation_client_
-          .BindNewEndpointAndPassDedicatedReceiverForTesting());
+      mock_navigation_client_.BindNewEndpointAndPassDedicatedReceiver());
   std::unique_ptr<blink::PendingURLLoaderFactoryBundle> pending_factory_bundle =
       ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
           std::make_unique<network::NotImplementedURLLoaderFactory>());
@@ -314,8 +313,7 @@
     const base::Optional<std::string>& error_page_content) {
   mock_navigation_client_.reset();
   BindNavigationClient(
-      mock_navigation_client_
-          .BindNewEndpointAndPassDedicatedReceiverForTesting());
+      mock_navigation_client_.BindNewEndpointAndPassDedicatedReceiver());
   std::unique_ptr<blink::PendingURLLoaderFactoryBundle> pending_factory_bundle =
       ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
           std::make_unique<network::NotImplementedURLLoaderFactory>());
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index 5cd069a9..01efd67 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -280,17 +280,17 @@
     mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
     mojo::AssociatedRemote<blink::mojom::Widget> blink_widget;
     auto blink_widget_receiver =
-        blink_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        blink_widget.BindNewEndpointAndPassDedicatedReceiver();
     GetWidget()->BindWidgetInterfaces(
-        blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         blink_widget.Unbind());
 
     mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
     mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
     auto frame_widget_receiver =
-        frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+        frame_widget.BindNewEndpointAndPassDedicatedReceiver();
     GetWidget()->BindFrameWidgetInterfaces(
-        frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        frame_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
         frame_widget.Unbind());
 
     main_frame->SetRenderFrameCreated(true);
diff --git a/content/test/test_render_widget_host.cc b/content/test/test_render_widget_host.cc
index f2940a9f..5e16091 100644
--- a/content/test/test_render_widget_host.cc
+++ b/content/test/test_render_widget_host.cc
@@ -33,9 +33,9 @@
   mojo::AssociatedRemote<blink::mojom::WidgetHost> blink_widget_host;
   mojo::AssociatedRemote<blink::mojom::Widget> blink_widget;
   auto blink_widget_receiver =
-      blink_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      blink_widget.BindNewEndpointAndPassDedicatedReceiver();
   BindWidgetInterfaces(
-      blink_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+      blink_widget_host.BindNewEndpointAndPassDedicatedReceiver(),
       blink_widget.Unbind());
 }
 
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 932276b..6396a142 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -20,6 +20,8 @@
 test("device_unittests") {
   sources = [
     "base/synchronization/one_writer_seqlock_unittest.cc",
+    "bluetooth/adapter_unittest.cc",
+    "bluetooth/advertisement_unittest.cc",
     "bluetooth/bluetooth_adapter_android_unittest.cc",
     "bluetooth/bluetooth_adapter_mac_metrics_unittest.mm",
     "bluetooth/bluetooth_adapter_mac_unittest.mm",
@@ -144,12 +146,14 @@
       "fido/ctap_request_unittest.cc",
       "fido/ctap_response_unittest.cc",
       "fido/fake_fido_discovery_unittest.cc",
+      "fido/fido_device_authenticator_unittest.cc",
       "fido/fido_device_discovery_unittest.cc",
       "fido/fido_parsing_utils_unittest.cc",
       "fido/fido_request_handler_unittest.cc",
       "fido/get_assertion_handler_unittest.cc",
       "fido/get_assertion_task_unittest.cc",
       "fido/hid/fido_hid_message_unittest.cc",
+      "fido/large_blob_unittest.cc",
       "fido/mac/browsing_data_deletion_unittest.mm",
       "fido/mac/credential_metadata_unittest.cc",
       "fido/mac/get_assertion_operation_unittest_mac.mm",
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index e7c5f8f..a27105b6 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -36,6 +36,8 @@
     "//device/bluetooth/public/mojom/gatt_result_type_converter.h",
     "adapter.cc",
     "adapter.h",
+    "advertisement.cc",
+    "advertisement.h",
     "device.cc",
     "device.h",
     "discovery_session.cc",
diff --git a/device/bluetooth/adapter.cc b/device/bluetooth/adapter.cc
index 4ff570e..13f0a70 100644
--- a/device/bluetooth/adapter.cc
+++ b/device/bluetooth/adapter.cc
@@ -12,6 +12,7 @@
 #include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
+#include "device/bluetooth/advertisement.h"
 #include "device/bluetooth/bluetooth_socket.h"
 #include "device/bluetooth/device.h"
 #include "device/bluetooth/discovery_session.h"
@@ -86,6 +87,31 @@
   std::move(callback).Run();
 }
 
+void Adapter::RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
+                                    const std::vector<uint8_t>& service_data,
+                                    RegisterAdvertisementCallback callback) {
+  auto advertisement_data =
+      std::make_unique<device::BluetoothAdvertisement::Data>(
+          device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+
+  auto uuid_list = std::make_unique<device::BluetoothAdvertisement::UUIDList>();
+  uuid_list->push_back(service_uuid.value());
+  advertisement_data->set_service_uuids(std::move(uuid_list));
+
+  auto service_data_map =
+      std::make_unique<device::BluetoothAdvertisement::ServiceData>();
+  service_data_map->emplace(service_uuid.value(), service_data);
+  advertisement_data->set_service_data(std::move(service_data_map));
+
+  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+  adapter_->RegisterAdvertisement(
+      std::move(advertisement_data),
+      base::BindOnce(&Adapter::OnRegisterAdvertisement,
+                     weak_ptr_factory_.GetWeakPtr(), copyable_callback),
+      base::BindOnce(&Adapter::OnRegisterAdvertisementError,
+                     weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+}
+
 void Adapter::SetDiscoverable(bool discoverable,
                               SetDiscoverableCallback callback) {
   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
@@ -205,6 +231,23 @@
                           /*device=*/mojo::NullRemote());
 }
 
+void Adapter::OnRegisterAdvertisement(
+    RegisterAdvertisementCallback callback,
+    scoped_refptr<device::BluetoothAdvertisement> advertisement) {
+  mojo::PendingRemote<mojom::Advertisement> pending_advertisement;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<Advertisement>(std::move(advertisement)),
+      pending_advertisement.InitWithNewPipeAndPassReceiver());
+  std::move(callback).Run(std::move(pending_advertisement));
+}
+
+void Adapter::OnRegisterAdvertisementError(
+    RegisterAdvertisementCallback callback,
+    device::BluetoothAdvertisement::ErrorCode error_code) {
+  DLOG(ERROR) << "Failed to register advertisement, error code: " << error_code;
+  std::move(callback).Run(/*advertisement=*/mojo::NullRemote());
+}
+
 void Adapter::OnSetDiscoverable(SetDiscoverableCallback callback) {
   std::move(callback).Run(/*success=*/true);
 }
diff --git a/device/bluetooth/adapter.h b/device/bluetooth/adapter.h
index 196ff885..f9d113a 100644
--- a/device/bluetooth/adapter.h
+++ b/device/bluetooth/adapter.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
 #include "device/bluetooth/bluetooth_gatt_connection.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/public/mojom/adapter.mojom.h"
@@ -37,6 +38,9 @@
   void GetInfo(GetInfoCallback callback) override;
   void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
                    AddObserverCallback callback) override;
+  void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
+                             const std::vector<uint8_t>& service_data,
+                             RegisterAdvertisementCallback callback) override;
   void SetDiscoverable(bool discoverable,
                        SetDiscoverableCallback callback) override;
   void SetName(const std::string& name, SetNameCallback callback) override;
@@ -74,6 +78,13 @@
   void OnConnectError(ConnectToDeviceCallback callback,
                       device::BluetoothDevice::ConnectErrorCode error_code);
 
+  void OnRegisterAdvertisement(
+      RegisterAdvertisementCallback callback,
+      scoped_refptr<device::BluetoothAdvertisement> advertisement);
+  void OnRegisterAdvertisementError(
+      RegisterAdvertisementCallback callback,
+      device::BluetoothAdvertisement::ErrorCode error_code);
+
   void OnSetDiscoverable(SetDiscoverableCallback callback);
   void OnSetDiscoverableError(SetDiscoverableCallback callback);
 
diff --git a/device/bluetooth/adapter_unittest.cc b/device/bluetooth/adapter_unittest.cc
new file mode 100644
index 0000000..e6e98d0
--- /dev/null
+++ b/device/bluetooth/adapter_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/adapter.h"
+
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_advertisement.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+namespace {
+
+const char kServiceId[] = "00000000-0000-0000-0000-000000000001";
+const char kDeviceServiceDataStr[] = "ServiceData";
+
+std::vector<uint8_t> GetByteVector(const std::string& str) {
+  return std::vector<uint8_t>(str.begin(), str.end());
+}
+
+class MockBluetoothAdapterWithAdvertisements
+    : public device::MockBluetoothAdapter {
+ public:
+  void RegisterAdvertisement(
+      std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
+      device::BluetoothAdapter::CreateAdvertisementCallback callback,
+      device::BluetoothAdapter::AdvertisementErrorCallback error_callback)
+      override {
+    last_register_advertisement_args_ =
+        std::make_pair(*advertisement_data->service_uuids(),
+                       *advertisement_data->service_data());
+
+    if (should_advertisement_registration_succeed_) {
+      std::move(callback).Run(
+          base::MakeRefCounted<device::MockBluetoothAdvertisement>());
+    } else {
+      std::move(error_callback)
+          .Run(device::BluetoothAdvertisement::ErrorCode::
+                   INVALID_ADVERTISEMENT_ERROR_CODE);
+    }
+  }
+
+  bool should_advertisement_registration_succeed_ = true;
+  base::Optional<std::pair<device::BluetoothAdvertisement::UUIDList,
+                           device::BluetoothAdvertisement::ServiceData>>
+      last_register_advertisement_args_;
+
+ protected:
+  ~MockBluetoothAdapterWithAdvertisements() override = default;
+};
+
+}  // namespace
+
+namespace bluetooth {
+
+class AdapterTest : public testing::Test {
+ public:
+  AdapterTest() = default;
+  ~AdapterTest() override = default;
+  AdapterTest(const AdapterTest&) = delete;
+  AdapterTest& operator=(const AdapterTest&) = delete;
+
+  void SetUp() override {
+    mock_bluetooth_adapter_ = base::MakeRefCounted<
+        NiceMock<MockBluetoothAdapterWithAdvertisements>>();
+    ON_CALL(*mock_bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
+    ON_CALL(*mock_bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
+
+    adapter_ = std::make_unique<Adapter>(mock_bluetooth_adapter_);
+  }
+
+ protected:
+  void VerifyRegisterAdvertisement(bool should_succeed) {
+    mock_bluetooth_adapter_->should_advertisement_registration_succeed_ =
+        should_succeed;
+
+    auto service_data = GetByteVector(kDeviceServiceDataStr);
+    mojo::Remote<mojom::Advertisement> advertisement;
+
+    base::RunLoop run_loop;
+    adapter_->RegisterAdvertisement(
+        device::BluetoothUUID(kServiceId), service_data,
+        base::BindLambdaForTesting([&](mojo::PendingRemote<mojom::Advertisement>
+                                           pending_advertisement) {
+          EXPECT_EQ(should_succeed, pending_advertisement.is_valid());
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+
+    auto& uuid_list =
+        mock_bluetooth_adapter_->last_register_advertisement_args_->first;
+    EXPECT_EQ(1u, uuid_list.size());
+    EXPECT_EQ(kServiceId, uuid_list[0]);
+    EXPECT_EQ(
+        service_data,
+        mock_bluetooth_adapter_->last_register_advertisement_args_->second.at(
+            kServiceId));
+  }
+
+  scoped_refptr<NiceMock<MockBluetoothAdapterWithAdvertisements>>
+      mock_bluetooth_adapter_;
+  std::unique_ptr<Adapter> adapter_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(AdapterTest, TestRegisterAdvertisement_Success) {
+  VerifyRegisterAdvertisement(/*should_succeed=*/true);
+}
+
+TEST_F(AdapterTest, TestRegisterAdvertisement_Error) {
+  VerifyRegisterAdvertisement(/*should_succeed=*/false);
+}
+
+}  // namespace bluetooth
diff --git a/device/bluetooth/advertisement.cc b/device/bluetooth/advertisement.cc
new file mode 100644
index 0000000..cdb8265
--- /dev/null
+++ b/device/bluetooth/advertisement.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/advertisement.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+
+namespace bluetooth {
+
+Advertisement::Advertisement(
+    scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement)
+    : bluetooth_advertisement_(std::move(bluetooth_advertisement)) {}
+
+Advertisement::~Advertisement() {
+  Unregister(base::DoNothing());
+}
+
+void Advertisement::Unregister(UnregisterCallback callback) {
+  if (!bluetooth_advertisement_)
+    return;
+
+  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
+  bluetooth_advertisement_->Unregister(
+      base::BindOnce(&Advertisement::OnUnregister,
+                     weak_ptr_factory_.GetWeakPtr(), copyable_callback),
+      base::BindOnce(&Advertisement::OnUnregisterError,
+                     weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+}
+
+void Advertisement::OnUnregister(UnregisterCallback callback) {
+  bluetooth_advertisement_.reset();
+  std::move(callback).Run();
+}
+
+void Advertisement::OnUnregisterError(
+    UnregisterCallback callback,
+    device::BluetoothAdvertisement::ErrorCode error_code) {
+  DLOG(ERROR) << "Failed to unregister advertisement, error code: "
+              << error_code;
+  bluetooth_advertisement_.reset();
+  std::move(callback).Run();
+}
+
+}  // namespace bluetooth
diff --git a/device/bluetooth/advertisement.h b/device/bluetooth/advertisement.h
new file mode 100644
index 0000000..7746681
--- /dev/null
+++ b/device/bluetooth/advertisement.h
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_ADVERTISEMENT_H_
+#define DEVICE_BLUETOOTH_ADVERTISEMENT_H_
+
+#include "base/memory/ref_counted.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+#include "device/bluetooth/public/mojom/adapter.mojom.h"
+
+namespace bluetooth {
+
+// Implementation of Mojo Advertisement in
+// device/bluetooth/public/mojom/adapter.mojom.
+// Uses the platform abstraction of //device/bluetooth.
+// An instance of this class is constructed by Adapter and strongly bound to its
+// MessagePipe. When the instance is destroyed, the underlying
+// BluetoothAdvertisement is destroyed.
+class Advertisement : public mojom::Advertisement {
+ public:
+  explicit Advertisement(
+      scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement);
+  ~Advertisement() override;
+  Advertisement(const Advertisement&) = delete;
+  Advertisement& operator=(const Advertisement&) = delete;
+
+  // mojom::Advertisement:
+  void Unregister(UnregisterCallback callback) override;
+
+ private:
+  void OnUnregister(UnregisterCallback callback);
+  void OnUnregisterError(UnregisterCallback callback,
+                         device::BluetoothAdvertisement::ErrorCode error_code);
+
+  scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement_;
+
+  base::WeakPtrFactory<Advertisement> weak_ptr_factory_{this};
+};
+
+}  // namespace bluetooth
+
+#endif  // DEVICE_BLUETOOTH_ADVERTISEMENT_H_
diff --git a/device/bluetooth/advertisement_unittest.cc b/device/bluetooth/advertisement_unittest.cc
new file mode 100644
index 0000000..2dd863c
--- /dev/null
+++ b/device/bluetooth/advertisement_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/advertisement.h"
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FakeBluetoothAdvertisement : public device::BluetoothAdvertisement {
+ public:
+  // device::BluetoothAdvertisement:
+  void Unregister(SuccessCallback success_callback,
+                  ErrorCallback error_callback) override {
+    called_unregister_ = true;
+    std::move(success_callback).Run();
+  }
+
+  bool called_unregister() { return called_unregister_; }
+
+ private:
+  ~FakeBluetoothAdvertisement() override = default;
+
+  bool called_unregister_ = false;
+};
+
+}  // namespace
+
+namespace bluetooth {
+
+class AdvertisementTest : public testing::Test {
+ public:
+  AdvertisementTest() = default;
+  ~AdvertisementTest() override = default;
+  AdvertisementTest(const AdvertisementTest&) = delete;
+  AdvertisementTest& operator=(const AdvertisementTest&) = delete;
+
+  void SetUp() override {
+    fake_bluetooth_advertisement_ =
+        base::MakeRefCounted<FakeBluetoothAdvertisement>();
+    advertisement_ =
+        std::make_unique<Advertisement>(fake_bluetooth_advertisement_);
+  }
+
+ protected:
+  scoped_refptr<FakeBluetoothAdvertisement> fake_bluetooth_advertisement_;
+  std::unique_ptr<Advertisement> advertisement_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(AdvertisementTest, TestOnDestroyCallsUnregister) {
+  // When destroyed, |advertisement_| is expected to tear down its
+  // BluetoothAdvertisement.
+  ASSERT_FALSE(fake_bluetooth_advertisement_->called_unregister());
+  advertisement_.reset();
+  EXPECT_TRUE(fake_bluetooth_advertisement_->called_unregister());
+}
+
+TEST_F(AdvertisementTest, TestUnregister) {
+  ASSERT_FALSE(fake_bluetooth_advertisement_->called_unregister());
+  base::RunLoop run_loop;
+  advertisement_->Unregister(run_loop.QuitClosure());
+  run_loop.Run();
+  EXPECT_TRUE(fake_bluetooth_advertisement_->called_unregister());
+}
+
+}  // namespace bluetooth
diff --git a/device/bluetooth/public/mojom/adapter.mojom b/device/bluetooth/public/mojom/adapter.mojom
index 24a9ab1..5d7fc04a 100644
--- a/device/bluetooth/public/mojom/adapter.mojom
+++ b/device/bluetooth/public/mojom/adapter.mojom
@@ -49,6 +49,22 @@
   bool discovering;
 };
 
+// Represents an ongoing BLE advertisement. Releasing it will release the
+// underlying object and stop the advertisement, but callers should prefer to
+// let a call to Unregister() to finish first.
+// Note: Methods which are declared [Sync] are for use by
+// //chrome/services/sharing/nearby; all other usage of their synchronous
+// signatures is strongly discouraged.
+interface Advertisement {
+  // Use to gracefully stop advertising before destroying the message pipe. The
+  // reply callback can be used to synchronize an attempt to re-register an
+  // advertisement; attempting to register an advertisement without first
+  // releasing limited advertisement slots in the hardware may fail with a
+  // busy error.
+  [Sync]
+  Unregister() => ();
+};
+
 // Represents a request to discover nearby devices.
 // Note: Methods which are declared [Sync] are for use by
 // //chrome/services/sharing/nearby; all other usage of their synchronous
@@ -128,6 +144,22 @@
   [Sync]
   AddObserver(pending_remote<AdapterObserver> observer) => ();
 
+  // Requests the adapter to broadcast a BLE advertisement on |service_id| with
+  // the associated packet |service_data|. Returns null if advertisement is not
+  // registered successfully.
+  // Important notes:
+  // * This method registers a "non-connectable" advertisement. Any future
+  //   effort to allow this API to support "connectable" advertisements would
+  //   also require the addition of an API to support hosting a GATT server.
+  // * Bluetooth chips generally can only broadcast a few advertisements,
+  //   sometimes even only one, simultaneously. This can be mitigated by
+  //   operating systems "rotating" advertisements in the higher software layer,
+  //   as Chrome OS does. Non-Chrome OS clients of this API are responsible for
+  //   understanding their host OS's and/or hardware's limitations.
+  [Sync]
+  RegisterAdvertisement(UUID service_id, array<uint8> service_data) =>
+      (pending_remote<Advertisement>? advertisement);
+
   // Requests the local device to make itself discoverable to nearby remote
   // devices.
   [Sync]
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn
index a5a5d64b..0c071c7 100644
--- a/device/fido/BUILD.gn
+++ b/device/fido/BUILD.gn
@@ -160,6 +160,8 @@
       "hid/fido_hid_message.h",
       "hid/fido_hid_packet.cc",
       "hid/fido_hid_packet.h",
+      "large_blob.cc",
+      "large_blob.h",
       "make_credential_request_handler.cc",
       "make_credential_request_handler.h",
       "make_credential_task.cc",
diff --git a/device/fido/bio/enrollment.h b/device/fido/bio/enrollment.h
index 92ff22a0..77ee7dc 100644
--- a/device/fido/bio/enrollment.h
+++ b/device/fido/bio/enrollment.h
@@ -145,7 +145,7 @@
   ~BioEnrollmentRequest();
 
  private:
-  BioEnrollmentRequest(Version);
+  explicit BioEnrollmentRequest(Version);
 };
 
 struct COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentResponse {
diff --git a/device/fido/ctap_get_assertion_request.h b/device/fido/ctap_get_assertion_request.h
index 72858c0..bec6388 100644
--- a/device/fido/ctap_get_assertion_request.h
+++ b/device/fido/ctap_get_assertion_request.h
@@ -19,6 +19,7 @@
 #include "device/fido/cable/cable_discovery_data.h"
 #include "device/fido/client_data.h"
 #include "device/fido/fido_constants.h"
+#include "device/fido/large_blob.h"
 #include "device/fido/pin.h"
 #include "device/fido/public_key_credential_descriptor.h"
 
@@ -57,6 +58,10 @@
   // it will be the first element and all others will have |credential_id|s.
   // Elements are sorted by |credential_id|s, where present.
   std::vector<PRFInput> prf_inputs;
+
+  // large_blob_operation indicates whether we should attempt to read or write a
+  // large blob after a successful assertion.
+  LargeBlobOperation large_blob_operation;
 };
 
 // Object that encapsulates request parameters for AuthenticatorGetAssertion as
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc
index 7e6e910..002fc03 100644
--- a/device/fido/device_response_converter.cc
+++ b/device/fido/device_response_converter.cc
@@ -67,7 +67,7 @@
     return CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
 
   auto code = static_cast<CtapDeviceResponseCode>(buffer[0]);
-  return base::Contains(GetCtapResponseCodeList(), code)
+  return base::Contains(kCtapResponseCodeList, code)
              ? code
              : CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
 }
diff --git a/device/fido/fido_authenticator.cc b/device/fido/fido_authenticator.cc
index 653b48f..abd4025 100644
--- a/device/fido/fido_authenticator.cc
+++ b/device/fido/fido_authenticator.cc
@@ -128,6 +128,21 @@
   NOTREACHED();
 }
 
+void FidoAuthenticator::WriteLargeBlob(
+    const std::vector<uint8_t>& large_blob,
+    const LargeBlobKey& large_blob_key,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    base::OnceCallback<void(CtapDeviceResponseCode)> callback) {
+  NOTREACHED();
+}
+
+void FidoAuthenticator::ReadLargeBlob(
+    const std::vector<const LargeBlobKey>& large_blob_keys,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    LargeBlobReadCallback callback) {
+  NOTREACHED();
+}
+
 base::Optional<base::span<const int32_t>> FidoAuthenticator::GetAlgorithms() {
   return base::nullopt;
 }
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index ad98403..71657de4 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_FIDO_AUTHENTICATOR_H_
 #define DEVICE_FIDO_FIDO_AUTHENTICATOR_H_
 
+#include <cstdint>
 #include <string>
 
 #include "base/callback_forward.h"
@@ -20,8 +21,10 @@
 #include "device/fido/authenticator_supported_options.h"
 #include "device/fido/bio/enrollment.h"
 #include "device/fido/credential_management.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_request_handler_base.h"
 #include "device/fido/fido_transport_protocol.h"
+#include "device/fido/large_blob.h"
 
 namespace device {
 
@@ -70,6 +73,10 @@
   using BioEnrollmentCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
                               base::Optional<BioEnrollmentResponse>)>;
+  using LargeBlobReadCallback = base::OnceCallback<void(
+      CtapDeviceResponseCode,
+      base::Optional<std::vector<std::pair<LargeBlobKey, std::vector<uint8_t>>>>
+          callback)>;
 
   FidoAuthenticator() = default;
   virtual ~FidoAuthenticator() = default;
@@ -202,6 +209,21 @@
                                std::vector<uint8_t> template_id,
                                BioEnrollmentCallback);
 
+  // Large blob commands.
+  // Attempts to write a |large_blob| into the credential. If there is an
+  // existing credential for the |large_blob_key|, it will be overwritten.
+  virtual void WriteLargeBlob(
+      const std::vector<uint8_t>& large_blob,
+      const LargeBlobKey& large_blob_key,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      base::OnceCallback<void(CtapDeviceResponseCode)> callback);
+  // Attempts to read large blobs from the credential encrypted with
+  // |large_blob_keys|. Returns a map of keys to their blobs.
+  virtual void ReadLargeBlob(
+      const std::vector<const LargeBlobKey>& large_blob_keys,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      LargeBlobReadCallback callback);
+
   // GetAlgorithms returns the list of supported COSEAlgorithmIdentifiers, or
   // |nullopt| if this is unknown and thus all requests should be tried in case
   // they work.
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index 2f03c37d..b7c62f0 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -88,7 +88,7 @@
   kCtap2ErrLimitExceeded = 0x15,
   kCtap2ErrUnsupportedExtension = 0x16,
   kCtap2ErrTooManyElements = 0x17,
-  kCtap2ErrExtensionNotSupported = 0x18,
+  kCtap2ErrLargeBlobStorageFull = 0x18,
   kCtap2ErrCredentialExcluded = 0x19,
   kCtap2ErrProcesssing = 0x21,
   kCtap2ErrInvalidCredential = 0x22,
@@ -116,6 +116,7 @@
   kCtap2ErrPinTokenExpired = 0x38,
   kCtap2ErrRequestTooLarge = 0x39,
   kCtap2ErrUvBlocked = 0x3C,
+  kCtap2ErrIntegrityFailure = 0x3D,
   kCtap2ErrOther = 0x7F,
   kCtap2ErrSpecLast = 0xDF,
   kCtap2ErrExtensionFirst = 0xE0,
@@ -124,57 +125,58 @@
   kCtap2ErrVendorLast = 0xFF
 };
 
-constexpr std::array<CtapDeviceResponseCode, 49> GetCtapResponseCodeList() {
-  return {CtapDeviceResponseCode::kSuccess,
-          CtapDeviceResponseCode::kCtap1ErrInvalidCommand,
-          CtapDeviceResponseCode::kCtap1ErrInvalidParameter,
-          CtapDeviceResponseCode::kCtap1ErrInvalidLength,
-          CtapDeviceResponseCode::kCtap1ErrInvalidSeq,
-          CtapDeviceResponseCode::kCtap1ErrTimeout,
-          CtapDeviceResponseCode::kCtap1ErrChannelBusy,
-          CtapDeviceResponseCode::kCtap1ErrLockRequired,
-          CtapDeviceResponseCode::kCtap1ErrInvalidChannel,
-          CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType,
-          CtapDeviceResponseCode::kCtap2ErrInvalidCBOR,
-          CtapDeviceResponseCode::kCtap2ErrMissingParameter,
-          CtapDeviceResponseCode::kCtap2ErrLimitExceeded,
-          CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension,
-          CtapDeviceResponseCode::kCtap2ErrTooManyElements,
-          CtapDeviceResponseCode::kCtap2ErrExtensionNotSupported,
-          CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
-          CtapDeviceResponseCode::kCtap2ErrProcesssing,
-          CtapDeviceResponseCode::kCtap2ErrInvalidCredential,
-          CtapDeviceResponseCode::kCtap2ErrUserActionPending,
-          CtapDeviceResponseCode::kCtap2ErrOperationPending,
-          CtapDeviceResponseCode::kCtap2ErrNoOperations,
-          CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm,
-          CtapDeviceResponseCode::kCtap2ErrOperationDenied,
-          CtapDeviceResponseCode::kCtap2ErrKeyStoreFull,
-          CtapDeviceResponseCode::kCtap2ErrNotBusy,
-          CtapDeviceResponseCode::kCtap2ErrNoOperationPending,
-          CtapDeviceResponseCode::kCtap2ErrUnsupportedOption,
-          CtapDeviceResponseCode::kCtap2ErrInvalidOption,
-          CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel,
-          CtapDeviceResponseCode::kCtap2ErrNoCredentials,
-          CtapDeviceResponseCode::kCtap2ErrUserActionTimeout,
-          CtapDeviceResponseCode::kCtap2ErrNotAllowed,
-          CtapDeviceResponseCode::kCtap2ErrPinInvalid,
-          CtapDeviceResponseCode::kCtap2ErrPinBlocked,
-          CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid,
-          CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked,
-          CtapDeviceResponseCode::kCtap2ErrPinNotSet,
-          CtapDeviceResponseCode::kCtap2ErrPinRequired,
-          CtapDeviceResponseCode::kCtap2ErrPinPolicyViolation,
-          CtapDeviceResponseCode::kCtap2ErrPinTokenExpired,
-          CtapDeviceResponseCode::kCtap2ErrRequestTooLarge,
-          CtapDeviceResponseCode::kCtap2ErrUvBlocked,
-          CtapDeviceResponseCode::kCtap2ErrOther,
-          CtapDeviceResponseCode::kCtap2ErrSpecLast,
-          CtapDeviceResponseCode::kCtap2ErrExtensionFirst,
-          CtapDeviceResponseCode::kCtap2ErrExtensionLast,
-          CtapDeviceResponseCode::kCtap2ErrVendorFirst,
-          CtapDeviceResponseCode::kCtap2ErrVendorLast};
-}
+constexpr std::array<CtapDeviceResponseCode, 50> kCtapResponseCodeList{
+    CtapDeviceResponseCode::kSuccess,
+    CtapDeviceResponseCode::kCtap1ErrInvalidCommand,
+    CtapDeviceResponseCode::kCtap1ErrInvalidParameter,
+    CtapDeviceResponseCode::kCtap1ErrInvalidLength,
+    CtapDeviceResponseCode::kCtap1ErrInvalidSeq,
+    CtapDeviceResponseCode::kCtap1ErrTimeout,
+    CtapDeviceResponseCode::kCtap1ErrChannelBusy,
+    CtapDeviceResponseCode::kCtap1ErrLockRequired,
+    CtapDeviceResponseCode::kCtap1ErrInvalidChannel,
+    CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType,
+    CtapDeviceResponseCode::kCtap2ErrInvalidCBOR,
+    CtapDeviceResponseCode::kCtap2ErrMissingParameter,
+    CtapDeviceResponseCode::kCtap2ErrLimitExceeded,
+    CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension,
+    CtapDeviceResponseCode::kCtap2ErrTooManyElements,
+    CtapDeviceResponseCode::kCtap2ErrLargeBlobStorageFull,
+    CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
+    CtapDeviceResponseCode::kCtap2ErrProcesssing,
+    CtapDeviceResponseCode::kCtap2ErrInvalidCredential,
+    CtapDeviceResponseCode::kCtap2ErrUserActionPending,
+    CtapDeviceResponseCode::kCtap2ErrOperationPending,
+    CtapDeviceResponseCode::kCtap2ErrNoOperations,
+    CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm,
+    CtapDeviceResponseCode::kCtap2ErrOperationDenied,
+    CtapDeviceResponseCode::kCtap2ErrKeyStoreFull,
+    CtapDeviceResponseCode::kCtap2ErrNotBusy,
+    CtapDeviceResponseCode::kCtap2ErrNoOperationPending,
+    CtapDeviceResponseCode::kCtap2ErrUnsupportedOption,
+    CtapDeviceResponseCode::kCtap2ErrInvalidOption,
+    CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel,
+    CtapDeviceResponseCode::kCtap2ErrNoCredentials,
+    CtapDeviceResponseCode::kCtap2ErrUserActionTimeout,
+    CtapDeviceResponseCode::kCtap2ErrNotAllowed,
+    CtapDeviceResponseCode::kCtap2ErrPinInvalid,
+    CtapDeviceResponseCode::kCtap2ErrPinBlocked,
+    CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid,
+    CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked,
+    CtapDeviceResponseCode::kCtap2ErrPinNotSet,
+    CtapDeviceResponseCode::kCtap2ErrPinRequired,
+    CtapDeviceResponseCode::kCtap2ErrPinPolicyViolation,
+    CtapDeviceResponseCode::kCtap2ErrPinTokenExpired,
+    CtapDeviceResponseCode::kCtap2ErrRequestTooLarge,
+    CtapDeviceResponseCode::kCtap2ErrUvBlocked,
+    CtapDeviceResponseCode::kCtap2ErrIntegrityFailure,
+    CtapDeviceResponseCode::kCtap2ErrOther,
+    CtapDeviceResponseCode::kCtap2ErrSpecLast,
+    CtapDeviceResponseCode::kCtap2ErrExtensionFirst,
+    CtapDeviceResponseCode::kCtap2ErrExtensionLast,
+    CtapDeviceResponseCode::kCtap2ErrVendorFirst,
+    CtapDeviceResponseCode::kCtap2ErrVendorLast,
+};
 
 // Commands supported by CTAPHID device as specified in
 // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#ctaphid-commands
@@ -232,6 +234,7 @@
   kAuthenticatorClientPin = 0x06,
   kAuthenticatorReset = 0x07,
   kAuthenticatorBioEnrollment = 0x09,
+  kAuthenticatorLargeBlobs = 0x0C,
   kAuthenticatorBioEnrollmentPreview = 0x40,
   kAuthenticatorCredentialManagement = 0x0a,
   kAuthenticatorCredentialManagementPreview = 0x41,
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index 2779c22a..99ce668 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -4,6 +4,7 @@
 
 #include "device/fido/fido_device_authenticator.h"
 
+#include <algorithm>
 #include <numeric>
 #include <utility>
 
@@ -16,9 +17,11 @@
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/features.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_device.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/get_assertion_task.h"
+#include "device/fido/large_blob.h"
 #include "device/fido/make_credential_task.h"
 #include "device/fido/pin.h"
 #include "device/fido/u2f_command_constructor.h"
@@ -730,6 +733,193 @@
       std::move(callback), base::BindOnce(&BioEnrollmentResponse::Parse));
 }
 
+void FidoDeviceAuthenticator::WriteLargeBlob(
+    const std::vector<uint8_t>& large_blob,
+    const LargeBlobKey& large_blob_key,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    base::OnceCallback<void(CtapDeviceResponseCode)> callback) {
+  auto pin_uv_auth_token_copy = pin_uv_auth_token;
+  FetchLargeBlobArray(
+      pin_uv_auth_token_copy, LargeBlobArrayReader(),
+      base::BindOnce(&FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite,
+                     weak_factory_.GetWeakPtr(), large_blob, large_blob_key,
+                     std::move(pin_uv_auth_token), std::move(callback)));
+}
+
+void FidoDeviceAuthenticator::ReadLargeBlob(
+    const std::vector<const LargeBlobKey>& large_blob_keys,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    LargeBlobReadCallback callback) {
+  FetchLargeBlobArray(
+      std::move(pin_uv_auth_token), LargeBlobArrayReader(),
+      base::BindOnce(&FidoDeviceAuthenticator::OnHaveLargeBlobArrayForRead,
+                     weak_factory_.GetWeakPtr(), large_blob_keys,
+                     std::move(callback)));
+}
+
+void FidoDeviceAuthenticator::FetchLargeBlobArray(
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    LargeBlobArrayReader large_blob_array_reader,
+    base::OnceCallback<void(CtapDeviceResponseCode,
+                            base::Optional<LargeBlobArrayReader>)> callback) {
+  size_t bytes_to_read = max_large_blob_fragment_length();
+  LargeBlobsRequest request =
+      LargeBlobsRequest::ForRead(bytes_to_read, large_blob_array_reader.size());
+  if (pin_uv_auth_token) {
+    request.SetPinParam(*pin_uv_auth_token);
+  }
+  RunOperation<LargeBlobsRequest, LargeBlobsResponse>(
+      std::move(request),
+      base::BindOnce(&FidoDeviceAuthenticator::OnReadLargeBlobFragment,
+                     weak_factory_.GetWeakPtr(), bytes_to_read,
+                     std::move(large_blob_array_reader),
+                     std::move(pin_uv_auth_token), std::move(callback)),
+      base::BindOnce(&LargeBlobsResponse::ParseForRead, bytes_to_read));
+}
+
+void FidoDeviceAuthenticator::OnReadLargeBlobFragment(
+    const size_t bytes_requested,
+    LargeBlobArrayReader large_blob_array_reader,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    base::OnceCallback<void(CtapDeviceResponseCode,
+                            base::Optional<LargeBlobArrayReader>)> callback,
+    CtapDeviceResponseCode status,
+    base::Optional<LargeBlobsResponse> response) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    std::move(callback).Run(status, base::nullopt);
+    return;
+  }
+
+  DCHECK(response && response->config());
+  large_blob_array_reader.Append(*response->config());
+
+  if (response->config()->size() == bytes_requested) {
+    // More data may be available, read the next fragment.
+    FetchLargeBlobArray(std::move(pin_uv_auth_token),
+                        std::move(large_blob_array_reader),
+                        std::move(callback));
+    return;
+  }
+
+  std::move(callback).Run(CtapDeviceResponseCode::kSuccess,
+                          std::move(large_blob_array_reader));
+}
+
+void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
+    const std::vector<uint8_t>& large_blob,
+    const LargeBlobKey& large_blob_key,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    base::OnceCallback<void(CtapDeviceResponseCode)> callback,
+    CtapDeviceResponseCode status,
+    base::Optional<LargeBlobArrayReader> large_blob_array_reader) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    std::move(callback).Run(status);
+    return;
+  }
+
+  base::Optional<std::vector<LargeBlobData>> large_blob_array =
+      large_blob_array_reader->Materialize();
+  if (!large_blob_array) {
+    // The large blob array is corrupted. Replace it completely with a new one.
+    // TODO(nsatragno): but maybe we want to do something else like trying
+    // again? It might have been corrupted while transported. Decide when we
+    // have hardware to test.
+    large_blob_array.emplace();
+    return;
+  }
+
+  auto existing_large_blob =
+      std::find_if(large_blob_array->begin(), large_blob_array->end(),
+                   [&large_blob_key](const LargeBlobData& blob) {
+                     return blob.Decrypt(large_blob_key);
+                   });
+
+  LargeBlobData new_large_blob_data(large_blob_key, large_blob);
+  if (existing_large_blob != large_blob_array->end()) {
+    *existing_large_blob = std::move(new_large_blob_data);
+  } else {
+    large_blob_array->emplace_back(std::move(new_large_blob_data));
+  }
+
+  WriteLargeBlobArray(std::move(pin_uv_auth_token),
+                      LargeBlobArrayWriter(*large_blob_array),
+                      std::move(callback));
+}
+
+void FidoDeviceAuthenticator::WriteLargeBlobArray(
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    LargeBlobArrayWriter large_blob_array_writer,
+    base::OnceCallback<void(CtapDeviceResponseCode)> callback) {
+  LargeBlobArrayFragment fragment =
+      large_blob_array_writer.Pop(max_large_blob_fragment_length());
+
+  LargeBlobsRequest request = LargeBlobsRequest::ForWrite(
+      std::move(fragment), large_blob_array_writer.size());
+  if (pin_uv_auth_token) {
+    request.SetPinParam(*pin_uv_auth_token);
+  }
+  RunOperation<LargeBlobsRequest, LargeBlobsResponse>(
+      std::move(request),
+      base::BindOnce(&FidoDeviceAuthenticator::OnWriteLargeBlobFragment,
+                     weak_factory_.GetWeakPtr(),
+                     std::move(large_blob_array_writer),
+                     std::move(pin_uv_auth_token), std::move(callback)),
+      base::BindOnce(&LargeBlobsResponse::ParseForWrite));
+}
+
+void FidoDeviceAuthenticator::OnWriteLargeBlobFragment(
+    LargeBlobArrayWriter large_blob_array_writer,
+    const base::Optional<pin::TokenResponse> pin_uv_auth_token,
+    base::OnceCallback<void(CtapDeviceResponseCode)> callback,
+    CtapDeviceResponseCode status,
+    base::Optional<LargeBlobsResponse> response) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    std::move(callback).Run(status);
+    return;
+  }
+
+  if (large_blob_array_writer.has_remaining_fragments()) {
+    WriteLargeBlobArray(std::move(pin_uv_auth_token),
+                        std::move(large_blob_array_writer),
+                        std::move(callback));
+    return;
+  }
+
+  std::move(callback).Run(CtapDeviceResponseCode::kSuccess);
+}
+
+void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForRead(
+    const std::vector<const LargeBlobKey>& large_blob_keys,
+    LargeBlobReadCallback callback,
+    CtapDeviceResponseCode status,
+    base::Optional<LargeBlobArrayReader> large_blob_array_reader) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    std::move(callback).Run(status, base::nullopt);
+    return;
+  }
+
+  base::Optional<std::vector<LargeBlobData>> large_blob_array =
+      large_blob_array_reader->Materialize();
+  if (!large_blob_array) {
+    std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrIntegrityFailure,
+                            base::nullopt);
+    return;
+  }
+
+  std::vector<std::pair<LargeBlobKey, std::vector<uint8_t>>> result;
+  for (const LargeBlobData& blob : *large_blob_array) {
+    for (const LargeBlobKey& key : large_blob_keys) {
+      base::Optional<std::vector<uint8_t>> plaintext = blob.Decrypt(key);
+      if (plaintext) {
+        result.emplace_back(std::make_pair(key, std::move(*plaintext)));
+        break;
+      }
+    }
+  }
+
+  std::move(callback).Run(CtapDeviceResponseCode::kSuccess, std::move(result));
+}
+
 base::Optional<base::span<const int32_t>>
 FidoDeviceAuthenticator::GetAlgorithms() {
   if (device_->supported_protocol() == ProtocolVersion::kU2f) {
@@ -883,6 +1073,13 @@
       base::BindOnce(&pin::TokenResponse::Parse, std::move(shared_key)));
 }
 
+size_t FidoDeviceAuthenticator::max_large_blob_fragment_length() {
+  return device_->device_info()->max_msg_size
+             ? *device_->device_info()->max_msg_size -
+                   kLargeBlobReadEncodingOverhead
+             : kLargeBlobDefaultMaxFragmentLength;
+}
+
 base::WeakPtr<FidoAuthenticator> FidoDeviceAuthenticator::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index 8a2a26d..286640c 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -17,7 +17,10 @@
 #include "build/build_config.h"
 #include "device/fido/ctap2_device_operation.h"
 #include "device/fido/fido_authenticator.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_request_handler_base.h"
+#include "device/fido/large_blob.h"
+#include "device/fido/pin.h"
 
 namespace device {
 
@@ -93,6 +96,14 @@
   void BioEnrollDelete(const pin::TokenResponse&,
                        std::vector<uint8_t> template_id,
                        BioEnrollmentCallback) override;
+  void WriteLargeBlob(
+      const std::vector<uint8_t>& large_blob,
+      const LargeBlobKey& large_blob_key,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      base::OnceCallback<void(CtapDeviceResponseCode)> callback) override;
+  void ReadLargeBlob(const std::vector<const LargeBlobKey>& large_blob_keys,
+                     base::Optional<pin::TokenResponse> pin_uv_auth_token,
+                     LargeBlobReadCallback callback) override;
 
   base::Optional<base::span<const int32_t>> GetAlgorithms() override;
   void Reset(ResetCallback callback) override;
@@ -171,6 +182,42 @@
       CtapDeviceResponseCode status,
       base::Optional<pin::KeyAgreementResponse> key);
 
+  void FetchLargeBlobArray(
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      LargeBlobArrayReader large_blob_array_reader,
+      base::OnceCallback<void(CtapDeviceResponseCode,
+                              base::Optional<LargeBlobArrayReader>)> callback);
+  void WriteLargeBlobArray(
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      LargeBlobArrayWriter large_blob_array_writer,
+      base::OnceCallback<void(CtapDeviceResponseCode)> callback);
+  void OnReadLargeBlobFragment(
+      const size_t bytes_requested,
+      LargeBlobArrayReader large_blob_array_reader,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      base::OnceCallback<void(CtapDeviceResponseCode,
+                              base::Optional<LargeBlobArrayReader>)> callback,
+      CtapDeviceResponseCode status,
+      base::Optional<LargeBlobsResponse> response);
+  void OnWriteLargeBlobFragment(
+      LargeBlobArrayWriter large_blob_array_writer,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      base::OnceCallback<void(CtapDeviceResponseCode)> callback,
+      CtapDeviceResponseCode status,
+      base::Optional<LargeBlobsResponse> response);
+  void OnHaveLargeBlobArrayForWrite(
+      const std::vector<uint8_t>& large_blob,
+      const LargeBlobKey& large_blob_key,
+      base::Optional<pin::TokenResponse> pin_uv_auth_token,
+      base::OnceCallback<void(CtapDeviceResponseCode)> callback,
+      CtapDeviceResponseCode status,
+      base::Optional<LargeBlobArrayReader> large_blob_array_reader);
+  void OnHaveLargeBlobArrayForRead(
+      const std::vector<const LargeBlobKey>& large_blob_keys,
+      LargeBlobReadCallback callback,
+      CtapDeviceResponseCode status,
+      base::Optional<LargeBlobArrayReader> large_blob_array_reader);
+
   template <typename... Args>
   void TaskClearProxy(base::OnceCallback<void(Args...)> callback, Args... args);
   template <typename... Args>
@@ -198,6 +245,8 @@
       CtapDeviceResponseCode status,
       base::Optional<EnumerateCredentialsResponse> response);
 
+  size_t max_large_blob_fragment_length();
+
   const std::unique_ptr<FidoDevice> device_;
   base::Optional<AuthenticatorSupportedOptions> options_;
   std::unique_ptr<FidoTask> task_;
diff --git a/device/fido/fido_device_authenticator_unittest.cc b/device/fido/fido_device_authenticator_unittest.cc
new file mode 100644
index 0000000..b9076a8
--- /dev/null
+++ b/device/fido/fido_device_authenticator_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/fido_device_authenticator.h"
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
+#include "base/test/task_environment.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/large_blob.h"
+#include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_ctap2_device.h"
+#include "device/fido/virtual_fido_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+using WriteCallback =
+    device::test::ValueCallbackReceiver<CtapDeviceResponseCode>;
+using ReadCallback = device::test::StatusAndValueCallbackReceiver<
+    CtapDeviceResponseCode,
+    base::Optional<std::vector<std::pair<LargeBlobKey, std::vector<uint8_t>>>>>;
+
+constexpr LargeBlobKey kDummyKey = {{0x01}};
+constexpr std::array<uint8_t, 4> kSmallBlob = {'l', 'u', 'm', 'a'};
+constexpr size_t kMaxStorageSize = 4096;
+
+class FidoDeviceAuthenticatorTest : public testing::Test {
+ public:
+  void SetUp() override {
+    VirtualCtap2Device::Config config;
+    config.pin_support = true;
+    config.large_blob_support = true;
+    config.resident_key_support = true;
+    config.available_large_blob_storage = kMaxStorageSize;
+
+    authenticator_state_ = base::MakeRefCounted<VirtualFidoDevice::State>();
+    auto virtual_device =
+        std::make_unique<VirtualCtap2Device>(authenticator_state_, config);
+    authenticator_ =
+        std::make_unique<FidoDeviceAuthenticator>(std::move(virtual_device));
+
+    device::test::TestCallbackReceiver<> callback;
+    authenticator_->InitializeAuthenticator(callback.callback());
+    callback.WaitForCallback();
+  }
+
+ protected:
+  void SetPin() {
+    authenticator_state_->pin = "1234";
+    authenticator_state_->pin_retries = device::kMaxPinRetries;
+  }
+
+  scoped_refptr<VirtualFidoDevice::State> authenticator_state_;
+  std::unique_ptr<FidoDeviceAuthenticator> authenticator_;
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) {
+  ReadCallback callback;
+  authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt,
+                                callback.callback());
+
+  callback.WaitForCallback();
+  EXPECT_EQ(CtapDeviceResponseCode::kSuccess, callback.status());
+  EXPECT_EQ(0u, callback.value()->size());
+}
+
+TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
+  authenticator_state_->large_blob[0] += 1;
+  ReadCallback callback;
+  authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt,
+                                callback.callback());
+
+  callback.WaitForCallback();
+  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrIntegrityFailure,
+            callback.status());
+  EXPECT_FALSE(callback.value());
+}
+
+// Test reading and writing a blob that fits in a single fragment.
+TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) {
+  std::vector<uint8_t> small_blob = fido_parsing_utils::Materialize(kSmallBlob);
+  WriteCallback write_callback;
+  authenticator_->WriteLargeBlob(small_blob, {kDummyKey}, base::nullopt,
+                                 write_callback.callback());
+
+  write_callback.WaitForCallback();
+  ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
+
+  ReadCallback read_callback;
+  authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt,
+                                read_callback.callback());
+  read_callback.WaitForCallback();
+  ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
+  auto large_blob_array = read_callback.value();
+  ASSERT_TRUE(large_blob_array);
+  ASSERT_EQ(1u, large_blob_array->size());
+  EXPECT_EQ(kDummyKey, large_blob_array->at(0).first);
+  EXPECT_EQ(small_blob, large_blob_array->at(0).second);
+}
+
+// Test reading and writing a blob that must fit in multiple fragments.
+TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) {
+  std::vector<uint8_t> large_blob;
+  large_blob.reserve(2048);
+  for (size_t i = 0; i < large_blob.capacity(); ++i) {
+    large_blob.emplace_back(i % 0xFF);
+  }
+
+  WriteCallback write_callback;
+  authenticator_->WriteLargeBlob(large_blob, {kDummyKey}, base::nullopt,
+                                 write_callback.callback());
+
+  write_callback.WaitForCallback();
+  ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
+
+  ReadCallback read_callback;
+  authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt,
+                                read_callback.callback());
+  read_callback.WaitForCallback();
+  ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
+  auto large_blob_array = read_callback.value();
+  ASSERT_TRUE(large_blob_array);
+  ASSERT_EQ(1u, large_blob_array->size());
+  EXPECT_EQ(kDummyKey, large_blob_array->at(0).first);
+  EXPECT_EQ(large_blob, large_blob_array->at(0).second);
+}
+
+}  // namespace
+
+}  // namespace device
diff --git a/device/fido/large_blob.cc b/device/fido/large_blob.cc
new file mode 100644
index 0000000..47aba0a6
--- /dev/null
+++ b/device/fido/large_blob.cc
@@ -0,0 +1,265 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/large_blob.h"
+#include "base/containers/span.h"
+#include "components/cbor/reader.h"
+#include "components/cbor/writer.h"
+#include "crypto/sha2.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/pin.h"
+
+namespace device {
+
+namespace {
+// The number of bytes the large blob validation hash is truncated to.
+constexpr size_t kTruncatedHashBytes = 16;
+}  // namespace
+
+LargeBlobArrayFragment::LargeBlobArrayFragment(const std::vector<uint8_t> bytes,
+                                               const size_t offset)
+    : bytes(std::move(bytes)), offset(offset) {}
+LargeBlobArrayFragment::~LargeBlobArrayFragment() = default;
+LargeBlobArrayFragment::LargeBlobArrayFragment(LargeBlobArrayFragment&&) =
+    default;
+
+bool VerifyLargeBlobArrayIntegrity(base::span<const uint8_t> large_blob_array) {
+  if (large_blob_array.size() <= kTruncatedHashBytes) {
+    return false;
+  }
+  const size_t trail_offset = large_blob_array.size() - kTruncatedHashBytes;
+  std::array<uint8_t, crypto::kSHA256Length> large_blob_hash =
+      crypto::SHA256Hash(large_blob_array.subspan(0, trail_offset));
+
+  base::span<const uint8_t> large_blob_trail =
+      large_blob_array.subspan(trail_offset);
+  return std::equal(large_blob_hash.begin(),
+                    large_blob_hash.begin() + kTruncatedHashBytes,
+                    large_blob_trail.begin(), large_blob_trail.end());
+}
+
+// static
+LargeBlobsRequest LargeBlobsRequest::ForRead(size_t bytes, size_t offset) {
+  DCHECK_GT(bytes, 0u);
+  LargeBlobsRequest request;
+  request.get_ = bytes;
+  request.offset_ = offset;
+  return request;
+}
+
+// static
+LargeBlobsRequest LargeBlobsRequest::ForWrite(LargeBlobArrayFragment fragment,
+                                              size_t length) {
+  LargeBlobsRequest request;
+  if (fragment.offset == 0) {
+    request.length_ = length;
+  }
+  request.offset_ = fragment.offset;
+  request.set_ = std::move(fragment.bytes);
+  return request;
+}
+
+LargeBlobsRequest::LargeBlobsRequest() = default;
+LargeBlobsRequest::LargeBlobsRequest(LargeBlobsRequest&& other) = default;
+LargeBlobsRequest::~LargeBlobsRequest() = default;
+
+void LargeBlobsRequest::SetPinParam(
+    const pin::TokenResponse& pin_uv_auth_token) {
+  // TODO(nsatragno): implement & test pin uv auth token.
+}
+
+// static
+base::Optional<LargeBlobsResponse> LargeBlobsResponse::ParseForRead(
+    const size_t bytes_to_read,
+    const base::Optional<cbor::Value>& cbor_response) {
+  if (!cbor_response || !cbor_response->is_map()) {
+    return base::nullopt;
+  }
+
+  const cbor::Value::MapValue& map = cbor_response->GetMap();
+  auto it =
+      map.find(cbor::Value(static_cast<int>(LargeBlobsResponseKey::kConfig)));
+  if (it == map.end() || !it->second.is_bytestring()) {
+    return base::nullopt;
+  }
+
+  const std::vector<uint8_t>& config = it->second.GetBytestring();
+  if (config.size() > bytes_to_read) {
+    return base::nullopt;
+  }
+
+  return LargeBlobsResponse(std::move(config));
+}
+
+// static
+base::Optional<LargeBlobsResponse> LargeBlobsResponse::ParseForWrite(
+    const base::Optional<cbor::Value>& cbor_response) {
+  // For writing, we expect an empty response.
+  if (cbor_response) {
+    return base::nullopt;
+  }
+
+  return LargeBlobsResponse();
+}
+
+LargeBlobsResponse::LargeBlobsResponse(
+    base::Optional<std::vector<uint8_t>> config)
+    : config_(std::move(config)) {}
+LargeBlobsResponse::LargeBlobsResponse(LargeBlobsResponse&& other) = default;
+LargeBlobsResponse& LargeBlobsResponse::operator=(LargeBlobsResponse&& other) =
+    default;
+LargeBlobsResponse::~LargeBlobsResponse() = default;
+
+std::pair<CtapRequestCommand, base::Optional<cbor::Value>>
+AsCTAPRequestValuePair(const LargeBlobsRequest& request) {
+  cbor::Value::MapValue map;
+  if (request.get_) {
+    map.emplace(static_cast<int>(LargeBlobsRequestKey::kGet), *request.get_);
+  }
+  if (request.set_) {
+    map.emplace(static_cast<int>(LargeBlobsRequestKey::kSet), *request.set_);
+  }
+  map.emplace(static_cast<int>(LargeBlobsRequestKey::kOffset), request.offset_);
+  if (request.length_) {
+    map.emplace(static_cast<int>(LargeBlobsRequestKey::kLength),
+                *request.length_);
+  }
+  if (request.pin_uv_auth_param_) {
+    map.emplace(static_cast<int>(LargeBlobsRequestKey::kPinUvAuthParam),
+                *request.pin_uv_auth_param_);
+  }
+  if (request.pin_uv_auth_protocol_) {
+    map.emplace(static_cast<int>(LargeBlobsRequestKey::kPinUvAuthProtocol),
+                *request.pin_uv_auth_protocol_);
+  }
+  return std::make_pair(CtapRequestCommand::kAuthenticatorLargeBlobs,
+                        cbor::Value(std::move(map)));
+}
+
+// static.
+base::Optional<LargeBlobData> LargeBlobData::Parse(const cbor::Value& value) {
+  if (!value.is_map()) {
+    return base::nullopt;
+  }
+  const cbor::Value::MapValue& map = value.GetMap();
+  auto ciphertext_it =
+      map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kCiphertext)));
+  if (ciphertext_it == map.end() || !ciphertext_it->second.is_bytestring()) {
+    return base::nullopt;
+  }
+  auto nonce_it =
+      map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kNonce)));
+  if (nonce_it == map.end() || !nonce_it->second.is_bytestring()) {
+    return base::nullopt;
+  }
+  auto orig_size_it =
+      map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kOrigSize)));
+  if (orig_size_it == map.end() || !orig_size_it->second.is_unsigned()) {
+    return base::nullopt;
+  }
+  return LargeBlobData(ciphertext_it->second.GetBytestring(),
+                       nonce_it->second.GetBytestring(),
+                       orig_size_it->second.GetUnsigned());
+}
+
+LargeBlobData::LargeBlobData(std::vector<uint8_t> ciphertext,
+                             std::vector<uint8_t> nonce,
+                             int64_t orig_size)
+    : ciphertext_(std::move(ciphertext)),
+      nonce_(std::move(nonce)),
+      orig_size_(std::move(orig_size)) {}
+LargeBlobData::LargeBlobData(LargeBlobKey key, std::vector<uint8_t> blob) {
+  // TODO(nsatragno): implement encrypting the data. For now, just store and
+  // return the blob as plaintext.
+  orig_size_ = blob.size();
+  ciphertext_ = blob;
+}
+LargeBlobData::LargeBlobData(LargeBlobData&&) = default;
+LargeBlobData& LargeBlobData::operator=(LargeBlobData&&) = default;
+LargeBlobData::~LargeBlobData() = default;
+
+bool LargeBlobData::operator==(const LargeBlobData& other) const {
+  return ciphertext_ == other.ciphertext_ && nonce_ == other.nonce_ &&
+         orig_size_ == other.orig_size_;
+}
+
+base::Optional<std::vector<uint8_t>> LargeBlobData::Decrypt(
+    LargeBlobKey key) const {
+  // TODO(nsatragno): implement decrypting the data. For now, store and return
+  // the blob as plaintext.
+  return ciphertext_;
+}
+
+cbor::Value::MapValue LargeBlobData::AsCBOR() const {
+  cbor::Value::MapValue map;
+  map.emplace(static_cast<int>(LargeBlobDataKeys::kCiphertext), ciphertext_);
+  map.emplace(static_cast<int>(LargeBlobDataKeys::kNonce), nonce_);
+  map.emplace(static_cast<int>(LargeBlobDataKeys::kOrigSize), orig_size_);
+  return map;
+}
+
+LargeBlobArrayReader::LargeBlobArrayReader() = default;
+LargeBlobArrayReader::LargeBlobArrayReader(LargeBlobArrayReader&&) = default;
+LargeBlobArrayReader::~LargeBlobArrayReader() = default;
+
+void LargeBlobArrayReader::Append(const std::vector<uint8_t>& fragment) {
+  bytes_.insert(bytes_.end(), fragment.begin(), fragment.end());
+}
+
+base::Optional<std::vector<LargeBlobData>> LargeBlobArrayReader::Materialize() {
+  if (!VerifyLargeBlobArrayIntegrity(bytes_)) {
+    return base::nullopt;
+  }
+
+  base::span<const uint8_t> cbor_bytes =
+      base::make_span(bytes_.data(), bytes_.size() - kTruncatedHashBytes);
+  base::Optional<cbor::Value> cbor = cbor::Reader::Read(cbor_bytes);
+  if (!cbor || !cbor->is_array()) {
+    return base::nullopt;
+  }
+
+  std::vector<LargeBlobData> large_blob_array;
+  const cbor::Value::ArrayValue& array = cbor->GetArray();
+  for (const cbor::Value& value : array) {
+    base::Optional<LargeBlobData> large_blob_data = LargeBlobData::Parse(value);
+    if (!large_blob_data) {
+      continue;
+    }
+
+    large_blob_array.emplace_back(std::move(*large_blob_data));
+  }
+
+  return large_blob_array;
+}
+
+LargeBlobArrayWriter::LargeBlobArrayWriter(
+    const std::vector<LargeBlobData>& large_blob_array) {
+  cbor::Value::ArrayValue array;
+  for (const LargeBlobData& large_blob_data : large_blob_array) {
+    array.emplace_back(large_blob_data.AsCBOR());
+  }
+  bytes_ = *cbor::Writer::Write(cbor::Value(array));
+
+  std::array<uint8_t, crypto::kSHA256Length> large_blob_hash =
+      crypto::SHA256Hash(bytes_);
+  bytes_.insert(bytes_.end(), large_blob_hash.begin(),
+                large_blob_hash.begin() + kTruncatedHashBytes);
+  DCHECK(VerifyLargeBlobArrayIntegrity(bytes_));
+}
+LargeBlobArrayWriter::LargeBlobArrayWriter(LargeBlobArrayWriter&&) = default;
+LargeBlobArrayWriter::~LargeBlobArrayWriter() = default;
+
+LargeBlobArrayFragment LargeBlobArrayWriter::Pop(size_t length) {
+  CHECK(has_remaining_fragments());
+  length = std::min(length, bytes_.size() - offset_);
+
+  LargeBlobArrayFragment fragment{
+      fido_parsing_utils::Materialize(
+          base::make_span(bytes_.data() + offset_, length)),
+      offset_};
+  offset_ += length;
+  return fragment;
+}
+
+}  // namespace device
diff --git a/device/fido/large_blob.h b/device/fido/large_blob.h
new file mode 100644
index 0000000..3ed5036
--- /dev/null
+++ b/device/fido/large_blob.h
@@ -0,0 +1,201 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_FIDO_LARGE_BLOB_H_
+#define DEVICE_FIDO_LARGE_BLOB_H_
+
+#include <cstdint>
+#include <cstdlib>
+#include <vector>
+
+#include "base/component_export.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/pin.h"
+
+namespace device {
+
+// https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#largeBlobsRW
+enum class LargeBlobsRequestKey : uint8_t {
+  kGet = 0x01,
+  kSet = 0x02,
+  kOffset = 0x03,
+  kLength = 0x04,
+  kPinUvAuthParam = 0x05,
+  kPinUvAuthProtocol = 0x06,
+};
+
+// https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#largeBlobsRW
+enum class LargeBlobsResponseKey : uint8_t {
+  kConfig = 0x01,
+};
+
+// https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#large-blob
+enum class LargeBlobDataKeys : uint8_t {
+  kCiphertext = 0x01,
+  kNonce = 0x02,
+  kOrigSize = 0x03,
+};
+
+enum class LargeBlobOperation {
+  kNone,
+  kRead,
+  kWrite,
+};
+
+using LargeBlobKey = std::array<uint8_t, kLargeBlobKeyLength>;
+
+constexpr size_t kLargeBlobDefaultMaxFragmentLength = 960;
+constexpr size_t kLargeBlobReadEncodingOverhead = 64;
+
+struct COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobArrayFragment {
+  LargeBlobArrayFragment(std::vector<uint8_t> bytes, size_t offset);
+  ~LargeBlobArrayFragment();
+  LargeBlobArrayFragment(const LargeBlobArrayFragment&) = delete;
+  LargeBlobArrayFragment operator=(const LargeBlobArrayFragment&) = delete;
+  LargeBlobArrayFragment(LargeBlobArrayFragment&&);
+  const std::vector<uint8_t> bytes;
+  const size_t offset;
+};
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+bool VerifyLargeBlobArrayIntegrity(base::span<const uint8_t> large_blob_array);
+
+class LargeBlobsRequest {
+ public:
+  ~LargeBlobsRequest();
+  LargeBlobsRequest(const LargeBlobsRequest&) = delete;
+  LargeBlobsRequest operator=(const LargeBlobsRequest&) = delete;
+  LargeBlobsRequest(LargeBlobsRequest&& other);
+
+  static LargeBlobsRequest ForRead(size_t bytes, size_t offset);
+  static LargeBlobsRequest ForWrite(LargeBlobArrayFragment fragment,
+                                    size_t length);
+
+  void SetPinParam(const pin::TokenResponse& pin_uv_auth_token);
+
+  friend std::pair<CtapRequestCommand, base::Optional<cbor::Value>>
+  AsCTAPRequestValuePair(const LargeBlobsRequest& request);
+
+ private:
+  LargeBlobsRequest();
+
+  base::Optional<int64_t> get_;
+  base::Optional<std::vector<uint8_t>> set_;
+  int64_t offset_ = 0;
+  base::Optional<int64_t> length_;
+  base::Optional<std::vector<uint8_t>> pin_uv_auth_param_;
+  base::Optional<int64_t> pin_uv_auth_protocol_;
+};
+
+class LargeBlobsResponse {
+ public:
+  LargeBlobsResponse(const LargeBlobsResponse&) = delete;
+  LargeBlobsResponse operator=(const LargeBlobsResponse&) = delete;
+  LargeBlobsResponse(LargeBlobsResponse&& other);
+  LargeBlobsResponse& operator=(LargeBlobsResponse&&);
+  ~LargeBlobsResponse();
+
+  static base::Optional<LargeBlobsResponse> ParseForRead(
+      size_t bytes_to_read,
+      const base::Optional<cbor::Value>& cbor_response);
+  static base::Optional<LargeBlobsResponse> ParseForWrite(
+      const base::Optional<cbor::Value>& cbor_response);
+
+  base::Optional<std::vector<uint8_t>> config() { return config_; }
+
+ private:
+  explicit LargeBlobsResponse(
+      base::Optional<std::vector<uint8_t>> config = base::nullopt);
+
+  base::Optional<std::vector<uint8_t>> config_;
+};
+
+// Represents the large-blob map structure
+// https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#large-blob
+class COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobData {
+ public:
+  static base::Optional<LargeBlobData> Parse(const cbor::Value& cbor_response);
+
+  LargeBlobData(LargeBlobKey key, std::vector<uint8_t> blob);
+  LargeBlobData(const LargeBlobData&) = delete;
+  LargeBlobData operator=(const LargeBlobData&) = delete;
+  LargeBlobData(LargeBlobData&&);
+  LargeBlobData& operator=(LargeBlobData&&);
+  ~LargeBlobData();
+  bool operator==(const LargeBlobData&) const;
+
+  base::Optional<std::vector<uint8_t>> Decrypt(LargeBlobKey key) const;
+  cbor::Value::MapValue AsCBOR() const;
+
+ private:
+  LargeBlobData(std::vector<uint8_t> ciphertext,
+                std::vector<uint8_t> nonce,
+                int64_t orig_size);
+  std::vector<uint8_t> ciphertext_;
+  std::vector<uint8_t> nonce_;
+  int64_t orig_size_;
+};
+
+// Reading large blob arrays is done in chunks. This class provides facilities
+// to assemble together those chunks.
+class COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobArrayReader {
+ public:
+  LargeBlobArrayReader();
+  LargeBlobArrayReader(const LargeBlobArrayReader&) = delete;
+  LargeBlobArrayReader operator=(const LargeBlobArrayReader&) = delete;
+  LargeBlobArrayReader(LargeBlobArrayReader&&);
+  ~LargeBlobArrayReader();
+
+  // Appends a fragment to the large blob array.
+  void Append(const std::vector<uint8_t>& fragment);
+
+  // Verifies the integrity of the large blob array. This should be called after
+  // all fragments have been |Append|ed.
+  // If successful, parses and returns the array.
+  base::Optional<std::vector<LargeBlobData>> Materialize();
+
+  // Returns the current size of the array fragments.
+  size_t size() const { return bytes_.size(); }
+
+ private:
+  std::vector<uint8_t> bytes_;
+};
+
+// Writing large blob arrays is done in chunks. This class provides facilities
+// to divide a blob into chunks.
+class COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobArrayWriter {
+ public:
+  explicit LargeBlobArrayWriter(
+      const std::vector<LargeBlobData>& large_blob_array);
+  LargeBlobArrayWriter(const LargeBlobArrayWriter&) = delete;
+  LargeBlobArrayWriter operator=(const LargeBlobArrayWriter&) = delete;
+  LargeBlobArrayWriter(LargeBlobArrayWriter&&);
+  ~LargeBlobArrayWriter();
+
+  // Extracts a fragment with |length|. Can only be called if
+  // has_remaining_fragments() is true.
+  LargeBlobArrayFragment Pop(size_t length);
+
+  // Returns the current size of the array fragments.
+  size_t size() const { return bytes_.size(); }
+
+  // Returns true if there are remaining fragments to be written, false
+  // otherwise.
+  bool has_remaining_fragments() const { return offset_ < size(); }
+
+  void set_bytes_for_testing(std::vector<uint8_t> bytes) {
+    bytes_ = std::move(bytes);
+  }
+
+ private:
+  std::vector<uint8_t> bytes_;
+  size_t offset_ = 0;
+};
+
+std::pair<CtapRequestCommand, base::Optional<cbor::Value>>
+AsCTAPRequestValuePair(const LargeBlobsRequest& request);
+
+}  // namespace device
+
+#endif  // DEVICE_FIDO_LARGE_BLOB_H_
diff --git a/device/fido/large_blob_unittest.cc b/device/fido/large_blob_unittest.cc
new file mode 100644
index 0000000..408f5fe2
--- /dev/null
+++ b/device/fido/large_blob_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/large_blob.h"
+
+#include "base/optional.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+class FidoLargeBlobTest : public testing::Test {};
+
+// An empty CBOR array (0x80) followed by LEFT(SHA-256(h'80'), 16).
+const std::array<uint8_t, 17> kValidEmptyLargeBlobArray = {
+    0x80, 0x76, 0xbe, 0x8b, 0x52, 0x8d, 0x00, 0x75, 0xf7,
+    0xaa, 0xe9, 0x8d, 0x6f, 0xa5, 0x7a, 0x6d, 0x3c};
+
+// Something that is not an empty CBOR array (0x10) followed by
+// LEFT(SHA-256(h'10'), 16).
+const std::array<uint8_t, 17> kInvalidLargeBlobArray = {
+    0x10, 0xc5, 0x55, 0xea, 0xb4, 0x5d, 0x08, 0x84, 0x5a,
+    0xe9, 0xf1, 0x0d, 0x45, 0x2a, 0x99, 0xbf, 0xcb};
+
+// An "valid" CBOR large blob array with two entries. The first entry is not a
+// valid large blob map structure. The second entry is valid.
+const std::array<uint8_t, 35> kValidLargeBlobArray = {
+    0x82, 0xA2, 0x02, 0x42, 0x11, 0x11, 0x03, 0x02, 0xA3, 0x01, 0x42, 0x22,
+    0x22, 0x02, 0x42, 0x33, 0x33, 0x03, 0x02, 0x53, 0x5b, 0xaa, 0xd2, 0x7a,
+    0x26, 0x68, 0x34, 0x9e, 0xc3, 0x90, 0xd3, 0x9a, 0x1c, 0x0a, 0xae};
+
+TEST_F(FidoLargeBlobTest, VerifyLargeBlobArrayIntegrityValid) {
+  std::vector<uint8_t> large_blob_array =
+      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+  EXPECT_TRUE(VerifyLargeBlobArrayIntegrity(large_blob_array));
+}
+
+TEST_F(FidoLargeBlobTest, VerifyLargeBlobArrayIntegrityInvalid) {
+  std::vector<uint8_t> large_blob_array =
+      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+  large_blob_array[0] += 1;
+  EXPECT_FALSE(VerifyLargeBlobArrayIntegrity(large_blob_array));
+
+  large_blob_array = fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+  large_blob_array.erase(large_blob_array.begin());
+  EXPECT_FALSE(VerifyLargeBlobArrayIntegrity(large_blob_array));
+}
+
+TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeEmpty) {
+  LargeBlobArrayReader large_blob_array_reader;
+  large_blob_array_reader.Append(
+      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray));
+  EXPECT_EQ(0u, large_blob_array_reader.Materialize()->size());
+}
+
+TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeInvalidCbor) {
+  LargeBlobArrayReader large_blob_array_reader;
+  large_blob_array_reader.Append(
+      fido_parsing_utils::Materialize(kInvalidLargeBlobArray));
+  EXPECT_FALSE(large_blob_array_reader.Materialize());
+}
+
+TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeInvalidHash) {
+  std::vector<uint8_t> large_blob_array =
+      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+  large_blob_array[0] += 1;
+  LargeBlobArrayReader large_blob_array_reader;
+  large_blob_array_reader.Append(
+      fido_parsing_utils::Materialize(large_blob_array));
+  EXPECT_FALSE(large_blob_array_reader.Materialize());
+}
+
+TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeValid) {
+  LargeBlobArrayReader large_blob_array_reader;
+  large_blob_array_reader.Append(
+      fido_parsing_utils::Materialize(kValidLargeBlobArray));
+  std::vector<LargeBlobData> vector = *large_blob_array_reader.Materialize();
+  EXPECT_EQ(1u, vector.size());
+}
+
+// Test popping the large blob array in a fragment size that does not evenly
+// divide the length of the array.
+TEST_F(FidoLargeBlobTest, LargeBlobArrayWriter_PopUnevenly) {
+  const size_t fragment_size = 8;
+  const size_t expected_fragments =
+      kValidLargeBlobArray.size() / fragment_size + 1;
+  size_t fragments = 0;
+  ASSERT_NE(0u, kValidLargeBlobArray.size() % fragment_size);
+
+  LargeBlobArrayWriter large_blob_array_writer({});
+  std::vector<uint8_t> large_blob_array =
+      fido_parsing_utils::Materialize(kValidLargeBlobArray);
+  large_blob_array_writer.set_bytes_for_testing(large_blob_array);
+  std::vector<uint8_t> reconstructed;
+  EXPECT_TRUE(large_blob_array_writer.has_remaining_fragments());
+  while (large_blob_array_writer.has_remaining_fragments()) {
+    LargeBlobArrayFragment fragment =
+        large_blob_array_writer.Pop(fragment_size);
+    ++fragments;
+    reconstructed.insert(reconstructed.end(), fragment.bytes.begin(),
+                         fragment.bytes.end());
+    EXPECT_EQ(fragments != expected_fragments,
+              large_blob_array_writer.has_remaining_fragments());
+  }
+
+  EXPECT_EQ(expected_fragments, fragments);
+  EXPECT_EQ(large_blob_array, reconstructed);
+}
+
+// Test popping the large blob array in a fragment size that evenly divides the
+// length of the array.
+TEST_F(FidoLargeBlobTest, LargeBlobArrayFragments_PopEvenly) {
+  const size_t fragment_size = 7;
+  const size_t expected_fragments = kValidLargeBlobArray.size() / fragment_size;
+  size_t fragments = 0;
+  ASSERT_EQ(0u, kValidLargeBlobArray.size() % fragment_size);
+
+  LargeBlobArrayWriter large_blob_array_writer({});
+  std::vector<uint8_t> large_blob_array =
+      fido_parsing_utils::Materialize(kValidLargeBlobArray);
+  large_blob_array_writer.set_bytes_for_testing(large_blob_array);
+  std::vector<uint8_t> reconstructed;
+  EXPECT_TRUE(large_blob_array_writer.has_remaining_fragments());
+  while (large_blob_array_writer.has_remaining_fragments()) {
+    LargeBlobArrayFragment fragment =
+        large_blob_array_writer.Pop(fragment_size);
+    ++fragments;
+    reconstructed.insert(reconstructed.end(), fragment.bytes.begin(),
+                         fragment.bytes.end());
+    EXPECT_EQ(fragments != expected_fragments,
+              large_blob_array_writer.has_remaining_fragments());
+  }
+
+  EXPECT_EQ(expected_fragments, fragments);
+  EXPECT_EQ(large_blob_array, reconstructed);
+}
+
+}  // namespace
+
+}  // namespace device
diff --git a/device/fido/pin.h b/device/fido/pin.h
index ef91cb7..42b2ed6 100644
--- a/device/fido/pin.h
+++ b/device/fido/pin.h
@@ -31,7 +31,7 @@
   kGetAssertion = 0x02,
   kCredentialManagement = 0x04,
   kBioEnrollment = 0x08,
-  kPlatformConfiguration = 0x10,
+  kLargeBlobWrite = 0x10,
 };
 
 // kProtocolVersion is the version of the PIN protocol that this code
@@ -246,7 +246,7 @@
 // decrypt a response, the shared key from the request is needed. Once a pin-
 // token has been decrypted, it can be used to calculate the pinAuth parameters
 // needed to show user-verification in future operations.
-class TokenResponse {
+class COMPONENT_EXPORT(DEVICE_FIDO) TokenResponse {
  public:
   ~TokenResponse();
   TokenResponse(const TokenResponse&);
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index c32beb47..fb4c5b4b 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -24,12 +24,14 @@
 #include "crypto/ec_private_key.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/authenticator_supported_options.h"
 #include "device/fido/bio/enrollment.h"
 #include "device/fido/credential_management.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
+#include "device/fido/large_blob.h"
 #include "device/fido/opaque_attestation_statement.h"
 #include "device/fido/p256_public_key.h"
 #include "device/fido/pin.h"
@@ -55,12 +57,24 @@
     {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04,
      0x05, 0x06, 0x07, 0x08}};
 
+// Some commands that validate PinUvAuthTokens include this padding to ensure a
+// PinUvAuthParam cannot be reused across different commands.
+constexpr std::array<uint8_t, 32> kPinUvAuthTokenSafetyPadding = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
 constexpr uint8_t kSupportedPermissionsMask =
     static_cast<uint8_t>(pin::Permissions::kMakeCredential) |
     static_cast<uint8_t>(pin::Permissions::kGetAssertion) |
     static_cast<uint8_t>(pin::Permissions::kCredentialManagement) |
     static_cast<uint8_t>(pin::Permissions::kBioEnrollment);
 
+constexpr std::array<uint8_t, 4> Uint32LittleEndian(int64_t value) {
+  return {value & 0xFF, value >> 8 & 0xFF, value >> 16 & 0xFF,
+          value >> 24 & 0xFF};
+}
+
 struct PinUvAuthTokenPermissions {
   uint8_t permissions;
   base::Optional<std::string> rp_id;
@@ -664,6 +678,9 @@
     case CtapRequestCommand::kAuthenticatorBioEnrollmentPreview:
       response_code = OnBioEnrollment(request_bytes, &response_data);
       break;
+    case CtapRequestCommand::kAuthenticatorLargeBlobs:
+      response_code = OnLargeBlobs(request_bytes, &response_data);
+      break;
     default:
       break;
   }
@@ -1391,7 +1408,7 @@
 
     if (request.large_blob_key) {
       if (!config_.large_blob_support) {
-        return CtapDeviceResponseCode::kCtap2ErrExtensionNotSupported;
+        return CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension;
       }
       if (registration.second->large_blob_key) {
         assertion.set_large_blob_key(*registration.second->large_blob_key);
@@ -2162,6 +2179,160 @@
   return CtapDeviceResponseCode::kSuccess;
 }
 
+CtapDeviceResponseCode VirtualCtap2Device::OnLargeBlobs(
+    base::span<const uint8_t> request_bytes,
+    std::vector<uint8_t>* response) {
+  if (!config_.large_blob_support) {
+    DLOG(ERROR) << "Large blob not supported";
+    return CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension;
+  }
+
+  // Read request bytes into |cbor::Value::MapValue|.
+  const auto& cbor_request = cbor::Reader::Read(request_bytes);
+  if (!cbor_request || !cbor_request->is_map()) {
+    return CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType;
+  }
+  const auto& request_map = cbor_request->GetMap();
+
+  const auto offset_it = request_map.find(
+      cbor::Value(static_cast<uint8_t>(LargeBlobsRequestKey::kOffset)));
+  if (offset_it == request_map.end() || !offset_it->second.is_unsigned()) {
+    return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+  }
+  const size_t offset = offset_it->second.GetUnsigned();
+
+  const auto get_it = request_map.find(
+      cbor::Value(static_cast<uint8_t>(LargeBlobsRequestKey::kGet)));
+  const auto set_it = request_map.find(
+      cbor::Value(static_cast<uint8_t>(LargeBlobsRequestKey::kSet)));
+  if ((get_it == request_map.end() && set_it == request_map.end()) ||
+      (get_it != request_map.end() && set_it != request_map.end())) {
+    return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+  }
+  if ((get_it != request_map.end() && !get_it->second.is_unsigned()) ||
+      (set_it != request_map.end() && !set_it->second.is_bytestring())) {
+    return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+  }
+  const auto length_it = request_map.find(
+      cbor::Value(static_cast<uint8_t>(LargeBlobsRequestKey::kLength)));
+  const size_t max_fragment_length = kLargeBlobDefaultMaxFragmentLength;
+
+  if (get_it != request_map.end()) {
+    if (length_it != request_map.end()) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+    }
+    const size_t get = get_it->second.GetUnsigned();
+    if (get > max_fragment_length) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidLength;
+    }
+    if (offset > mutable_state()->large_blob.size()) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+    }
+    cbor::Value::MapValue response_map;
+    response_map.emplace(
+        static_cast<uint8_t>(LargeBlobsResponseKey::kConfig),
+        base::make_span(
+            mutable_state()->large_blob.data() + offset,
+            std::min(get, mutable_state()->large_blob.size() - offset)));
+    *response =
+        cbor::Writer::Write(cbor::Value(std::move(response_map))).value();
+  } else {
+    DCHECK(set_it != request_map.end());
+    const std::vector<uint8_t>& set = set_it->second.GetBytestring();
+    if (set.size() > max_fragment_length) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidLength;
+    }
+    if (offset == 0) {
+      if (length_it == request_map.end() || !length_it->second.is_unsigned()) {
+        return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+      }
+      const size_t length = length_it->second.GetUnsigned();
+      if (length > config_.available_large_blob_storage) {
+        return CtapDeviceResponseCode::kCtap2ErrLargeBlobStorageFull;
+      }
+      constexpr size_t kMinBlobLength = 17;
+      if (length < kMinBlobLength) {
+        return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+      }
+      mutable_state()->large_blob_expected_length = length;
+      mutable_state()->large_blob_expected_next_offset = 0;
+    } else {
+      if (length_it != request_map.end()) {
+        return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+      }
+    }
+
+    if (offset != mutable_state()->large_blob_expected_next_offset) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidSeq;
+    }
+
+    if (device_info_->options.client_pin_availability ==
+            AuthenticatorSupportedOptions::ClientPinAvailability::
+                kSupportedAndPinSet ||
+        device_info_->options.user_verification_availability ==
+            AuthenticatorSupportedOptions::UserVerificationAvailability::
+                kSupportedAndConfigured) {
+      // If the device is protected by some sort of user verification:
+      const auto pin_uv_auth_param_it = request_map.find(cbor::Value(
+          static_cast<uint8_t>(LargeBlobsRequestKey::kPinUvAuthParam)));
+      const auto pin_uv_auth_protocol_it = request_map.find(cbor::Value(
+          static_cast<uint8_t>(LargeBlobsRequestKey::kPinUvAuthProtocol)));
+      if (pin_uv_auth_param_it == request_map.end() ||
+          !pin_uv_auth_param_it->second.is_bytestring() ||
+          pin_uv_auth_protocol_it == request_map.end() ||
+          !pin_uv_auth_protocol_it->second.is_unsigned()) {
+        return CtapDeviceResponseCode::kCtap2ErrOperationDenied;
+      }
+      if (pin_uv_auth_protocol_it->second.GetUnsigned() !=
+          pin::kProtocolVersion) {
+        return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
+      }
+      if (!(mutable_state()->pin_uv_token_permissions &
+            static_cast<uint8_t>(pin::Permissions::kLargeBlobWrite))) {
+        return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
+      }
+
+      // verify(pinUvAuthToken,
+      //        32×0xff || h’0c00' || uint32LittleEndian(offset) ||
+      //          contents of set byte string, i.e. not including an outer CBOR
+      //          tag with major type two,
+      //        pinUvAuthParam)
+      std::vector<uint8_t> pinauth_bytes;
+      pinauth_bytes.insert(pinauth_bytes.begin(),
+                           kPinUvAuthTokenSafetyPadding.begin(),
+                           kPinUvAuthTokenSafetyPadding.end());
+      pinauth_bytes.insert(pinauth_bytes.end(), {0x0c, 0x00});
+      auto offset_vec = Uint32LittleEndian(offset);
+      pinauth_bytes.insert(pinauth_bytes.end(), offset_vec.begin(),
+                           offset_vec.end());
+      pinauth_bytes.insert(pinauth_bytes.end(), set.begin(), set.end());
+      if (!CheckPINToken(mutable_state()->pin_token,
+                         pin_uv_auth_param_it->second.GetBytestring(),
+                         pinauth_bytes)) {
+        return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
+      }
+    }
+    if (offset + set.size() > mutable_state()->large_blob_expected_length) {
+      return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
+    }
+    if (offset == 0) {
+      mutable_state()->large_blob_buffer.clear();
+    }
+    mutable_state()->large_blob_buffer.insert(
+        mutable_state()->large_blob_buffer.end(), set.begin(), set.end());
+    mutable_state()->large_blob_expected_next_offset =
+        mutable_state()->large_blob_buffer.size();
+    if (mutable_state()->large_blob_buffer.size() ==
+        mutable_state()->large_blob_expected_length) {
+      if (!VerifyLargeBlobArrayIntegrity(mutable_state()->large_blob_buffer)) {
+        return CtapDeviceResponseCode::kCtap2ErrIntegrityFailure;
+      }
+      mutable_state()->large_blob = mutable_state()->large_blob_buffer;
+    }
+  }
+  return CtapDeviceResponseCode::kSuccess;
+}
+
 void VirtualCtap2Device::InitPendingRPs() {
   mutable_state()->pending_rps.clear();
   std::set<std::string> rp_ids;
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index 3db8f2a..19f148d 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -69,6 +69,11 @@
     bool cred_protect_support = false;
     bool hmac_secret_support = false;
     bool large_blob_support = false;
+    // The space available to store a large blob. In real authenticators this
+    // may change depending on the number of resident credentials. We treat this
+    // as a fixed size area for the large blob.
+    size_t available_large_blob_storage = 1024;
+
     IncludeCredential include_credential_in_assertion_response =
         IncludeCredential::ONLY_IF_NEEDED;
 
@@ -218,6 +223,8 @@
       std::vector<uint8_t>* response);
   CtapDeviceResponseCode OnBioEnrollment(base::span<const uint8_t> request,
                                          std::vector<uint8_t>* response);
+  CtapDeviceResponseCode OnLargeBlobs(base::span<const uint8_t> request,
+                                      std::vector<uint8_t>* response);
   CtapDeviceResponseCode OnAuthenticatorGetInfo(
       std::vector<uint8_t>* response) const;
 
diff --git a/device/fido/virtual_fido_device.h b/device/fido/virtual_fido_device.h
index 5e80237..d3e8e95 100644
--- a/device/fido/virtual_fido_device.h
+++ b/device/fido/virtual_fido_device.h
@@ -226,6 +226,17 @@
     // expected sequence of requests was sent.
     std::vector<size_t> allow_list_sizes;
 
+    // The large-blob array. This is initialized to an empty CBOR array (0x80)
+    // followed by LEFT(SHA-256(h'80'), 16).
+    std::vector<uint8_t> large_blob = {0x80, 0x76, 0xbe, 0x8b, 0x52, 0x8d,
+                                       0x00, 0x75, 0xf7, 0xaa, 0xe9, 0x8d,
+                                       0x6f, 0xa5, 0x7a, 0x6d, 0x3c};
+    // Buffer that gets progressively filled with large blob fragments until
+    // committed.
+    std::vector<uint8_t> large_blob_buffer;
+    uint64_t large_blob_expected_next_offset = 0;
+    uint64_t large_blob_expected_length = 0;
+
     FidoTransportProtocol transport =
         FidoTransportProtocol::kUsbHumanInterfaceDevice;
 
diff --git a/docs/mac/triage.md b/docs/mac/triage.md
index 3408e12..20690c3 100644
--- a/docs/mac/triage.md
+++ b/docs/mac/triage.md
@@ -146,7 +146,7 @@
 * Admin
 * Blink
 * Infra
-* Internals>Headless, Network, Plugins, Printing, Skia, Views
+* Internals>Cast, Headless, Network, Plugins, Printing, Skia, Views
 * IO>Bluetooth, USB
 * Platform
 * Services>Chromoting
@@ -156,6 +156,6 @@
 
 [unconfirmed]: https://bugs.chromium.org/p/chromium/issues/list?q=OS%3DMac%20status%3AUnconfirmed%20-component%3ABlink%2CEnterprise%2CInternals%3ENetwork%2CPlatform%3EDevtools%2CServices%3ESyncs%2CUI%3EBrowser%3EPasswords&can=2
 [untriaged-m]: https://bugs.chromium.org/p/chromium/issues/list?q=has%3AMac%20status%3AUntriaged&can=2
-[untriaged-c]: https://bugs.chromium.org/p/chromium/issues/list?q=OS%3DMac%20-OS%3DWindows%2CLinux%2CChrome%2CAndroid%2CiOS%20status%3AUntriaged%20-component%3AAdmin%2CBlink%2CInfra%2CInternals%3EHeadless%2CInternals%3ENetwork%2CInternals%3EPlugins%3EPDF%2CInternals%3EPrinting%2CInternals%3ESkia%2CInternals%3EUpdater%2CInternals%3EViews%2CIO%3EBluetooth%2CIO%3EUSB%2CPlatform%2CServices%3EChromoting%2CTest%3ETelemetry%2CUI%3EBrowser%3EWebAppInstalls%2CPlatform%3EWebAppProvider%2CUI%3EBrowser%3EWebUI&can=2
+[untriaged-c]: https://bugs.chromium.org/p/chromium/issues/list?q=OS%3DMac%20-OS%3DWindows%2CLinux%2CChrome%2CAndroid%2CiOS%20status%3AUntriaged%20-component%3AAdmin%2CBlink%2CInfra%2CInternals%3ECast%2CInternals%3EHeadless%2CInternals%3ENetwork%2CInternals%3EPlugins%3EPDF%2CInternals%3EPrinting%2CInternals%3ESkia%2CInternals%3EUpdater%2CInternals%3EViews%2CIO%3EBluetooth%2CIO%3EUSB%2CPlatform%2CServices%3EChromoting%2CTest%3ETelemetry%2CUI%3EBrowser%3EWebAppInstalls%2CPlatform%3EWebAppProvider%2CUI%3EBrowser%3EWebUI&can=2
 [available]: https://bugs.chromium.org/p/chromium/issues/list?q=has%3AMac%20status%3AAvailable&can=2
 [assigned]: https://bugs.chromium.org/p/chromium/issues/list?q=has%3AMac%20status%3AAssigned&can=2
diff --git a/docs/tab_helpers.md b/docs/tab_helpers.md
index 3a63a25b8..9f0ca4f 100644
--- a/docs/tab_helpers.md
+++ b/docs/tab_helpers.md
@@ -50,6 +50,8 @@
     : public content::WebContentsObserver,
       public content::WebContentsUserData<TitleLoggerTabHelper> {
  public:
+  TitleLoggerTabHelper(const TitleLoggerTabHelper&) = delete;
+  TitleLoggerTabHelper& operator=(const TitleLoggerTabHelper&) = delete;
   ~TitleLoggerTabHelper() override;
 
   // content::WebContentsObserver
@@ -60,8 +62,6 @@
  private:
   explicit TitleLoggerTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<TitleLoggerTabHelper>;
-
-  DISALLOW_COPY_AND_ASSIGN(TitleLoggerTabHelper);
 };
 ```
 
diff --git a/docs/webui_in_components.md b/docs/webui_in_components.md
index 006f3f7..67b0154 100644
--- a/docs/webui_in_components.md
+++ b/docs/webui_in_components.md
@@ -142,9 +142,10 @@
 class HelloWorldUI : public content::WebUIController {
  public:
   explicit HelloWorldUI(content::WebUI* web_ui);
+  HelloWorldUI(const HelloWorldUI&) = delete;
+  HelloWorldUI& operator=(const HelloWorldUI&) = delete;
   ~HelloWorldUI() override;
  private:
-  DISALLOW_COPY_AND_ASSIGN(HelloWorldUI);
 };
 
 #endif  // COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
@@ -257,8 +258,6 @@
 +  private:
 +   // Add two numbers together using integer arithmetic.
 +   void AddNumbers(const base::ListValue* args);
-
-    DISALLOW_COPY_AND_ASSIGN(HelloWorldUI);
   };
 ```
 
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index ad55e02d..b4aae04fb 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -535,6 +535,7 @@
       "//chromeos/dbus/upstart",
       "//chromeos/login/login_state",
       "//chromeos/network",
+      "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
     ]
   }
 }
@@ -580,8 +581,6 @@
 
   if (is_chromeos) {
     sources += [
-      "api/crash_report_private/mock_crash_endpoint.cc",
-      "api/crash_report_private/mock_crash_endpoint.h",
       "api/feedback_private/mock_feedback_service.cc",
       "api/feedback_private/mock_feedback_service.h",
     ]
diff --git a/extensions/browser/api/crash_report_private/BUILD.gn b/extensions/browser/api/crash_report_private/BUILD.gn
index 7d9b411..bf2b0f8 100644
--- a/extensions/browser/api/crash_report_private/BUILD.gn
+++ b/extensions/browser/api/crash_report_private/BUILD.gn
@@ -14,12 +14,8 @@
   ]
 
   deps = [
-    "//components/crash/core/app",
-    "//components/feedback",
+    "//components/crash/content/browser/error_reporting",
     "//content/public/browser",
     "//extensions/common/api",
-    "//net",
-    "//services/network:network_service",
-    "//services/network/public/cpp",
   ]
 }
diff --git a/extensions/browser/api/crash_report_private/DEPS b/extensions/browser/api/crash_report_private/DEPS
index d2cde66..c0cd4158 100644
--- a/extensions/browser/api/crash_report_private/DEPS
+++ b/extensions/browser/api/crash_report_private/DEPS
@@ -1,10 +1,4 @@
 include_rules = [
-  "+components/crash/core/app/client_upload_info.h",
-  "+components/feedback/redaction_tool.h",
+  "+components/crash/content/browser/error_reporting",
 ]
 
-specific_include_rules = {
-  "mock_crash_endpoint.cc": [
-    "+components/crash/core/app/crash_reporter_client.h",
-  ],
-}
diff --git a/extensions/browser/api/crash_report_private/crash_report_private_api.cc b/extensions/browser/api/crash_report_private/crash_report_private_api.cc
index a9658eb..362c825 100644
--- a/extensions/browser/api/crash_report_private/crash_report_private_api.cc
+++ b/extensions/browser/api/crash_report_private/crash_report_private_api.cc
@@ -4,21 +4,9 @@
 
 #include "extensions/browser/api/crash_report_private/crash_report_private_api.h"
 
-#include "base/no_destructor.h"
-#include "base/strings/strcat.h"
-#include "base/strings/stringprintf.h"
-#include "base/system/sys_info.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
 #include "base/time/default_clock.h"
-#include "components/crash/core/app/client_upload_info.h"
-#include "components/feedback/redaction_tool.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/base/escape.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
+#include "components/crash/content/browser/error_reporting/javascript_error_report.h"
+#include "components/crash/content/browser/error_reporting/send_javascript_error_report.h"
 
 namespace extensions {
 namespace api {
@@ -30,171 +18,6 @@
 
 base::Clock* g_clock = base::DefaultClock::GetInstance();
 
-#if defined(GOOGLE_CHROME_BUILD)
-constexpr char kCrashEndpointUrl[] = "https://clients2.google.com/cr/report";
-#else
-constexpr char kCrashEndpointUrl[] = "";
-#endif
-
-std::string& GetCrashEndpoint() {
-  static base::NoDestructor<std::string> crash_endpoint(kCrashEndpointUrl);
-  return *crash_endpoint;
-}
-
-constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024;
-
-void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
-                       base::OnceCallback<void()> callback,
-                       std::unique_ptr<std::string> response_body) {
-  if (response_body) {
-    DVLOG(1) << "Uploaded crash report. ID: " << *response_body;
-  } else {
-    LOG(ERROR) << "Failed to upload crash report";
-  }
-  std::move(callback).Run();
-}
-
-// Sometimes, the stack trace will contain an error message as the first line,
-// which confuses the Crash server. This function deletes it if it is present.
-std::string RemoveErrorMessageFromStackTrace(const std::string& stack_trace,
-                                             const std::string& error_message) {
-  // Return the original stack trace if the error message is not present.
-  const auto error_message_index = stack_trace.find(error_message);
-  if (error_message_index == std::string::npos)
-    return stack_trace;
-
-  // If the stack trace only contains one line, then delete the whole trace.
-  const auto first_line_end_index = stack_trace.find('\n');
-  if (first_line_end_index == std::string::npos)
-    return std::string();
-
-  // Otherwise, delete the first line.
-  return stack_trace.substr(first_line_end_index + 1);
-}
-
-using ParameterMap = std::map<std::string, std::string>;
-
-std::string BuildPostRequestQueryString(const ParameterMap& params) {
-  std::vector<std::string> query_parts;
-  for (const auto& kv : params) {
-    query_parts.push_back(base::StrCat(
-        {kv.first, "=",
-         net::EscapeQueryParamValue(kv.second, /* use_plus */ false)}));
-  }
-  return base::JoinString(query_parts, "&");
-}
-
-struct PlatformInfo {
-  std::string product_name;
-  std::string version;
-  std::string channel;
-  std::string platform;
-  std::string os_version;
-};
-
-PlatformInfo GetPlatformInfo() {
-  PlatformInfo info;
-  crash_reporter::GetClientProductNameAndVersion(&info.product_name,
-                                                 &info.version, &info.channel);
-
-  int32_t os_major_version = 0;
-  int32_t os_minor_version = 0;
-  int32_t os_bugfix_version = 0;
-  base::SysInfo::OperatingSystemVersionNumbers(
-      &os_major_version, &os_minor_version, &os_bugfix_version);
-
-  info.os_version = base::StringPrintf("%d.%d.%d", os_major_version,
-                                       os_minor_version, os_bugfix_version);
-  return info;
-}
-
-void SendReport(scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
-                const GURL& url,
-                const std::string& body,
-                base::OnceCallback<void()> callback) {
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->method = "POST";
-  resource_request->url = url;
-
-  const auto traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("javascript_report_error", R"(
-      semantics {
-        sender: "JavaScript error reporter"
-        description:
-          "Chrome can send JavaScript errors that occur within built-in "
-          "component extensions. If enabled, the error message, along "
-          "with information about Chrome and the operating system."
-        trigger:
-            "A JavaScript error occurs in a Chrome component extension"
-        data:
-            "The JavaScript error message, the version and channel of Chrome, "
-            "the URL of the extension, the line and column number where the "
-            "error occurred, and a stack trace of the error."
-        destination: GOOGLE_OWNED_SERVICE
-      }
-  )");
-
-  DVLOG(1) << "Sending crash report: " << resource_request->url;
-
-  auto url_loader = network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-
-  if (!body.empty()) {
-    url_loader->AttachStringForUpload(body, "text/plain");
-  }
-
-  network::SimpleURLLoader* loader = url_loader.get();
-  loader->DownloadToString(
-      loader_factory.get(),
-      base::BindOnce(&OnRequestComplete, std::move(url_loader),
-                     std::move(callback)),
-      kCrashEndpointResponseMaxSizeInBytes);
-}
-
-void ReportJavaScriptError(
-    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
-    const crash_report_private::ErrorInfo& error,
-    base::OnceCallback<void()> callback,
-    const std::string& redacted_message) {
-  const auto platform = GetPlatformInfo();
-
-  const GURL source(error.url);
-  const auto product = error.product ? *error.product : platform.product_name;
-  const auto version = error.version ? *error.version : platform.version;
-
-  ParameterMap params;
-  params["prod"] = net::EscapeQueryParamValue(product, /* use_plus */ false);
-  params["ver"] = net::EscapeQueryParamValue(version, /* use_plus */ false);
-  params["type"] = "JavascriptError";
-  params["error_message"] = redacted_message;
-  params["browser"] = "Chrome";
-  params["browser_version"] = platform.version;
-  params["channel"] = platform.channel;
-  params["os"] = "ChromeOS";
-  params["os_version"] = platform.os_version;
-  params["full_url"] = source.spec();
-  params["url"] = source.path();
-  params["src"] = source.spec();
-  if (error.line_number)
-    params["line"] = *error.line_number;
-  if (error.column_number)
-    params["column"] = *error.column_number;
-
-  const GURL url(base::StrCat(
-      {GetCrashEndpoint(), "?", BuildPostRequestQueryString(params)}));
-  const std::string body =
-      error.stack_trace
-          ? RemoveErrorMessageFromStackTrace(*error.stack_trace, error.message)
-          : "";
-
-  SendReport(loader_factory, url, body, std::move(callback));
-}
-
-std::string RedactErrorMessage(const std::string& message) {
-  return feedback::RedactionTool(/*first_party_extension_ids=*/nullptr)
-      .Redact(message);
-}
-
 }  // namespace
 
 CrashReportPrivateReportErrorFunction::CrashReportPrivateReportErrorFunction() =
@@ -209,45 +32,42 @@
       g_clock->Now() - g_last_called_time < base::TimeDelta::FromHours(1)) {
     return RespondNow(Error("Too many calls to this API"));
   }
+  g_last_called_time = base::Time::Now();
 
   // TODO(https://crbug.com/986166): Use crash_reporter for Chrome OS.
   const auto params = crash_report_private::ReportError::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  // Consent checking may be blocking, so do it on a separate thread to avoid
-  // blocking the UI thread.
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&crash_reporter::GetClientCollectStatsConsent),
-      base::BindOnce(
-          &CrashReportPrivateReportErrorFunction::OnConsentCheckCompleted, this,
-          std::move(params->info)));
-
-  return RespondLater();
-}
-
-void CrashReportPrivateReportErrorFunction::OnConsentCheckCompleted(
-    crash_report_private::ErrorInfo info,
-    bool consented) {
-  // Do not report errors if the user did not give consent for crash reporting.
-  if (!consented) {
-    Respond(NoArguments());
-    return;
+  JavaScriptErrorReport error_report;
+  error_report.message = std::move(params->info.message);
+  error_report.url = std::move(params->info.url);
+  if (params->info.product) {
+    error_report.product = std::move(*params->info.product);
   }
 
-  scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
-      content::BrowserContext::GetDefaultStoragePartition(browser_context())
-          ->GetURLLoaderFactoryForBrowserProcess();
+  if (params->info.version) {
+    error_report.version = std::move(*params->info.version);
+  }
 
-  // Don't redact the report on the UI thread as it can take some time.
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&RedactErrorMessage, info.message),
-      base::BindOnce(
-          &ReportJavaScriptError, std::move(loader_factory), std::move(info),
-          base::BindOnce(
-              &CrashReportPrivateReportErrorFunction::OnReportComplete, this)));
+  if (params->info.line_number) {
+    error_report.line_number = *params->info.line_number;
+  }
 
-  g_last_called_time = base::Time::Now();
+  if (params->info.column_number) {
+    error_report.column_number = *params->info.column_number;
+  }
+
+  if (params->info.stack_trace) {
+    error_report.stack_trace = std::move(*params->info.stack_trace);
+  }
+
+  SendJavaScriptErrorReport(
+      std::move(error_report),
+      base::BindOnce(&CrashReportPrivateReportErrorFunction::OnReportComplete,
+                     this),
+      browser_context());
+
+  return RespondLater();
 }
 
 void CrashReportPrivateReportErrorFunction::OnReportComplete() {
@@ -258,9 +78,5 @@
   g_clock = clock;
 }
 
-void SetCrashEndpointForTesting(const std::string& endpoint) {
-  GetCrashEndpoint() = endpoint;
-}
-
 }  // namespace api
 }  // namespace extensions
diff --git a/extensions/browser/api/crash_report_private/crash_report_private_api.h b/extensions/browser/api/crash_report_private/crash_report_private_api.h
index 0a7c7af..fd3eab2 100644
--- a/extensions/browser/api/crash_report_private/crash_report_private_api.h
+++ b/extensions/browser/api/crash_report_private/crash_report_private_api.h
@@ -29,8 +29,6 @@
   ResponseAction Run() override;
 
  private:
-  void OnConsentCheckCompleted(crash_report_private::ErrorInfo info,
-                               bool consented);
   void OnReportComplete();
 
   DISALLOW_COPY_AND_ASSIGN(CrashReportPrivateReportErrorFunction);
@@ -38,8 +36,6 @@
 
 void SetClockForTesting(base::Clock* clock);
 
-void SetCrashEndpointForTesting(const std::string& endpoint);
-
 }  // namespace api
 }  // namespace extensions
 
diff --git a/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc b/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
index 30c2a8f4..86378103 100644
--- a/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
+++ b/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
@@ -4,9 +4,9 @@
 
 #include "base/system/sys_info.h"
 #include "base/test/simple_test_clock.h"
+#include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/api/crash_report_private/crash_report_private_api.h"
-#include "extensions/browser/api/crash_report_private/mock_crash_endpoint.h"
 #include "extensions/browser/browsertest_util.h"
 #include "extensions/common/switches.h"
 #include "extensions/shell/test/shell_apitest.h"
@@ -76,7 +76,7 @@
   }
 
  protected:
-  const MockCrashEndpoint::Report& last_report() {
+  const base::Optional<MockCrashEndpoint::Report>& last_report() {
     return crash_endpoint_->last_report();
   }
   const Extension* extension_;
@@ -97,40 +97,47 @@
   ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
                                 kTestScript);
 
-  EXPECT_EQ(last_report().query,
+  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
+  ASSERT_TRUE(report);
+  EXPECT_EQ(report->query,
             "browser=Chrome&browser_version=1.2.3.4&channel=Stable&"
             "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2F&"
             "os=ChromeOS&os_version=" +
                 GetOsVersion() +
-                "&prod=Chrome%2520(Chrome%2520OS)&src=http%3A%2F%2Fwww.test."
+                "&prod=Chrome_ChromeOS&src=http%3A%2F%2Fwww.test."
                 "com%2F&type=JavascriptError&url=%2F&ver=1.2.3.4");
-  EXPECT_EQ(last_report().content, "");
+  EXPECT_EQ(report->content, "");
 }
 
 IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, ExtraParamsAndStackTrace) {
-  constexpr char kTestScript[] = R"(
+  constexpr char kTestScript[] = R"-(
     chrome.crashReportPrivate.reportError({
         message: "hi",
         url: "http://www.test.com/foo",
-        product: "TestApp",
+        product: "Chrome (Chrome OS)",
         version: "1.0.0.0",
         lineNumber: 123,
         columnNumber: 456,
         stackTrace: "   at <anonymous>:1:1",
       },
       () => window.domAutomationController.send(""));
-  )";
+  )-";
   ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
                                 kTestScript);
 
-  EXPECT_EQ(last_report().query,
-            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=%C8&"
+  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
+  ASSERT_TRUE(report);
+  // The product name is escaped twice. The first time, it becomes
+  // "Chrome%20(Chrome%20OS)" and then the second escapes the '%' into '%25'.
+  EXPECT_EQ(report->query,
+            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=456&"
             "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2Ffoo"
-            "&line=%7B&os=ChromeOS&os_version=" +
+            "&line=123&os=ChromeOS&os_version=" +
                 GetOsVersion() +
-                "&prod=TestApp&src=http%3A%2F%2Fwww.test.com%2Ffoo&type="
-                "JavascriptError&url=%2Ffoo&ver=1.0.0.0");
-  EXPECT_EQ(last_report().content, "   at <anonymous>:1:1");
+                "&prod=Chrome%2520(Chrome%2520OS)&"
+                "src=http%3A%2F%2Fwww.test.com%2Ffoo&"
+                "type=JavascriptError&url=%2Ffoo&ver=1.0.0.0");
+  EXPECT_EQ(report->content, "   at <anonymous>:1:1");
 }
 
 IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, StackTraceWithErrorMessage) {
@@ -149,17 +156,19 @@
   ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
                                 kTestScript);
 
-  EXPECT_EQ(last_report().query,
-            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=%C8&"
+  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
+  ASSERT_TRUE(report);
+  EXPECT_EQ(report->query,
+            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=456&"
             "error_message=hi&full_url=http%3A%2F%2Fwww.test.com%2Ffoo&"
-            "line=%7B&os=ChromeOS&os_version=" +
+            "line=123&os=ChromeOS&os_version=" +
                 GetOsVersion() +
                 "&prod=TestApp&src=http%3A%2F%2Fwww.test.com%2Ffoo&type="
                 "JavascriptError&url=%2Ffoo&ver=1.0.0.0");
-  EXPECT_EQ(last_report().content, "");
+  EXPECT_EQ(report->content, "");
 }
 
-IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, AnonymizeMessage) {
+IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, RedactMessage) {
   // We use the feedback APIs redaction tool, which scrubs many different types
   // of PII. As a sanity check, test if MAC addresses are redacted.
   constexpr char kTestScript[] = R"(
@@ -176,15 +185,17 @@
   ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
                                 kTestScript);
 
-  EXPECT_EQ(last_report().query,
-            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=%C8&"
+  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
+  ASSERT_TRUE(report);
+  EXPECT_EQ(report->query,
+            "browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=456&"
             "error_message=%5BMAC%20OUI%3D06%3A00%3A00%20IFACE%3D1%5D&"
-            "full_url=http%3A%2F%2Fwww.test.com%2Ffoo&line=%7B&os=ChromeOS&"
+            "full_url=http%3A%2F%2Fwww.test.com%2Ffoo&line=123&os=ChromeOS&"
             "os_version=" +
                 GetOsVersion() +
                 "&prod=TestApp&src=http%3A%2F%2Fwww.test.com%2Ffoo&type="
                 "JavascriptError&url=%2Ffoo&ver=1.0.0.0");
-  EXPECT_EQ(last_report().content, "");
+  EXPECT_EQ(report->content, "");
 }
 
 IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, Throttling) {
@@ -238,8 +249,8 @@
   EXPECT_EQ("", ExecuteScriptInBackgroundPage(browser_context(),
                                               extension_->id(), kTestScript));
   // The server should not receive any reports.
-  EXPECT_EQ(last_report().query, "");
-  EXPECT_EQ(last_report().content, "");
+  const base::Optional<MockCrashEndpoint::Report>& report = last_report();
+  EXPECT_FALSE(report);
 }
 
 }  // namespace extensions
diff --git a/extensions/docs/api_functions.md b/extensions/docs/api_functions.md
index 081f578..6839f80 100644
--- a/extensions/docs/api_functions.md
+++ b/extensions/docs/api_functions.md
@@ -97,14 +97,14 @@
   DECLARE_EXTENSION_FUNCTION("gizmo.frobulate", GIZMO_FROBULATE)
 
   GizmoFrobulateFunction();
+  GizmoFrobulateFunction(const GizmoFrobulateFunction&) = delete;
+  GizmoFrobulateFunction& operator=(const GizmoFrobulateFunction&) = delete;
 
  private:
   ~GizmoFrobulateFunction() override;
 
   // ExtensionFunction:
   ResponseAction Run() override;
-
-  DISALLOW_COPY_AND_ASSIGN(GizmoFrobulateFunction);
 };
 ```
 
diff --git a/google_apis/gcm/base/socket_stream.cc b/google_apis/gcm/base/socket_stream.cc
index 65510186..2bc252f81 100644
--- a/google_apis/gcm/base/socket_stream.cc
+++ b/google_apis/gcm/base/socket_stream.cc
@@ -216,7 +216,6 @@
   DCHECK_LT(error, net::ERR_IO_PENDING);
   ResetInternal();
   last_error_ = error;
-  LOG(ERROR) << "Closing stream with result " << error;
 }
 
 SocketOutputStream::SocketOutputStream(
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 98159da..a63d44f 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3543,6 +3543,22 @@
       "features": [
         "reset_vp_when_colorspace_changes"
       ]
+    },
+    {
+      "id": 351,
+      "cr_bugs": [1079393],
+      "description": "Some Intel GPUs have NV12 overlay support, but aren't advertised.",
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x8086",
+      "device_id": ["0x5906", "0x5913", "0x5916", "0x5921", "0x5926", "0x590e",
+                    "0x5915", "0x591e", "0x5902", "0x5917", "0x590b", "0x5908",
+                    "0x591b", "0x593b", "0x590a", "0x591a", "0x591d", "0x5923",
+                    "0x5927", "0x5932", "0x592b", "0x592a", "0x593a", "0x593d"],
+      "features": [
+        "force_nv12_overlay_support"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 1e064f7..fc52c0ff 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -72,6 +72,7 @@
 force_high_performance_gpu
 force_int_or_srgb_cube_texture_complete
 force_low_power_gpu
+force_nv12_overlay_support
 force_rgb10a2_overlay_support_flags
 force_update_scissor_state_when_binding_fbo0
 get_frag_data_info_bug
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index b3dd888..7a4deca1 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -96,6 +96,9 @@
           gpu::ENABLE_BGRA8_OVERLAYS_WITH_YUV_OVERLAY_SUPPORT)) {
     gl::DirectCompositionSurfaceWin::EnableBGRA8OverlaysWithYUVOverlaySupport();
   }
+  if (gpu_feature_info.IsWorkaroundEnabled(gpu::FORCE_NV12_OVERLAY_SUPPORT)) {
+    gl::DirectCompositionSurfaceWin::ForceNV12OverlaySupport();
+  }
   DCHECK(gpu_info);
   CollectHardwareOverlayInfo(&gpu_info->overlay_info);
 #elif defined(OS_ANDROID)
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index e1e8d71..b01ed8a 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -1015,6 +1015,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/linux-inverse-fieldtrials-fyi-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/linux-lacros-fyi-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index dd345a15..95795a3 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -10818,6 +10818,35 @@
       }
     }
     builders {
+      name: "linux-inverse-fieldtrials-fyi-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+      }
+    }
+    builders {
       name: "linux-lacros-builder-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -23405,6 +23434,43 @@
       }
     }
     builders {
+      name: "linux-inverse-fieldtrials-fyi-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"tryserver.chromium.linux\",\"recipe\":\"chromium_trybot\"}"
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+      }
+    }
+    builders {
       name: "linux-lacros-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 923bdb7..6bb8463 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -6689,6 +6689,10 @@
     category: "linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/linux-inverse-fieldtrials-fyi-rel"
+    category: "linux"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/linux-lacros-builder-fyi-rel"
     category: "linux"
   }
@@ -7949,1436 +7953,6 @@
   include_experimental_builds: true
 }
 consoles {
-  id: "chromium.goma.migration"
-  name: "chromium.goma.migration"
-  repo_url: "https://chromium.googlesource.com/chromium/src"
-  refs: "regexp:refs/heads/master"
-  manifest_name: "REVISION"
-  builders {
-    name: "buildbucket/luci.chromium.ci/VR Linux"
-    category: "week1|linux"
-    short_name: "vr"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mojo Linux"
-    category: "week1|linux"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)"
-    category: "week1|linux|dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
-    category: "week1|linux|dbg"
-    short_name: "32bit"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux CFI"
-    category: "week1|linux|cfi"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux MSan Builder"
-    category: "week1|linux"
-    short_name: "msan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Afl Upload Linux ASan"
-    category: "week1|linux"
-    short_name: "afl-asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/WebKit Linux ASAN"
-    category: "week1|linux|webkit"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/WebKit Linux Leak"
-    category: "week1|linux|webkit"
-    short_name: "leak"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/WebKit Linux MSAN"
-    category: "week1|linux|webkit"
-    short_name: "msan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI 32 Vk Release (Pixel 2)"
-    category: "week2a|android|32"
-    short_name: "p2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI 32 dEQP Vk Release (Pixel 2)"
-    category: "week2a|android|32deqp"
-    short_name: "p2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI 64 Vk Release (Pixel 2)"
-    category: "week2a|android|64"
-    short_name: "p2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI 64 dEQP Vk Release (Pixel 2)"
-    category: "week2a|android|64deqp"
-    short_name: "p2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (NVIDIA Shield TV)"
-    category: "week2a|android|rel"
-    short_name: "shdtv"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Nexus 5)"
-    category: "week2a|android|rel"
-    short_name: "n5"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Nexus 5X)"
-    category: "week2a|android|rel"
-    short_name: "n5x"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Nexus 6)"
-    category: "week2a|android|rel"
-    short_name: "n6"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Nexus 6P)"
-    category: "week2a|android|rel"
-    short_name: "n6p"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Nexus 9)"
-    category: "week2a|android|rel"
-    short_name: "n9"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI Release (Pixel 2)"
-    category: "week2a|android|rel"
-    short_name: "p2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI dEQP Release (Nexus 5X)"
-    category: "week2a|android|deqp"
-    short_name: "n5x"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Deterministic Android"
-    category: "week2a|android|det"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Deterministic Android (dbg)"
-    category: "week2a|android|det"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder"
-    category: "week2b|android|release"
-    short_name: "32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder (dbg)"
-    category: "week2b|android|debug|builder"
-    short_name: "32"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 (dbg)"
-    category: "week2b|android|debug|builder"
-    short_name: "64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder"
-    category: "week2b|linux|release"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder (dbg)"
-    category: "week2b|linux|debug"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder"
-    category: "week2b|mac|release"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder (dbg)"
-    category: "week2b|mac|debug"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc/WebRTC Chromium Android Builder"
-    category: "week2b|android"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc/WebRTC Chromium Linux Builder"
-    category: "week2b|linux"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc/WebRTC Chromium Mac Builder"
-    category: "week2b|mac"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac ASAN Release"
-    category: "week2c|mac|asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac ASAN Release Media"
-    category: "week2c|mac|asan"
-    short_name: "media"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac ASan 64 Builder"
-    category: "week2c|mac|asan"
-    short_name: "64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Mac ASan"
-    category: "week2c|mac|asan"
-    short_name: "fuzz"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android CFI"
-    category: "week2c|android"
-    short_name: "cfi"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Site Isolation Android"
-    category: "week2c|android"
-    short_name: "isolate"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mojo Android"
-    category: "week2c|android"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android x64 Builder (dbg)"
-    category: "week2c|android|dbg"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android x86 Builder (dbg)"
-    category: "week2c|android|dbg"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android WebView L (dbg)"
-    category: "week2c|android|dbg|webview"
-    short_name: "l"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android WebView M (dbg)"
-    category: "week2c|android|dbg|webview"
-    short_name: "m"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android WebView N (dbg)"
-    category: "week2c|android|dbg|webview"
-    short_name: "n"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android WebView O (dbg)"
-    category: "week2c|android|dbg|webview"
-    short_name: "o"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android WebView P FYI (rel)"
-    category: "week2c|android|rel|webview"
-    short_name: "p"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Linux x64 Builder"
-    category: "week2d|linux|dawn"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Linux x64 DEPS Builder"
-    category: "week2d|linux|dawn"
-    short_name: "deps"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Linux Builder"
-    category: "week2d|linux|gpu|fyi"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Linux Builder (dbg)"
-    category: "week2d|linux|gpu|fyi"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Linux Ozone Builder"
-    category: "week2d|linux|gpu|fyi"
-    short_name: "ozone"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Linux dEQP Builder"
-    category: "week2d|linux|gpu|fyi"
-    short_name: "deqp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux FYI GPU TSAN Release"
-    category: "week2d|linux|gpu|fyi"
-    short_name: "tsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Linux Builder (dbg)"
-    category: "week2d|linux|gpu"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Viz"
-    category: "week2d|linux"
-    short_name: "viz"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux remote_run Builder"
-    category: "week2d|linux"
-    short_name: "rem"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Closure Compilation Linux"
-    category: "week2d|linux"
-    short_name: "clsr"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Deterministic Linux"
-    category: "week2d|linux|det"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Deterministic Linux (dbg)"
-    category: "week2d|linux|det"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Mac x64 Builder"
-    category: "week2d|mac|dawn"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Mac x64 DEPS Builder"
-    category: "week2d|mac|dawn"
-    short_name: "deps"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Mac Builder"
-    category: "week2d|mac|gpu|fyi"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Mac Builder (dbg)"
-    category: "week2d|mac|gpu|fyi"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Mac dEQP Builder"
-    category: "week2d|mac|gpu|fyi"
-    short_name: "deqp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac FYI GPU ASAN Release"
-    category: "week2d|mac|gpu|fyi"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Mac Builder (dbg)"
-    category: "week2d|mac|gpu"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac deterministic"
-    category: "week2d|mac|det"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac deterministic (dbg)"
-    category: "week2d|mac|det"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Builder"
-    category: "week2.5|linux"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Linux Builder"
-    category: "week2.5|linux"
-    short_name: "gpu"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-ozone-rel"
-    category: "week3a|linux"
-    short_name: "ozone"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-annotator-rel"
-    category: "week3a|linux"
-    short_name: "anno"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-code-coverage"
-    category: "week3a|linux"
-    short_name: "code"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-blink-animation-use-time-delta"
-    category: "week3a|linux|blink"
-    short_name: "anim"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-blink-heap-concurrent-marking-tsan-rel"
-    category: "week3a|linux|blink"
-    short_name: "tsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-blink-heap-verification"
-    category: "week3a|linux|blink"
-    short_name: "ver"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-chromium-tests-staging-builder"
-    category: "week3a|linux"
-    short_name: "crtests"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-arm-dbg"
-    category: "week3b|android|cronet|arm"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-arm-rel"
-    category: "week3b|android|cronet|arm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-asan-arm-rel"
-    category: "week3b|android|cronet|arm"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-kitkat-arm-rel"
-    category: "week3b|android|cronet|arm"
-    short_name: "kkat"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-lollipop-arm-rel"
-    category: "week3b|android|cronet|arm"
-    short_name: "lpop"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-arm64-rel"
-    category: "week3b|android|cronet|arm64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-arm64-dbg"
-    category: "week3b|android|cronet|arm64"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-marshmallow-arm64-rel"
-    category: "week3b|android|cronet|arm64"
-    short_name: "marsh"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-marshmallow-arm64-perf-rel"
-    category: "week3b|android|cronet|arm64"
-    short_name: "perf"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-x86-rel"
-    category: "week3b|android|cronet|x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-cronet-x86-dbg"
-    category: "week3b|android|cronet|x86"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-incremental-dbg"
-    category: "week3b|android"
-    short_name: "inc"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-mojo-webview-rel"
-    category: "week3b|android"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-pie-arm64-dbg"
-    category: "week3b|linux"
-    short_name: "pie"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-code-coverage"
-    category: "week3c|mac"
-    short_name: "code"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-hermetic-upgrade-rel"
-    category: "week3c|mac"
-    short_name: "herm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-mojo-rel"
-    category: "week3c|mac"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-osxbeta-rel"
-    category: "week3c|mac"
-    short_name: "osx"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac Builder"
-    category: "week3c|mac"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac Builder (dbg)"
-    category: "week3c|mac"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Mac Builder"
-    category: "week3c|mac"
-    short_name: "gpu"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
-    category: "week4|linux"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan Debug"
-    category: "week4|linux"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux MSan"
-    category: "week4|linux"
-    short_name: "msan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux UBSan"
-    category: "week4|linux"
-    short_name: "ubsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux V8-ARM64 ASan"
-    category: "week4|linux|v8arm"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux V8-ARM64 ASan Debug"
-    category: "week4|linux|v8arm"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 ASan"
-    category: "week4|linux32"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 ASan Debug"
-    category: "week4|linux32"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 V8-ARM ASan"
-    category: "week4|linux32|v8arm"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux32 V8-ARM ASan Debug"
-    category: "week4|linux32|v8arm"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASan Debug (32-bit x86 with V8-ARM)"
-    category: "week5|asan"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASan Release (32-bit x86 with V8-ARM)"
-    category: "week5|asan"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASan Release Media (32-bit x86 with V8-ARM)"
-    category: "week5|asan"
-    short_name: "media"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASAN Debug"
-    category: "week6|asan"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASAN Release"
-    category: "week6|asan"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ASAN Release Media"
-    category: "week6|asan"
-    short_name: "media"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/MSAN Release (chained origins)"
-    category: "week7|msan"
-    short_name: "chain"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/MSAN Release (no origins)"
-    category: "week7|msan"
-    short_name: "none"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/TSAN Release"
-    category: "week8|tsan"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/TSAN Debug"
-    category: "week8|tsan"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/UBSan Release"
-    category: "week9|ubsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/UBSan vptr Release"
-    category: "week9|ubsan|vptr"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/UBSanVptr Linux"
-    category: "week9|ubsan|vptr"
-    short_name: "lnx"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Cast Android (dbg)"
-    category: "week10|android"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Cast Audio Linux"
-    category: "week10|linux"
-    short_name: "audio"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Cast Linux"
-    category: "week10|linux"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
-    category: "week11|fuchsia|arm64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-arm64-cast"
-    category: "week11|fuchsia|arm64"
-    short_name: "cast"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
-    category: "week11|fuchsia|x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-x64-cast"
-    category: "week11|fuchsia|x64"
-    short_name: "cast"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-arm64-rel"
-    category: "week11|fuchsia|fyi"
-    short_name: "arm64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-x64-dbg"
-    category: "week11|fuchsia|fyi"
-    short_name: "x64 dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-x64-rel"
-    category: "week11|fuchsia|fyi"
-    short_name: "x64 rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-marshmallow-arm64-rel"
-    category: "week13|android"
-    short_name: "marsh"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android Release (Nexus 5X)"
-    category: "week13|android"
-    short_name: "n5x"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Builder"
-    category: "week14a|linux"
-    short_name: "asanlsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux TSan Builder"
-    category: "week14a|linux"
-    short_name: "tsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android ASAN (dbg)"
-    category: "week14b|android"
-    short_name: "asanlsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android arm Builder (dbg)"
-    category: "week14b|android"
-    short_name: "tsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android arm64 Builder (dbg)"
-    category: "week14b|android"
-    short_name: "tsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-x64-dbg"
-    category: "week15a|fuchsia"
-    short_name: "x64dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-archive-dbg"
-    category: "week15a|linux|archive"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-archive-rel"
-    category: "week15a|linux|archive"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-bfcache-rel"
-    category: "week15a|linux"
-    short_name: "bfc"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-fieldtrial-rel"
-    category: "week15a|linux"
-    short_name: "field"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-archive-rel"
-    category: "week15b|android|archive"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-archive-dbg"
-    category: "week15b|android|archive"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-asan"
-    category: "week15b|android"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-bfcache-rel"
-    category: "week15b|android"
-    short_name: "bfc"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-code-coverage"
-    category: "week15b|android"
-    short_name: "code"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-pie-arm64-rel"
-    category: "week15b|android|pie"
-    short_name: "arm64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-pie-x86-rel"
-    category: "week15b|android|pie"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Perf Android 64 Builder"
-    category: "week15b|android|gpu"
-    short_name: "perf64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-archive-rel"
-    category: "week15b|mac|archive"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/mac-archive-dbg"
-    category: "week15b|mac|archive"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-marshmallow-x86-rel-non-cq"
-    category: "week16b"
-    short_name: "marsh"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android FYI 64 Perf (Pixel 2)"
-    category: "week16b"
-    short_name: "pxl2"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-tot-angle-x64"
-    category: "week17|swangle|angle"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-tot-angle-x86"
-    category: "week17|swangle|angle"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-tot-swiftshader-x64"
-    category: "week17|swangle|swift"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-tot-swiftshader-x86"
-    category: "week17|swangle|swift"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-x64"
-    category: "week17|swangle"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-swangle-x86"
-    category: "week17|swangle"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/android-code-coverage-native"
-    category: "misc|android"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win ASan Release"
-    category: "win|week1|asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win ASan Release Media"
-    category: "win|week1|asan"
-    short_name: "media"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win10-code-coverage"
-    category: "win|week1.1"
-    short_name: "code"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win x64 Builder (dbg)"
-    category: "win|week1.5"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-archive-dbg"
-    category: "win|week1.5|archive"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-archive-rel"
-    category: "win|week1.5|archive"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 Builder"
-    category: "win|week2|dawn"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 DEPS Builder"
-    category: "win|week2|dawn"
-    short_name: "deps"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder (dbg)"
-    category: "win|week2|gpu"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI XR Win x64 Builder"
-    category: "win|week2|gpu|fyi"
-    short_name: "xr"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 dEQP Builder"
-    category: "win|week2|gpu|fyi"
-    short_name: "deqp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder"
-    category: "win|week2|gpu|fyi|dx12"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
-    category: "win|week2|gpu|fyi|dx12"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder"
-    category: "win|week2|gpu|fyi"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (dbg)"
-    category: "win|week2|gpu|fyi"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win Builder"
-    category: "win|week3"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win Builder (dbg)"
-    category: "win|week3"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win Builder"
-    category: "win|week3|gpu|fyi"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win Builder (dbg)"
-    category: "win|week3|gpu|fyi"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win dEQP Builder"
-    category: "win|week3|gpu|fyi"
-    short_name: "deqp"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 Builder"
-    category: "win|week3|dawn"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 DEPS Builder"
-    category: "win|week3|dawn"
-    short_name: "deps"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win32-archive-rel"
-    category: "win|week4|win32"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win32-archive-dbg"
-    category: "win|week4|win32"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win32-arm64-rel"
-    category: "win|week4|win32"
-    short_name: "arm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Windows ASan"
-    category: "win|week4"
-    short_name: "libfuzzer"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Windows deterministic"
-    category: "win|week4"
-    short_name: "det"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mojo Windows"
-    category: "win|week4"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-celab-builder-rel"
-    category: "win|week4"
-    short_name: "celab"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-asan"
-    category: "win|week4"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win 10 Fast Ring"
-    category: "win|week4"
-    short_name: "fastring"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-annotator-rel"
-    category: "win|week4"
-    short_name: "anno"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-pixel-builder-rel"
-    category: "win|week4"
-    short_name: "pixel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc/WebRTC Chromium Win Builder"
-    category: "win|week4|webrtc"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder"
-    category: "win|week4|webrtc|fyi"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder (dbg)"
-    category: "win|week4|webrtc|fyi"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-x86"
-    category: "win|week4|swangle"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-x64"
-    category: "win|week4|swangle"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-tot-angle-x86"
-    category: "win|week4|swangle|angle"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-tot-angle-x64"
-    category: "win|week4|swangle|angle"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-tot-swiftshader-x86"
-    category: "win|week4|swangle|swift"
-    short_name: "x86"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/win-swangle-tot-swiftshader-x64"
-    category: "win|week4|swangle|swift"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Win x64 Builder"
-    category: "win|week5"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder"
-    category: "win|week5"
-    short_name: "gpu"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-chromeos-dbg"
-    category: "cros|week1"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-chromeos-rel"
-    category: "cros|week1"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/linux-chromeos-code-coverage"
-    category: "cros|week1"
-    short_name: "code"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux ChromiumOS Full"
-    category: "cros|week2"
-    short_name: "full"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ChromiumOS ASAN Release"
-    category: "cros|week2"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Chromium OS ASan LSan Builder"
-    category: "cros|week2"
-    short_name: "asan lsan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux ChromiumOS MSan Builder"
-    category: "cros|week2"
-    short_name: "msan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mojo ChromiumOS"
-    category: "cros|week2"
-    short_name: "mojo"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Chrome OS ASan"
-    category: "cros|week2"
-    short_name: "fuzz"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-rel"
-    category: "cros|week3|amd64"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-dbg"
-    category: "cros|week3|amd64"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-asan-rel"
-    category: "cros|week3|amd64"
-    short_name: "asan"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-cfi-thin-lto-rel"
-    category: "cros|week3|amd64"
-    short_name: "thinlto"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-arm-generic-rel"
-    category: "cros|week3|arm"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-arm-generic-dbg"
-    category: "cros|week3|arm"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-kevin-rel"
-    category: "cros|week3|kevin"
-    short_name: "rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI ios-device"
-    category: "ios|week1a"
-    short_name: "dev"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator"
-    category: "ios|week1a"
-    short_name: "sim"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-device"
-    category: "ios|week1b"
-    short_name: "dev"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator"
-    category: "ios|week1b|sim"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator-full-configs"
-    category: "ios|week1b|sim"
-    short_name: "fullconf"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator-noncq"
-    category: "ios|week1b|sim"
-    short_name: "noncq"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios13-beta-simulator"
-    category: "ios|week1b|ios13|beta"
-    short_name: "sim"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios13-sdk-simulator"
-    category: "ios|week1b|ios13|sdk"
-    short_name: "sim"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator-cronet"
-    category: "ios|week1c"
-    short_name: "cro"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator-cr-recipe"
-    category: "ios|week1c"
-    short_name: "crr"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-webkit-tot"
-    category: "ios|week1c"
-    short_name: "webkit"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios13-sdk-device"
-    category: "ios|week1c|ios13"
-    short_name: "dev"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios-simulator-code-coverage"
-    category: "ios|week1d"
-    short_name: "code"
-  }
-  header {
-    oncalls {
-      name: "Chromium"
-      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
-    }
-    oncalls {
-      name: "Android"
-      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-android-sheriff"
-    }
-    oncalls {
-      name: "iOS"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_ios.json"
-    }
-    oncalls {
-      name: "GPU"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
-    }
-    oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
-    }
-    oncalls {
-      name: "Perf"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_perf.json"
-    }
-    oncalls {
-      name: "Perfbot"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_perfbot.json"
-    }
-    oncalls {
-      name: "Trooper"
-      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-ops-client-infra"
-      show_primary_secondary_labels: true
-    }
-    links {
-      name: "Builds"
-      links {
-        text: "continuous"
-        url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
-        alt: "Continuous browser snapshots"
-      }
-      links {
-        text: "symbols"
-        url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
-        alt: "Windows Symbols"
-      }
-      links {
-        text: "status"
-        url: "https://chromium-status.appspot.com/"
-        alt: "Current tree status"
-      }
-    }
-    links {
-      name: "Dashboards"
-      links {
-        text: "perf"
-        url: "https://chromeperf.appspot.com/"
-        alt: "Chrome perf dashboard"
-      }
-      links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
-        alt: "New flake portal"
-      }
-      links {
-        text: "legacy-flakiness"
-        url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
-        alt: "Legacy flakiness dashboard"
-      }
-    }
-    links {
-      name: "Chromium"
-      links {
-        text: "source"
-        url: "https://chromium.googlesource.com/chromium/src"
-        alt: "Chromium source code repository"
-      }
-      links {
-        text: "reviews"
-        url: "https://chromium-review.googlesource.com"
-        alt: "Chromium code review tool"
-      }
-      links {
-        text: "bugs"
-        url: "https://crbug.com"
-        alt: "Chromium bug tracker"
-      }
-      links {
-        text: "coverage"
-        url: "https://analysis.chromium.org/p/chromium/coverage"
-        alt: "Chromium code coverage dashboard"
-      }
-      links {
-        text: "dev"
-        url: "https://dev.chromium.org/Home"
-        alt: "Chromium developer home page"
-      }
-      links {
-        text: "support"
-        url: "https://support.google.com/chrome/#topic=7438008"
-        alt: "Google Chrome help center"
-      }
-    }
-    links {
-      name: "Consoles"
-      links {
-        text: "android"
-        url: "/p/chromium/g/chromium.android"
-        alt: "Chromium Android console"
-      }
-      links {
-        text: "clang"
-        url: "/p/chromium/g/chromium.clang"
-        alt: "Chromium Clang console"
-      }
-      links {
-        text: "dawn"
-        url: "/p/chromium/g/chromium.dawn"
-        alt: "Chromium Dawn console"
-      }
-      links {
-        text: "fuzz"
-        url: "/p/chromium/g/chromium.fuzz"
-        alt: "Chromium Fuzz console"
-      }
-      links {
-        text: "fyi"
-        url: "/p/chromium/g/chromium.fyi"
-        alt: "Chromium FYI console"
-      }
-      links {
-        text: "gpu"
-        url: "/p/chromium/g/chromium.gpu"
-        alt: "Chromium GPU console"
-      }
-      links {
-        text: "perf"
-        url: "/p/chrome/g/chrome.perf/console"
-        alt: "Chromium Perf console"
-      }
-      links {
-        text: "perf.fyi"
-        url: "/p/chrome/g/chrome.perf.fyi/console"
-        alt: "Chromium Perf FYI console"
-      }
-      links {
-        text: "swangle"
-        url: "/p/chromium/g/chromium.swangle"
-        alt: "Chromium SWANGLE console"
-      }
-      links {
-        text: "webrtc"
-        url: "/p/chromium/g/chromium.webrtc"
-        alt: "Chromium WebRTC console"
-      }
-      links {
-        text: "chromiumos"
-        url: "/p/chromium/g/chromium.chromiumos"
-        alt: "ChromiumOS console"
-      }
-    }
-    links {
-      name: "Branch Consoles"
-      links {
-        text: "m86"
-        url: "/p/chromium-m86/g/main/console"
-        alt: "Beta branch console"
-      }
-      links {
-        text: "m85"
-        url: "/p/chromium/g/main-m85/console"
-        alt: "Stable branch console"
-      }
-      links {
-        text: "trunk"
-        url: "/p/chromium/g/main/console"
-        alt: "Trunk (ToT) console"
-      }
-    }
-    links {
-      name: "Tryservers"
-      links {
-        text: "android"
-        url: "/p/chromium/g/tryserver.chromium.android/builders"
-        alt: "Android"
-      }
-      links {
-        text: "angle"
-        url: "/p/chromium/g/tryserver.chromium.angle/builders"
-        alt: "Angle"
-      }
-      links {
-        text: "blink"
-        url: "/p/chromium/g/tryserver.blink/builders"
-        alt: "Blink"
-      }
-      links {
-        text: "chrome"
-        url: "/p/chrome/g/tryserver.chrome/builders"
-        alt: "Chrome"
-      }
-      links {
-        text: "chromiumos"
-        url: "/p/chromium/g/tryserver.chromium.chromiumos/builders"
-        alt: "ChromiumOS"
-      }
-      links {
-        text: "linux"
-        url: "/p/chromium/g/tryserver.chromium.linux/builders"
-        alt: "Linux"
-      }
-      links {
-        text: "mac"
-        url: "/p/chromium/g/tryserver.chromium.mac/builders"
-        alt: "Mac"
-      }
-      links {
-        text: "swangle"
-        url: "/p/chromium/g/tryserver.chromium.swangle/builders"
-        alt: "SWANGLE"
-      }
-      links {
-        text: "win"
-        url: "/p/chromium/g/tryserver.chromium.win/builders"
-        alt: "Win"
-      }
-    }
-    links {
-      name: "Navigate"
-      links {
-        text: "about"
-        url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
-        alt: "Tour of the console"
-      }
-      links {
-        text: "customize"
-        url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo.cfg"
-        alt: "Customize this console"
-      }
-    }
-    console_groups {
-      title {
-        text: "Tree Closers"
-        url: "https://chromium-status.appspot.com/"
-      }
-      console_ids: "chromium/chromium"
-      console_ids: "chromium/chromium.win"
-      console_ids: "chromium/chromium.mac"
-      console_ids: "chromium/chromium.linux"
-      console_ids: "chromium/chromium.chromiumos"
-      console_ids: "chrome/chrome"
-      console_ids: "chromium/chromium.memory"
-      console_ids: "chromium/chromium.gpu"
-    }
-    console_groups {
-      console_ids: "chromium/chromium.android"
-      console_ids: "chrome/chrome.perf"
-      console_ids: "chromium/chromium.gpu.fyi"
-      console_ids: "chromium/chromium.swangle"
-      console_ids: "chromium/chromium.fuzz"
-    }
-    tree_status_host: "chromium-status.appspot.com"
-  }
-  include_experimental_builds: true
-}
-consoles {
   id: "chromium.gpu"
   name: "chromium.gpu"
   repo_url: "https://chromium.googlesource.com/chromium/src"
@@ -13885,6 +12459,9 @@
     name: "buildbucket/luci.chromium.try/linux-chromeos-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-inverse-fieldtrials-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-libfuzzer-asan-rel"
   }
   builders {
@@ -14604,6 +13181,9 @@
     name: "buildbucket/luci.chromium.try/linux-gcc-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-inverse-fieldtrials-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-lacros-fyi-rel"
   }
   builders {
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 0584895..1d6b86b 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -9030,6 +9030,16 @@
   }
 }
 job {
+  id: "ci-linux-inverse-fieldtrials-fyi-rel"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-inverse-fieldtrials-fyi-rel"
+  }
+}
+job {
   id: "ci-linux-lacros-builder-fyi-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -11274,6 +11284,16 @@
   }
 }
 job {
+  id: "linux-inverse-fieldtrials-fyi-rel"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-inverse-fieldtrials-fyi-rel"
+  }
+}
+job {
   id: "linux-lacros-builder-fyi-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -12416,6 +12436,7 @@
   triggers: "linux-chromium-tests-staging-builder"
   triggers: "linux-fieldtrial-rel"
   triggers: "linux-gcc-rel"
+  triggers: "linux-inverse-fieldtrials-fyi-rel"
   triggers: "linux-lacros-builder-fyi-rel"
   triggers: "linux-lacros-builder-rel"
   triggers: "ci-linux-official"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index f7d2279..270a095e 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -2515,6 +2515,13 @@
 )
 
 ci.fyi_builder(
+    name = "linux-inverse-fieldtrials-fyi-rel",
+    console_view_entry = ci.console_view_entry(
+        category = "linux",
+    ),
+)
+
+ci.fyi_builder(
     name = "linux-fieldtrial-rel",
     console_view_entry = ci.console_view_entry(
         category = "linux",
diff --git a/infra/config/subprojects/chromium/consoles/luci.chromium.try.star b/infra/config/subprojects/chromium/consoles/luci.chromium.try.star
index 3a9f060..10dc2c8 100644
--- a/infra/config/subprojects/chromium/consoles/luci.chromium.try.star
+++ b/infra/config/subprojects/chromium/consoles/luci.chromium.try.star
@@ -115,6 +115,7 @@
         "try/linux-blink-heap-concurrent-marking-tsan-rel",
         "try/linux-blink-heap-verification-try",
         "try/linux-chromeos-rel",
+        "try/linux-inverse-fieldtrials-fyi-rel",
         "try/linux-libfuzzer-asan-rel",
         "try/linux-ozone-rel",
         "try/linux_android_dbg_ng",
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 007312d..b8eb2fd 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -852,6 +852,10 @@
 )
 
 try_.chromium_linux_builder(
+    name = "linux-inverse-fieldtrials-fyi-rel",
+)
+
+try_.chromium_linux_builder(
     name = "linux-lacros-fyi-rel",
 )
 
diff --git a/infra/config/subprojects/goma/consoles/chromium.goma.migration.star b/infra/config/subprojects/goma/consoles/chromium.goma.migration.star
deleted file mode 100644
index 091df29..0000000
--- a/infra/config/subprojects/goma/consoles/chromium.goma.migration.star
+++ /dev/null
@@ -1,1173 +0,0 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# These are used for monitoring builders that have recently been migrated to
-# Goma RBE (See crbug.com/950413).
-luci.console_view(
-    name = "chromium.goma.migration",
-    header = "//chromium-header.textpb",
-    include_experimental_builds = True,
-    repo = "https://chromium.googlesource.com/chromium/src",
-    entries = [
-        luci.console_view_entry(
-            builder = "ci/VR Linux",
-            category = "week1|linux",
-            short_name = "vr",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mojo Linux",
-            category = "week1|linux",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux Builder (dbg)",
-            category = "week1|linux|dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux Builder (dbg)(32)",
-            category = "week1|linux|dbg",
-            short_name = "32bit",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux CFI",
-            category = "week1|linux|cfi",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux MSan Builder",
-            category = "week1|linux",
-            short_name = "msan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Afl Upload Linux ASan",
-            category = "week1|linux",
-            short_name = "afl-asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/WebKit Linux ASAN",
-            category = "week1|linux|webkit",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/WebKit Linux Leak",
-            category = "week1|linux|webkit",
-            short_name = "leak",
-        ),
-        luci.console_view_entry(
-            builder = "ci/WebKit Linux MSAN",
-            category = "week1|linux|webkit",
-            short_name = "msan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI 32 Vk Release (Pixel 2)",
-            category = "week2a|android|32",
-            short_name = "p2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI 32 dEQP Vk Release (Pixel 2)",
-            category = "week2a|android|32deqp",
-            short_name = "p2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI 64 Vk Release (Pixel 2)",
-            category = "week2a|android|64",
-            short_name = "p2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI 64 dEQP Vk Release (Pixel 2)",
-            category = "week2a|android|64deqp",
-            short_name = "p2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (NVIDIA Shield TV)",
-            category = "week2a|android|rel",
-            short_name = "shdtv",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Nexus 5)",
-            category = "week2a|android|rel",
-            short_name = "n5",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Nexus 5X)",
-            category = "week2a|android|rel",
-            short_name = "n5x",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Nexus 6)",
-            category = "week2a|android|rel",
-            short_name = "n6",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Nexus 6P)",
-            category = "week2a|android|rel",
-            short_name = "n6p",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Nexus 9)",
-            category = "week2a|android|rel",
-            short_name = "n9",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI Release (Pixel 2)",
-            category = "week2a|android|rel",
-            short_name = "p2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI dEQP Release (Nexus 5X)",
-            category = "week2a|android|deqp",
-            short_name = "n5x",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Deterministic Android",
-            category = "week2a|android|det",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Deterministic Android (dbg)",
-            category = "week2a|android|det",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Android Builder",
-            category = "week2b|android|release",
-            short_name = "32",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Android Builder (dbg)",
-            category = "week2b|android|debug|builder",
-            short_name = "32",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 (dbg)",
-            category = "week2b|android|debug|builder",
-            short_name = "64",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Linux Builder",
-            category = "week2b|linux|release",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Linux Builder (dbg)",
-            category = "week2b|linux|debug",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Mac Builder",
-            category = "week2b|mac|release",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Mac Builder (dbg)",
-            category = "week2b|mac|debug",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc/WebRTC Chromium Android Builder",
-            category = "week2b|android",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc/WebRTC Chromium Linux Builder",
-            category = "week2b|linux",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc/WebRTC Chromium Mac Builder",
-            category = "week2b|mac",
-            short_name = "bld",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac ASAN Release",
-            category = "week2c|mac|asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac ASAN Release Media",
-            category = "week2c|mac|asan",
-            short_name = "media",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac ASan 64 Builder",
-            category = "week2c|mac|asan",
-            short_name = "64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Mac ASan",
-            category = "week2c|mac|asan",
-            short_name = "fuzz",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android CFI",
-            category = "week2c|android",
-            short_name = "cfi",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Site Isolation Android",
-            category = "week2c|android",
-            short_name = "isolate",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mojo Android",
-            category = "week2c|android",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android x64 Builder (dbg)",
-            category = "week2c|android|dbg",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android x86 Builder (dbg)",
-            category = "week2c|android|dbg",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android WebView L (dbg)",
-            category = "week2c|android|dbg|webview",
-            short_name = "l",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android WebView M (dbg)",
-            category = "week2c|android|dbg|webview",
-            short_name = "m",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android WebView N (dbg)",
-            category = "week2c|android|dbg|webview",
-            short_name = "n",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android WebView O (dbg)",
-            category = "week2c|android|dbg|webview",
-            short_name = "o",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android WebView P FYI (rel)",
-            category = "week2c|android|rel|webview",
-            short_name = "p",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Linux x64 Builder",
-            category = "week2d|linux|dawn",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Linux x64 DEPS Builder",
-            category = "week2d|linux|dawn",
-            short_name = "deps",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Linux Builder",
-            category = "week2d|linux|gpu|fyi",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Linux Builder (dbg)",
-            category = "week2d|linux|gpu|fyi",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Linux Ozone Builder",
-            category = "week2d|linux|gpu|fyi",
-            short_name = "ozone",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Linux dEQP Builder",
-            category = "week2d|linux|gpu|fyi",
-            short_name = "deqp",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux FYI GPU TSAN Release",
-            category = "week2d|linux|gpu|fyi",
-            short_name = "tsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Linux Builder (dbg)",
-            category = "week2d|linux|gpu",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux Viz",
-            category = "week2d|linux",
-            short_name = "viz",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux remote_run Builder",
-            category = "week2d|linux",
-            short_name = "rem",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Closure Compilation Linux",
-            category = "week2d|linux",
-            short_name = "clsr",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Deterministic Linux",
-            category = "week2d|linux|det",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Deterministic Linux (dbg)",
-            category = "week2d|linux|det",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Mac x64 Builder",
-            category = "week2d|mac|dawn",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Mac x64 DEPS Builder",
-            category = "week2d|mac|dawn",
-            short_name = "deps",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Mac Builder",
-            category = "week2d|mac|gpu|fyi",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Mac Builder (dbg)",
-            category = "week2d|mac|gpu|fyi",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Mac dEQP Builder",
-            category = "week2d|mac|gpu|fyi",
-            short_name = "deqp",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac FYI GPU ASAN Release",
-            category = "week2d|mac|gpu|fyi",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Mac Builder (dbg)",
-            category = "week2d|mac|gpu",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac deterministic",
-            category = "week2d|mac|det",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac deterministic (dbg)",
-            category = "week2d|mac|det",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux Builder",
-            category = "week2.5|linux",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Linux Builder",
-            category = "week2.5|linux",
-            short_name = "gpu",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-ozone-rel",
-            category = "week3a|linux",
-            short_name = "ozone",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-annotator-rel",
-            category = "week3a|linux",
-            short_name = "anno",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-code-coverage",
-            category = "week3a|linux",
-            short_name = "code",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-blink-animation-use-time-delta",
-            category = "week3a|linux|blink",
-            short_name = "anim",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-blink-heap-concurrent-marking-tsan-rel",
-            category = "week3a|linux|blink",
-            short_name = "tsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-blink-heap-verification",
-            category = "week3a|linux|blink",
-            short_name = "ver",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-chromium-tests-staging-builder",
-            category = "week3a|linux",
-            short_name = "crtests",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-arm-dbg",
-            category = "week3b|android|cronet|arm",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-arm-rel",
-            category = "week3b|android|cronet|arm",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-asan-arm-rel",
-            category = "week3b|android|cronet|arm",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-kitkat-arm-rel",
-            category = "week3b|android|cronet|arm",
-            short_name = "kkat",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-lollipop-arm-rel",
-            category = "week3b|android|cronet|arm",
-            short_name = "lpop",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-arm64-rel",
-            category = "week3b|android|cronet|arm64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-arm64-dbg",
-            category = "week3b|android|cronet|arm64",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-marshmallow-arm64-rel",
-            category = "week3b|android|cronet|arm64",
-            short_name = "marsh",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-marshmallow-arm64-perf-rel",
-            category = "week3b|android|cronet|arm64",
-            short_name = "perf",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-x86-rel",
-            category = "week3b|android|cronet|x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-cronet-x86-dbg",
-            category = "week3b|android|cronet|x86",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-incremental-dbg",
-            category = "week3b|android",
-            short_name = "inc",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-mojo-webview-rel",
-            category = "week3b|android",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-pie-arm64-dbg",
-            category = "week3b|linux",
-            short_name = "pie",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-code-coverage",
-            category = "week3c|mac",
-            short_name = "code",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-hermetic-upgrade-rel",
-            category = "week3c|mac",
-            short_name = "herm",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-mojo-rel",
-            category = "week3c|mac",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-osxbeta-rel",
-            category = "week3c|mac",
-            short_name = "osx",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac Builder",
-            category = "week3c|mac",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mac Builder (dbg)",
-            category = "week3c|mac",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Mac Builder",
-            category = "week3c|mac",
-            short_name = "gpu",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux ASan",
-            category = "week4|linux",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux ASan Debug",
-            category = "week4|linux",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux MSan",
-            category = "week4|linux",
-            short_name = "msan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux UBSan",
-            category = "week4|linux",
-            short_name = "ubsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux V8-ARM64 ASan",
-            category = "week4|linux|v8arm",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux V8-ARM64 ASan Debug",
-            category = "week4|linux|v8arm",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux32 ASan",
-            category = "week4|linux32",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux32 ASan Debug",
-            category = "week4|linux32",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux32 V8-ARM ASan",
-            category = "week4|linux32|v8arm",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Linux32 V8-ARM ASan Debug",
-            category = "week4|linux32|v8arm",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASan Debug (32-bit x86 with V8-ARM)",
-            category = "week5|asan",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASan Release (32-bit x86 with V8-ARM)",
-            category = "week5|asan",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASan Release Media (32-bit x86 with V8-ARM)",
-            category = "week5|asan",
-            short_name = "media",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASAN Debug",
-            category = "week6|asan",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASAN Release",
-            category = "week6|asan",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ASAN Release Media",
-            category = "week6|asan",
-            short_name = "media",
-        ),
-        luci.console_view_entry(
-            builder = "ci/MSAN Release (chained origins)",
-            category = "week7|msan",
-            short_name = "chain",
-        ),
-        luci.console_view_entry(
-            builder = "ci/MSAN Release (no origins)",
-            category = "week7|msan",
-            short_name = "none",
-        ),
-        luci.console_view_entry(
-            builder = "ci/TSAN Release",
-            category = "week8|tsan",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/TSAN Debug",
-            category = "week8|tsan",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/UBSan Release",
-            category = "week9|ubsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/UBSan vptr Release",
-            category = "week9|ubsan|vptr",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/UBSanVptr Linux",
-            category = "week9|ubsan|vptr",
-            short_name = "lnx",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Cast Android (dbg)",
-            category = "week10|android",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Cast Audio Linux",
-            category = "week10|linux",
-            short_name = "audio",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Cast Linux",
-            category = "week10|linux",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Fuchsia ARM64",
-            category = "week11|fuchsia|arm64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-arm64-cast",
-            category = "week11|fuchsia|arm64",
-            short_name = "cast",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Fuchsia x64",
-            category = "week11|fuchsia|x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-x64-cast",
-            category = "week11|fuchsia|x64",
-            short_name = "cast",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-fyi-arm64-rel",
-            category = "week11|fuchsia|fyi",
-            short_name = "arm64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-fyi-x64-dbg",
-            category = "week11|fuchsia|fyi",
-            short_name = "x64 dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-fyi-x64-rel",
-            category = "week11|fuchsia|fyi",
-            short_name = "x64 rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-marshmallow-arm64-rel",
-            category = "week13|android",
-            short_name = "marsh",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android Release (Nexus 5X)",
-            category = "week13|android",
-            short_name = "n5x",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux ASan LSan Builder",
-            category = "week14a|linux",
-            short_name = "asanlsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux TSan Builder",
-            category = "week14a|linux",
-            short_name = "tsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android ASAN (dbg)",
-            category = "week14b|android",
-            short_name = "asanlsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android arm Builder (dbg)",
-            category = "week14b|android",
-            short_name = "tsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android arm64 Builder (dbg)",
-            category = "week14b|android",
-            short_name = "tsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/fuchsia-x64-dbg",
-            category = "week15a|fuchsia",
-            short_name = "x64dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-archive-dbg",
-            category = "week15a|linux|archive",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-archive-rel",
-            category = "week15a|linux|archive",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-bfcache-rel",
-            category = "week15a|linux",
-            short_name = "bfc",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-fieldtrial-rel",
-            category = "week15a|linux",
-            short_name = "field",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-archive-rel",
-            category = "week15b|android|archive",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-archive-dbg",
-            category = "week15b|android|archive",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-asan",
-            category = "week15b|android",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-bfcache-rel",
-            category = "week15b|android",
-            short_name = "bfc",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-code-coverage",
-            category = "week15b|android",
-            short_name = "code",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-pie-arm64-rel",
-            category = "week15b|android|pie",
-            short_name = "arm64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-pie-x86-rel",
-            category = "week15b|android|pie",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Perf Android 64 Builder",
-            category = "week15b|android|gpu",
-            short_name = "perf64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-archive-rel",
-            category = "week15b|mac|archive",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/mac-archive-dbg",
-            category = "week15b|mac|archive",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-marshmallow-x86-rel-non-cq",
-            category = "week16b",
-            short_name = "marsh",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Android FYI 64 Perf (Pixel 2)",
-            category = "week16b",
-            short_name = "pxl2",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-tot-angle-x64",
-            category = "week17|swangle|angle",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-tot-angle-x86",
-            category = "week17|swangle|angle",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-tot-swiftshader-x64",
-            category = "week17|swangle|swift",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-tot-swiftshader-x86",
-            category = "week17|swangle|swift",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-x64",
-            category = "week17|swangle",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-swangle-x86",
-            category = "week17|swangle",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/android-code-coverage-native",
-            category = "misc|android",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win ASan Release",
-            category = "win|week1|asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win ASan Release Media",
-            category = "win|week1|asan",
-            short_name = "media",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win10-code-coverage",
-            category = "win|week1.1",
-            short_name = "code",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win x64 Builder (dbg)",
-            category = "win|week1.5",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-archive-dbg",
-            category = "win|week1.5|archive",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-archive-rel",
-            category = "win|week1.5|archive",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Win10 x64 Builder",
-            category = "win|week2|dawn",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Win10 x64 DEPS Builder",
-            category = "win|week2|dawn",
-            short_name = "deps",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Win x64 Builder (dbg)",
-            category = "win|week2|gpu",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI XR Win x64 Builder",
-            category = "win|week2|gpu|fyi",
-            short_name = "xr",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win x64 dEQP Builder",
-            category = "win|week2|gpu|fyi",
-            short_name = "deqp",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win x64 DX12 Vulkan Builder",
-            category = "win|week2|gpu|fyi|dx12",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg)",
-            category = "win|week2|gpu|fyi|dx12",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win x64 Builder",
-            category = "win|week2|gpu|fyi",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win x64 Builder (dbg)",
-            category = "win|week2|gpu|fyi",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win Builder",
-            category = "win|week3",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win Builder (dbg)",
-            category = "win|week3",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win Builder",
-            category = "win|week3|gpu|fyi",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win Builder (dbg)",
-            category = "win|week3|gpu|fyi",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU FYI Win dEQP Builder",
-            category = "win|week3|gpu|fyi",
-            short_name = "deqp",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Win10 x86 Builder",
-            category = "win|week3|dawn",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Dawn Win10 x86 DEPS Builder",
-            category = "win|week3|dawn",
-            short_name = "deps",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win32-archive-rel",
-            category = "win|week4|win32",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win32-archive-dbg",
-            category = "win|week4|win32",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win32-arm64-rel",
-            category = "win|week4|win32",
-            short_name = "arm",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Windows ASan",
-            category = "win|week4",
-            short_name = "libfuzzer",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Windows deterministic",
-            category = "win|week4",
-            short_name = "det",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mojo Windows",
-            category = "win|week4",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-celab-builder-rel",
-            category = "win|week4",
-            short_name = "celab",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-asan",
-            category = "win|week4",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win 10 Fast Ring",
-            category = "win|week4",
-            short_name = "fastring",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-annotator-rel",
-            category = "win|week4",
-            short_name = "anno",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-pixel-builder-rel",
-            category = "win|week4",
-            short_name = "pixel",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc/WebRTC Chromium Win Builder",
-            category = "win|week4|webrtc",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Win Builder",
-            category = "win|week4|webrtc|fyi",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Win Builder (dbg)",
-            category = "win|week4|webrtc|fyi",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-x86",
-            category = "win|week4|swangle",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-x64",
-            category = "win|week4|swangle",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-tot-angle-x86",
-            category = "win|week4|swangle|angle",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-tot-angle-x64",
-            category = "win|week4|swangle|angle",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-tot-swiftshader-x86",
-            category = "win|week4|swangle|swift",
-            short_name = "x86",
-        ),
-        luci.console_view_entry(
-            builder = "ci/win-swangle-tot-swiftshader-x64",
-            category = "win|week4|swangle|swift",
-            short_name = "x64",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Win x64 Builder",
-            category = "win|week5",
-        ),
-        luci.console_view_entry(
-            builder = "ci/GPU Win x64 Builder",
-            category = "win|week5",
-            short_name = "gpu",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-chromeos-dbg",
-            category = "cros|week1",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-chromeos-rel",
-            category = "cros|week1",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/linux-chromeos-code-coverage",
-            category = "cros|week1",
-            short_name = "code",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux ChromiumOS Full",
-            category = "cros|week2",
-            short_name = "full",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ChromiumOS ASAN Release",
-            category = "cros|week2",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux Chromium OS ASan LSan Builder",
-            category = "cros|week2",
-            short_name = "asan lsan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Linux ChromiumOS MSan Builder",
-            category = "cros|week2",
-            short_name = "msan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Mojo ChromiumOS",
-            category = "cros|week2",
-            short_name = "mojo",
-        ),
-        luci.console_view_entry(
-            builder = "ci/Libfuzzer Upload Chrome OS ASan",
-            category = "cros|week2",
-            short_name = "fuzz",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-amd64-generic-rel",
-            category = "cros|week3|amd64",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-amd64-generic-dbg",
-            category = "cros|week3|amd64",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-amd64-generic-asan-rel",
-            category = "cros|week3|amd64",
-            short_name = "asan",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-amd64-generic-cfi-thin-lto-rel",
-            category = "cros|week3|amd64",
-            short_name = "thinlto",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-arm-generic-rel",
-            category = "cros|week3|arm",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-arm-generic-dbg",
-            category = "cros|week3|arm",
-            short_name = "dbg",
-        ),
-        luci.console_view_entry(
-            builder = "ci/chromeos-kevin-rel",
-            category = "cros|week3|kevin",
-            short_name = "rel",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI ios-device",
-            category = "ios|week1a",
-            short_name = "dev",
-        ),
-        luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI ios-simulator",
-            category = "ios|week1a",
-            short_name = "sim",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-device",
-            category = "ios|week1b",
-            short_name = "dev",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator",
-            category = "ios|week1b|sim",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator-full-configs",
-            category = "ios|week1b|sim",
-            short_name = "fullconf",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator-noncq",
-            category = "ios|week1b|sim",
-            short_name = "noncq",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios13-beta-simulator",
-            category = "ios|week1b|ios13|beta",
-            short_name = "sim",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios13-sdk-simulator",
-            category = "ios|week1b|ios13|sdk",
-            short_name = "sim",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator-cronet",
-            category = "ios|week1c",
-            short_name = "cro",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator-cr-recipe",
-            category = "ios|week1c",
-            short_name = "crr",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-webkit-tot",
-            category = "ios|week1c",
-            short_name = "webkit",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios13-sdk-device",
-            category = "ios|week1c|ios13",
-            short_name = "dev",
-        ),
-        luci.console_view_entry(
-            builder = "ci/ios-simulator-code-coverage",
-            category = "ios|week1d",
-            short_name = "code",
-        ),
-    ],
-)
diff --git a/infra/config/subprojects/goma/subproject.star b/infra/config/subprojects/goma/subproject.star
index 0ff40f4..1a05416 100644
--- a/infra/config/subprojects/goma/subproject.star
+++ b/infra/config/subprojects/goma/subproject.star
@@ -5,6 +5,5 @@
 exec("./goma.star")
 exec("./consoles/chromium.goma.star")
 exec("./consoles/chromium.goma.fyi.star")
-exec("./consoles/chromium.goma.migration.star")
 exec("./consoles/goma.latest.star")
 exec("./consoles/luci.chromium.goma.star")
diff --git a/ios/build/bots/scripts/shard_util.py b/ios/build/bots/scripts/shard_util.py
index 37b9494a..961fef3 100644
--- a/ios/build/bots/scripts/shard_util.py
+++ b/ios/build/bots/scripts/shard_util.py
@@ -14,14 +14,18 @@
 # WARNING: THESE DUPLICATE CONSTANTS IN:
 # //build/scripts/slave/recipe_modules/ios/api.py
 
+# Regex to parse all compiled EG tests, including disabled (prepended with
+# DISABLED_ or FLAKY_).
 TEST_NAMES_DEBUG_APP_PATTERN = re.compile(
     'imp +(?:0[xX][0-9a-fA-F]+ )?-\[(?P<testSuite>[A-Za-z_][A-Za-z0-9_]'
-    '*Test[Case]*) (?P<testMethod>test[A-Za-z0-9_]*)\]')
+    '*Test[Case]*) (?P<testMethod>(?:DISABLED_|FLAKY_)?test[A-Za-z0-9_]*)\]')
 TEST_CLASS_RELEASE_APP_PATTERN = re.compile(
     r'name +0[xX]\w+ '
     '(?P<testSuite>[A-Za-z_][A-Za-z0-9_]*Test(?:Case|))\n')
+# Regex to parse all compiled EG tests, including disabled (prepended with
+# DISABLED_ or FLAKY_).
 TEST_NAME_RELEASE_APP_PATTERN = re.compile(
-    r'name +0[xX]\w+ (?P<testCase>test[A-Za-z0-9_]+)\n')
+    r'name +0[xX]\w+ (?P<testCase>(?:DISABLED_|FLAKY_)?test[A-Za-z0-9_]+)\n')
 # 'ChromeTestCase' and 'BaseEarlGreyTestCase' are parent classes
 # of all EarlGrey/EarlGrey2 test classes. They have no real tests.
 IGNORED_CLASSES = ['BaseEarlGreyTestCase', 'ChromeTestCase']
@@ -81,7 +85,7 @@
 
 def fetch_test_names_for_release(stdout):
   """Parse otool output to get all testMethods in all TestCases in the
-     format of (TestCase, testMethod), in release app.
+     format of (TestCase, testMethod) including disabled tests, in release app.
 
      WARNING: This logic is similar to what's found in
       //build/scripts/slave/recipe_modules/ios/api.py
@@ -90,7 +94,7 @@
         stdout: (string) response of 'otool -ov'
 
     Returns:
-        (list) a list of (TestCase, testMethod)
+        (list) a list of (TestCase, testMethod), containing disabled tests.
     """
   # For Release builds `otool -ov` command generates output that is
   # different from Debug builds.
@@ -114,29 +118,30 @@
 
 def fetch_test_names_for_debug(stdout):
   """Parse otool output to get all testMethods in all TestCases in the
-     format of (TestCase, testMethod), in debug app.
+     format of (TestCase, testMethod) including disabled tests, in debug app.
 
     Args:
         stdout: (string) response of 'otool -ov'
 
     Returns:
-        (list) a list of (TestCase, testMethod)
+        (list) a list of (TestCase, testMethod), containing disabled tests.
     """
   test_names = TEST_NAMES_DEBUG_APP_PATTERN.findall(stdout)
   return filter(lambda (test_case, _): test_case not in IGNORED_CLASSES,
                 test_names)
 
 
-def fetch_test_names(app, host_app, release=False):
+def fetch_test_names(app, host_app, release, enabled_tests_only=True):
   """Determine the list of (TestCase, testMethod) for the app.
 
     Args:
         app: (string) path to app
         host_app: (string) path to host app. None or "NO_PATH" for EG1.
         release: (bool) whether this is a release build.
+        enabled_tests_only: (bool) output only enabled tests.
 
     Returns:
-        (list) a list of (TestCase, testMethod)
+        (list) a list of (TestCase, testMethod).
     """
   # Determine what path to use
   app_path = determine_app_path(app, host_app, release)
@@ -147,9 +152,13 @@
   LOGGER.info("Ignored test classes: {}".format(IGNORED_CLASSES))
   if release:
     LOGGER.info("Release build detected. Fetching test names for release.")
-    return fetch_test_names_for_release(stdout)
-
-  return fetch_test_names_for_debug(stdout)
+  all_test_names = (
+      fetch_test_names_for_release(stdout)
+      if release else fetch_test_names_for_debug(stdout))
+  enabled_test_names = (
+      filter(lambda (_, test_method): test_method.startswith('test'),
+             all_test_names))
+  return enabled_test_names if enabled_tests_only else all_test_names
 
 
 def balance_into_sublists(test_counts, total_shards):
diff --git a/ios/build/bots/scripts/shard_util_test.py b/ios/build/bots/scripts/shard_util_test.py
index 1f182f9..597f13d 100644
--- a/ios/build/bots/scripts/shard_util_test.py
+++ b/ios/build/bots/scripts/shard_util_test.py
@@ -23,7 +23,9 @@
     'name 0x1064b8438 PasswordsTestCase',
     'imp 0x1075e6887 -[PasswordsTestCase testG]',
     'name 0x1064b8438 ToolBarTestCase',
-    'imp 0x1075e6887 -[ToolBarTestCase testH]', 'version 0'
+    'imp 0x1075e6887 -[ToolBarTestCase testH]',
+    'imp 0x1075e6887 -[ToolBarTestCase DISABLED_testI]',
+    'imp 0x1075e6887 -[ToolBarTestCase FLAKY_testJ]', 'version 0'
 ])
 
 # Debug app otool output format in Xcode 11.4 toolchain.
@@ -43,7 +45,9 @@
     '    name    0x1064b8438 PasswordsTestCase',
     '    imp     0x1075e6887 -[PasswordsTestCase testG]',
     '    name    0x1064b8438 ToolBarTestCase',
-    '    imp     0x1075e6887 -[ToolBarTestCase testH]', 'version 0'
+    '    imp     0x1075e6887 -[ToolBarTestCase testH]',
+    '    imp     0x1075e6887 -[ToolBarTestCase DISABLED_testI]',
+    '    imp     0x1075e6887 -[ToolBarTestCase FLAKY_testJ]', 'version 0'
 ])
 
 RELEASE_APP_OTOOL_OUTPUT = '\n'.join([
@@ -58,8 +62,9 @@
     'name 0x1075e6887 testF', 'baseProtocols 0x0',
     'name 0x1064b8438 ChromeTestCase', 'name 0x1064b8438 setUp',
     'baseProtocols 0x0', 'name 0x1064b8438 ToolBarTestCase',
-    'name 0x1075e6887 testG', 'name 0x1075e6887 testH', 'baseProtocols 0x0',
-    'version 0'
+    'name 0x1075e6887 testG', 'name 0x1075e6887 testH',
+    'name 0x1075e6887 DISABLED_testI', 'name 0x1075e6887 FLAKY_testJ',
+    'baseProtocols 0x0', 'version 0'
 ])
 
 # Release app otool output format in Xcode 11.4 toolchain.
@@ -76,7 +81,8 @@
     '    name    0x1064b8438 ChromeTestCase', '    name    0x1064b8438 setUp',
     'baseProtocols 0x0', '    name    0x1064b8438 ToolBarTestCase',
     '    name    0x1075e6887 testG', '    name    0x1075e6887 testH',
-    'baseProtocols 0x0', 'version 0'
+    '    name    0x1075e6893 DISABLED_testI',
+    '    name    0x1075e723f FLAKY_testJ', 'baseProtocols 0x0', 'version 0'
 ])
 
 
@@ -117,92 +123,110 @@
   def test_fetch_test_names_debug(self):
     """Ensures that the debug output is formatted correctly"""
     resp = shard_util.fetch_test_names_for_debug(DEBUG_APP_OTOOL_OUTPUT)
-    self.assertEqual(len(resp), 8)
-    expected_test_names = [('CacheTestCase', 'testA'), ('CacheTestCase',
-                                                        'testB'),
-                           ('CacheTestCase', 'testc'), ('TabUITestCase',
-                                                        'testD'),
-                           ('TabUITestCase', 'testE'),
-                           ('KeyboardTestCase', 'testF'),
-                           ('PasswordsTestCase', 'testG'),
-                           ('ToolBarTestCase', 'testH')]
+    self.assertEqual(len(resp), 10)
+    expected_test_names = [
+        ('CacheTestCase', 'testA'),
+        ('CacheTestCase', 'testB'),
+        ('CacheTestCase', 'testc'),
+        ('TabUITestCase', 'testD'),
+        ('TabUITestCase', 'testE'),
+        ('KeyboardTestCase', 'testF'),
+        ('PasswordsTestCase', 'testG'),
+        ('ToolBarTestCase', 'testH'),
+        ('ToolBarTestCase', 'DISABLED_testI'),
+        ('ToolBarTestCase', 'FLAKY_testJ'),
+    ]
     for test_name in expected_test_names:
       self.assertTrue(test_name in resp)
 
     test_cases = map(lambda (test_case, test_method): test_case, resp)
 
     # ({'CacheTestCase': 3, 'TabUITestCase': 2, 'PasswordsTestCase': 1,
-    # 'KeyboardTestCase': 1, 'ToolBarTestCase': 1})
+    # 'KeyboardTestCase': 1, 'ToolBarTestCase': 3})
     counts = collections.Counter(test_cases).most_common()
     name, _ = counts[0]
-    self.assertEqual(name, 'CacheTestCase')
+    self.assertEqual(name, 'ToolBarTestCase')
 
   def test_fetch_test_counts_release(self):
     """Ensures that the release output is formatted correctly"""
     resp = shard_util.fetch_test_names_for_release(RELEASE_APP_OTOOL_OUTPUT)
-    self.assertEqual(len(resp), 8)
+    self.assertEqual(len(resp), 10)
 
-    expected_test_names = [('CacheTestCase', 'testA'), ('CacheTestCase',
-                                                        'testB'),
-                           ('CacheTestCase', 'testc'), ('KeyboardTest',
-                                                        'testD'),
-                           ('KeyboardTest', 'testE'), ('KeyboardTest', 'testF'),
-                           ('ToolBarTestCase', 'testG'),
-                           ('ToolBarTestCase', 'testH')]
+    expected_test_names = [
+        ('CacheTestCase', 'testA'),
+        ('CacheTestCase', 'testB'),
+        ('CacheTestCase', 'testc'),
+        ('KeyboardTest', 'testD'),
+        ('KeyboardTest', 'testE'),
+        ('KeyboardTest', 'testF'),
+        ('ToolBarTestCase', 'testG'),
+        ('ToolBarTestCase', 'testH'),
+        ('ToolBarTestCase', 'DISABLED_testI'),
+        ('ToolBarTestCase', 'FLAKY_testJ'),
+    ]
     for test_name in expected_test_names:
       self.assertTrue(test_name in resp)
 
     test_cases = map(lambda (test_case, test_method): test_case, resp)
     # ({'KeyboardTest': 3, 'CacheTestCase': 3,
-    # 'ToolBarTestCase': 2})
+    # 'ToolBarTestCase': 4})
     counts = collections.Counter(test_cases).most_common()
     name, _ = counts[0]
-    self.assertEqual(name, 'KeyboardTest')
+    self.assertEqual(name, 'ToolBarTestCase')
 
   def test_fetch_test_names_debug_114(self):
     """Test the debug output from otool in Xcode 11.4"""
     resp = shard_util.fetch_test_names_for_debug(DEBUG_APP_OTOOL_OUTPUT_114)
-    self.assertEqual(len(resp), 8)
-    expected_test_names = [('CacheTestCase', 'testA'), ('CacheTestCase',
-                                                        'testB'),
-                           ('CacheTestCase', 'testc'), ('TabUITestCase',
-                                                        'testD'),
-                           ('TabUITestCase', 'testE'),
-                           ('KeyboardTestCase', 'testF'),
-                           ('PasswordsTestCase', 'testG'),
-                           ('ToolBarTestCase', 'testH')]
+    self.assertEqual(len(resp), 10)
+    expected_test_names = [
+        ('CacheTestCase', 'testA'),
+        ('CacheTestCase', 'testB'),
+        ('CacheTestCase', 'testc'),
+        ('TabUITestCase', 'testD'),
+        ('TabUITestCase', 'testE'),
+        ('KeyboardTestCase', 'testF'),
+        ('PasswordsTestCase', 'testG'),
+        ('ToolBarTestCase', 'testH'),
+        ('ToolBarTestCase', 'DISABLED_testI'),
+        ('ToolBarTestCase', 'FLAKY_testJ'),
+    ]
     for test_name in expected_test_names:
       self.assertTrue(test_name in resp)
 
     test_cases = map(lambda (test_case, test_method): test_case, resp)
 
     # ({'CacheTestCase': 3, 'TabUITestCase': 2, 'PasswordsTestCase': 1,
-    # 'KeyboardTestCase': 1, 'ToolBarTestCase': 1})
+    # 'KeyboardTestCase': 1, 'ToolBarTestCase': 3})
     counts = collections.Counter(test_cases).most_common()
     name, _ = counts[0]
-    self.assertEqual(name, 'CacheTestCase')
+    self.assertEqual(name, 'ToolBarTestCase')
 
   def test_fetch_test_counts_release_114(self):
     """Test the release output from otool in Xcode 11.4"""
     resp = shard_util.fetch_test_names_for_release(RELEASE_APP_OTOOL_OUTPUT_114)
-    self.assertEqual(len(resp), 8)
+    self.assertEqual(len(resp), 10)
 
-    expected_test_names = [('CacheTestCase', 'testA'), ('CacheTestCase',
-                                                        'testB'),
-                           ('CacheTestCase', 'testc'), ('KeyboardTest',
-                                                        'testD'),
-                           ('KeyboardTest', 'testE'), ('KeyboardTest', 'testF'),
-                           ('ToolBarTestCase', 'testG'),
-                           ('ToolBarTestCase', 'testH')]
+    expected_test_names = [
+        ('CacheTestCase', 'testA'),
+        ('CacheTestCase', 'testB'),
+        ('CacheTestCase', 'testc'),
+        ('KeyboardTest', 'testD'),
+        ('KeyboardTest', 'testE'),
+        ('KeyboardTest', 'testF'),
+        ('ToolBarTestCase', 'testG'),
+        ('ToolBarTestCase', 'testH'),
+        ('ToolBarTestCase', 'DISABLED_testI'),
+        ('ToolBarTestCase', 'FLAKY_testJ'),
+    ]
     for test_name in expected_test_names:
       self.assertTrue(test_name in resp)
 
     test_cases = map(lambda (test_case, test_method): test_case, resp)
     # ({'KeyboardTest': 3, 'CacheTestCase': 3,
-    # 'ToolBarTestCase': 2})
+    # 'ToolBarTestCase': 4})
     counts = collections.Counter(test_cases).most_common()
     name, _ = counts[0]
-    self.assertEqual(name, 'KeyboardTest')
+    self.assertEqual(name, 'ToolBarTestCase')
 
   def test_balance_into_sublists_debug(self):
     """Ensure the balancing algorithm works"""
@@ -217,11 +241,15 @@
     sublists_3 = shard_util.balance_into_sublists(test_counts, 3)
     self.assertEqual(len(sublists_3), 3)
     # CacheTestCase has 3,
-    # TabUITestCase has 2, ToolBarTestCase has 1
+    # TabUITestCase has 2, ToolBarTestCase has 4
     # PasswordsTestCase has 1, KeyboardTestCase has 1
-    self.assertEqual(len(sublists_3[0]), 1)
-    self.assertEqual(len(sublists_3[1]), 2)
-    self.assertEqual(len(sublists_3[2]), 2)
+    # They will be balanced into:
+    # [[ToolBarTestCase], [CacheTestCase, PasswordsTestCase],
+    # [TabUITestCase, KeyboardTestCase]]
+    self.assertEqual(
+        sorted([len(sublists_3[0]),
+                len(sublists_3[1]),
+                len(sublists_3[2])]), [1, 2, 2])
 
   def test_balance_into_sublists_release(self):
     """Ensure the balancing algorithm works"""
@@ -233,7 +261,8 @@
     self.assertEqual(len(sublists_3), 3)
     # KeyboardTest has 3
     # CacheTestCase has 3
-    # ToolbarTest Case has 2
+    # ToolbarTest Case has 4
+    # They will be balanced as one in each shard.
     self.assertEqual(len(sublists_3[0]), 1)
     self.assertEqual(len(sublists_3[1]), 1)
     self.assertEqual(len(sublists_3[2]), 1)
diff --git a/ios/build/bots/scripts/test_apps.py b/ios/build/bots/scripts/test_apps.py
index 6b01824e..fa225c05 100644
--- a/ios/build/bots/scripts/test_apps.py
+++ b/ios/build/bots/scripts/test_apps.py
@@ -12,6 +12,9 @@
 import test_runner
 
 
+OUTPUT_DISABLED_TESTS_TEST_ARG = '--write-compiled-tests-json-to-writable-path'
+
+
 #TODO(crbug.com/1046911): Remove usage of KIF filters.
 def get_kif_test_filter(tests, invert=False):
   """Returns the KIF test filter to filter the given test cases.
@@ -111,6 +114,7 @@
       self.env_vars[env_var[0]] = None if len(env_var) == 1 else env_var[1]
     self.included_tests = included_tests or []
     self.excluded_tests = excluded_tests or []
+    self.disabled_tests = []
     self.module_name = os.path.splitext(os.path.basename(test_app))[0]
     self.release = release
     self.host_app_path = host_app_path
@@ -238,16 +242,27 @@
     # but they are not test-methods.
     # TODO(crbug.com/982435): Rename not test methods with test-suffix.
     none_tests = ['ChromeTestCase/testServer', 'FindInPageTestCase/testURL']
+    # TODO(crbug.com/1123681): Move all_tests to class var. Set all_tests,
+    # disabled_tests values in initialization to avoid multiple calls to otool.
     all_tests = []
+    # Only store the tests when there is the test arg.
+    store_disabled_tests = OUTPUT_DISABLED_TESTS_TEST_ARG in self.test_args
+    self.disabled_tests = []
     for test_class, test_method in shard_util.fetch_test_names(
-        self.test_app_path, self.host_app_path, self.release):
+        self.test_app_path,
+        self.host_app_path,
+        self.release,
+        enabled_tests_only=False):
       test_name = '%s/%s' % (test_class, test_method)
       if (test_name not in none_tests and
           # inlcuded_tests contains the tests to execute, which may be a subset
           # of all tests b/c of the iOS test sharding logic in run.py. Filter by
           # self.included_tests if specified
           (test_class in self.included_tests if self.included_tests else True)):
-        all_tests.append(test_name)
+        if test_method.startswith('test'):
+          all_tests.append(test_name)
+        elif store_disabled_tests:
+          self.disabled_tests.append(test_name)
     return all_tests
 
 
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index a827ce2..9983a3d 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -403,6 +403,12 @@
       if shard_attempts[-1]['failed']:
         self.logs['failed tests'].extend(shard_attempts[-1]['failed'].keys())
 
+    # Gets disabled tests from test app object if any.
+    self.logs['disabled tests'] = []
+    for launch_command in launch_commands:
+      self.logs['disabled tests'].extend(
+          launch_command.egtests_app.disabled_tests)
+
     # Gets all failures/flakes and lists them in bot summary
     all_failures = set()
     for shard_attempts in attempts_results:
@@ -459,6 +465,8 @@
         for test in attempt_results['passed']:
           output.mark_passed(test)
 
+    output.mark_all_skipped(self.logs['disabled tests'])
+
     self.test_results['tests'] = output.tests
 
     # Test is failed if there are failures for the last run.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index f99fc03..baad67e 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -486,6 +486,7 @@
     needRestoration = [CrashRestoreHelper
         moveAsideSessionInformationForBrowserState:chromeBrowserState];
   }
+  [[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs];
 
   // Initialize and set the main browser state.
   [self initializeBrowserState:chromeBrowserState];
diff --git a/ios/chrome/browser/discover_feed/BUILD.gn b/ios/chrome/browser/discover_feed/BUILD.gn
index 11663b08..89fd0117 100644
--- a/ios/chrome/browser/discover_feed/BUILD.gn
+++ b/ios/chrome/browser/discover_feed/BUILD.gn
@@ -18,6 +18,7 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/ui/content_suggestions:metrics",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/discover_feed",
   ]
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.h b/ios/chrome/browser/discover_feed/discover_feed_service.h
index 1a94d3c..5d66a1b 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.h
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.h
@@ -9,6 +9,7 @@
 #include "components/signin/public/identity_manager/identity_manager.h"
 
 class ChromeBrowserState;
+@class DiscoverFeedMetricsRecorder;
 class DiscoverFeedProvider;
 
 // A browser-context keyed service that is used to keep the Discover Feed data
@@ -20,6 +21,10 @@
   DiscoverFeedService(ChromeBrowserState* browser_state);
   ~DiscoverFeedService() override;
 
+  // Returns the FeedMetricsRecorder to be used by the Feed, a single instance
+  // of DiscoverFeedMetricsRecorder needs to be used per BrowserState.
+  DiscoverFeedMetricsRecorder* GetDiscoverFeedMetricsRecorder();
+
   // KeyedService:
   void Shutdown() override;
 
@@ -36,6 +41,9 @@
   // Discover Feed provider to notify of changes.
   DiscoverFeedProvider* discover_feed_provider_;
 
+  // Metrics recorder for the DiscoverFeed.
+  DiscoverFeedMetricsRecorder* discover_feed_metrics_recorder_;
+
   DISALLOW_COPY_AND_ASSIGN(DiscoverFeedService);
 };
 
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.mm b/ios/chrome/browser/discover_feed/discover_feed_service.mm
index f731c50..035964a 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.mm
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.mm
@@ -7,6 +7,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
+#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h"
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
@@ -23,14 +24,22 @@
     identity_manager_->AddObserver(this);
   }
 
+  discover_feed_metrics_recorder_ = [[DiscoverFeedMetricsRecorder alloc] init];
+
   DiscoverFeedConfiguration* discover_config =
       [[DiscoverFeedConfiguration alloc] init];
   discover_config.browserState = browser_state;
+  discover_config.metricsRecorder = discover_feed_metrics_recorder_;
   discover_feed_provider_->StartFeed(discover_config);
 }
 
 DiscoverFeedService::~DiscoverFeedService() {}
 
+DiscoverFeedMetricsRecorder*
+DiscoverFeedService::GetDiscoverFeedMetricsRecorder() {
+  return discover_feed_metrics_recorder_;
+}
+
 void DiscoverFeedService::Shutdown() {
   if (identity_manager_) {
     identity_manager_->RemoveObserver(this);
diff --git a/ios/chrome/browser/metrics/previous_session_info.h b/ios/chrome/browser/metrics/previous_session_info.h
index 073e6ea..cadb03b 100644
--- a/ios/chrome/browser/metrics/previous_session_info.h
+++ b/ios/chrome/browser/metrics/previous_session_info.h
@@ -17,6 +17,9 @@
 // Key in the UserDefaults for a boolean describing whether or not the session
 // restoration is in progress.
 extern NSString* const kPreviousSessionInfoRestoringSession;
+// Key in the UserDefaults for an array which contains the ids for the connected
+// scene sessions on the previous run.
+extern NSString* const kPreviousSessionInfoConnectedSceneSessionIDs;
 
 // The values of this enum are persisted (both to NSUserDefaults and logs) and
 // represent the state of the last session (which may have been running a
@@ -143,6 +146,15 @@
 // ignored.
 - (void)resetMemoryWarningFlag;
 
+// Adds |sessionID| to the list of connected sessions.
+- (void)addSceneSessionID:(NSString*)sessionID;
+
+// Removes |sessionID| from the list of connected sessions.
+- (void)removeSceneSessionID:(NSString*)sessionID;
+
+// Empties the list of connected session.
+- (void)resetConnectedSceneSessionIDs;
+
 // Must be called when Chrome starts session restoration. The returned closure
 // runner will clear up the flag when destroyed. Can be used on different
 // threads.
diff --git a/ios/chrome/browser/metrics/previous_session_info.mm b/ios/chrome/browser/metrics/previous_session_info.mm
index 76e5dcd7..18819a77 100644
--- a/ios/chrome/browser/metrics/previous_session_info.mm
+++ b/ios/chrome/browser/metrics/previous_session_info.mm
@@ -94,6 +94,7 @@
 //   version of the application.
 NSString* const kPreviousSessionInfoMultiWindowEnabled =
     @"PreviousSessionInfoMultiWindowEnabled";
+
 }  // namespace
 
 namespace previous_session_info_constants {
@@ -102,6 +103,8 @@
 NSString* const kOSStartTime = @"OSStartTime";
 NSString* const kPreviousSessionInfoRestoringSession =
     @"PreviousSessionInfoRestoringSession";
+NSString* const kPreviousSessionInfoConnectedSceneSessionIDs =
+    @"PreviousSessionInfoConnectedSceneSessionIDs";
 }  // namespace previous_session_info_constants
 
 @interface PreviousSessionInfo ()
@@ -113,6 +116,10 @@
 // Can be greater than one if multiple sessions are being restored in parallel.
 @property(atomic, assign) int numberOfSessionsBeingRestored;
 
+// The list of the session IDs for all the connected scenes, used for crash
+// restoration.
+@property(nonatomic, strong) NSMutableSet<NSString*>* connectedSceneSessionsIDs;
+
 // Redefined to be read-write.
 @property(nonatomic, assign) NSInteger availableDeviceStorage;
 @property(nonatomic, assign) float deviceBatteryLevel;
@@ -185,6 +192,12 @@
     gSharedInstance.isMultiWindowEnabledSession =
         [defaults boolForKey:kPreviousSessionInfoMultiWindowEnabled];
 
+    gSharedInstance.connectedSceneSessionsIDs = [NSMutableSet
+        setWithArray:[defaults
+                         stringArrayForKey:
+                             previous_session_info_constants::
+                                 kPreviousSessionInfoConnectedSceneSessionIDs]];
+
     NSTimeInterval lastSystemStartTime =
         [defaults doubleForKey:previous_session_info_constants::kOSStartTime];
 
@@ -403,6 +416,29 @@
   [defaults synchronize];
 }
 
+- (void)synchronizeSceneSessionIDs {
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setObject:[self.connectedSceneSessionsIDs allObjects]
+               forKey:previous_session_info_constants::
+                          kPreviousSessionInfoConnectedSceneSessionIDs];
+  [defaults synchronize];
+}
+
+- (void)addSceneSessionID:(NSString*)sessionID {
+  [self.connectedSceneSessionsIDs addObject:sessionID];
+  [self synchronizeSceneSessionIDs];
+}
+
+- (void)removeSceneSessionID:(NSString*)sessionID {
+  [self.connectedSceneSessionsIDs removeObject:sessionID];
+  [self synchronizeSceneSessionIDs];
+}
+
+- (void)resetConnectedSceneSessionIDs {
+  self.connectedSceneSessionsIDs = [[NSMutableSet alloc] init];
+  [self synchronizeSceneSessionIDs];
+}
+
 - (base::ScopedClosureRunner)startSessionRestoration {
   if (self.numberOfSessionsBeingRestored == 0) {
     [NSUserDefaults.standardUserDefaults
diff --git a/ios/chrome/browser/metrics/previous_session_info_private.h b/ios/chrome/browser/metrics/previous_session_info_private.h
index fb7f5a1..8ac12ba 100644
--- a/ios/chrome/browser/metrics/previous_session_info_private.h
+++ b/ios/chrome/browser/metrics/previous_session_info_private.h
@@ -14,6 +14,7 @@
 @property(nonatomic, assign)
     previous_session_info_constants::DeviceBatteryState deviceBatteryState;
 @property(nonatomic, assign) BOOL OSRestartedAfterPreviousSession;
+@property(nonatomic, strong) NSMutableSet<NSString*>* connectedSceneSessionsIDs;
 
 + (void)resetSharedInstanceForTesting;
 
diff --git a/ios/chrome/browser/metrics/previous_session_info_unittest.mm b/ios/chrome/browser/metrics/previous_session_info_unittest.mm
index 35758c0..e240041 100644
--- a/ios/chrome/browser/metrics/previous_session_info_unittest.mm
+++ b/ios/chrome/browser/metrics/previous_session_info_unittest.mm
@@ -16,6 +16,8 @@
 #endif
 
 using previous_session_info_constants::kPreviousSessionInfoRestoringSession;
+using previous_session_info_constants::
+    kPreviousSessionInfoConnectedSceneSessionIDs;
 
 namespace {
 
@@ -31,6 +33,11 @@
 // last session.
 NSString* const kLastRanLanguage = @"LastRanLanguage";
 
+// IDs to be used for testing scene sessions.
+NSString* const kTestSession1ID = @"test_session_1";
+NSString* const kTestSession2ID = @"test_session_2";
+NSString* const kTestSession3ID = @"test_session_3";
+
 using PreviousSessionInfoTest = PlatformTest;
 
 TEST_F(PreviousSessionInfoTest, InitializationWithEmptyDefaults) {
@@ -326,6 +333,52 @@
       terminatedDuringSessionRestoration]);
 }
 
+// Tests that AddSceneSessionID adds to User Defaults.
+TEST_F(PreviousSessionInfoTest, AddSceneSessionID) {
+  [PreviousSessionInfo resetSharedInstanceForTesting];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession1ID];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession2ID];
+  NSArray<NSString*>* sessionIDs = [NSUserDefaults.standardUserDefaults
+      stringArrayForKey:kPreviousSessionInfoConnectedSceneSessionIDs];
+  EXPECT_TRUE([sessionIDs containsObject:kTestSession1ID]);
+  EXPECT_TRUE([sessionIDs containsObject:kTestSession2ID]);
+  EXPECT_EQ(2U, [sessionIDs count]);
+}
+
+// Tests that RemoveSceneSessionID removes id from User Defaults.
+TEST_F(PreviousSessionInfoTest, RemoveSceneSessionID) {
+  [PreviousSessionInfo resetSharedInstanceForTesting];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession1ID];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession2ID];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession3ID];
+  NSArray<NSString*>* sessionIDs = [NSUserDefaults.standardUserDefaults
+      stringArrayForKey:kPreviousSessionInfoConnectedSceneSessionIDs];
+  ASSERT_EQ(3U, [sessionIDs count]);
+  [[PreviousSessionInfo sharedInstance] removeSceneSessionID:kTestSession3ID];
+  [[PreviousSessionInfo sharedInstance] removeSceneSessionID:kTestSession1ID];
+  sessionIDs = [NSUserDefaults.standardUserDefaults
+      stringArrayForKey:kPreviousSessionInfoConnectedSceneSessionIDs];
+  EXPECT_FALSE([sessionIDs containsObject:kTestSession3ID]);
+  EXPECT_FALSE([sessionIDs containsObject:kTestSession1ID]);
+  EXPECT_EQ(1U, [sessionIDs count]);
+}
+
+// Tests that resetConnectedSceneSessionIDs remove all session ids from User
+// Defaults.
+TEST_F(PreviousSessionInfoTest, resetConnectedSceneSessionIDs) {
+  [PreviousSessionInfo resetSharedInstanceForTesting];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession1ID];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession2ID];
+  [[PreviousSessionInfo sharedInstance] addSceneSessionID:kTestSession3ID];
+  NSArray<NSString*>* sessionIDs = [NSUserDefaults.standardUserDefaults
+      stringArrayForKey:kPreviousSessionInfoConnectedSceneSessionIDs];
+  ASSERT_EQ(3U, [sessionIDs count]);
+  [[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs];
+  sessionIDs = [NSUserDefaults.standardUserDefaults
+      stringArrayForKey:kPreviousSessionInfoConnectedSceneSessionIDs];
+  EXPECT_EQ(0U, [sessionIDs count]);
+}
+
 // Tests that scoped object returned from startSessionRestoration correctly
 // resets User Defaults.
 TEST_F(PreviousSessionInfoTest, ParallelSessionRestorations) {
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 1631665c..4a4db90 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -2210,6 +2210,11 @@
   }
 
   const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+
+  // Disable the edit and move menu options if the node is not editable by user,
+  // or if editing bookmarks is not allowed.
+  BOOL canEditNode =
+      [self isEditBookmarksEnabled] && [self isNodeEditableByUser:node];
   UIContextMenuActionProvider actionProvider;
 
   // TODO (crbug.com/1093302): Add more actions for Bookmark URL and Folder.
@@ -2247,9 +2252,13 @@
 
       [menuElements addObject:[actionFactory actionToCopyURL:node->url()]];
 
-      [menuElements addObject:[actionFactory actionToEditWithBlock:^{
-                      [self editNode:node];
-                    }]];
+      UIAction* editAction = [actionFactory actionToEditWithBlock:^{
+        [self editNode:node];
+      }];
+      if (!canEditNode) {
+        editAction.attributes = UIMenuElementAttributesDisabled;
+      }
+      [menuElements addObject:editAction];
 
       [menuElements
           addObject:[actionFactory actionToShareWithBlock:^{
@@ -2279,15 +2288,22 @@
       NSMutableArray<UIMenuElement*>* menuElements =
           [[NSMutableArray alloc] init];
 
-      [menuElements addObject:[actionFactory actionToEditWithBlock:^{
-                      [self editNode:node];
-                    }]];
+      UIAction* editAction = [actionFactory actionToEditWithBlock:^{
+        [self editNode:node];
+      }];
+      UIAction* moveAction = [actionFactory actionToMoveFolderWithBlock:^{
+        std::set<const BookmarkNode*> nodes;
+        nodes.insert(node);
+        [self moveNodes:nodes];
+      }];
 
-      [menuElements addObject:[actionFactory actionToMoveFolderWithBlock:^{
-                      std::set<const BookmarkNode*> nodes;
-                      nodes.insert(node);
-                      [self moveNodes:nodes];
-                    }]];
+      if (!canEditNode) {
+        editAction.attributes = UIMenuElementAttributesDisabled;
+        moveAction.attributes = UIMenuElementAttributesDisabled;
+      }
+
+      [menuElements addObject:editAction];
+      [menuElements addObject:moveAction];
 
       return [UIMenu menuWithTitle:@"" children:menuElements];
     };
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index bf44aec..9a4e529e 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -23,8 +23,6 @@
     "content_suggestions_service_bridge_observer.h",
     "content_suggestions_service_bridge_observer.mm",
     "discover_feed_delegate.h",
-    "discover_feed_metrics_recorder.h",
-    "discover_feed_metrics_recorder.mm",
     "mediator_util.h",
     "mediator_util.mm",
     "ntp_home_mediator.h",
@@ -35,6 +33,7 @@
   ]
   deps = [
     ":feature_flags",
+    ":metrics",
     "//base",
     "//components/favicon/core",
     "//components/feature_engagement/public",
@@ -103,6 +102,7 @@
     "//ui/base",
     "//ui/strings",
   ]
+  public_deps = [ ":metrics" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
@@ -151,6 +151,7 @@
     ":content_suggestions_constant",
     ":content_suggestions_ui_util",
     ":feature_flags",
+    ":metrics",
     "resources:content_suggestions_no_image",
     "resources:content_suggestions_offline",
     "resources:ntp_search_icon",
@@ -185,6 +186,15 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
+source_set("metrics") {
+  sources = [
+    "discover_feed_metrics_recorder.h",
+    "discover_feed_metrics_recorder.mm",
+  ]
+  deps = [ "//base" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
 source_set("content_suggestions_constant") {
   sources = [
     "ntp_home_constant.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index cd2a0e5..16a54d6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -19,6 +19,7 @@
 #import "components/search_engines/template_url.h"
 #import "components/search_engines/template_url_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/discover_feed/discover_feed_service.h"
 #include "ios/chrome/browser/discover_feed/discover_feed_service_factory.h"
 #include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h"
 #import "ios/chrome/browser/drag_and_drop/url_drag_drop_handler.h"
@@ -209,8 +210,11 @@
 
   if (IsDiscoverFeedEnabled()) {
     // Creating the DiscoverFeedService will start the DiscoverFeed.
-    DiscoverFeedServiceFactory::GetForBrowserState(
-        self.browser->GetBrowserState());
+    DiscoverFeedService* discoverFeedService =
+        DiscoverFeedServiceFactory::GetForBrowserState(
+            self.browser->GetBrowserState());
+    self.discoverFeedMetricsRecorder =
+        discoverFeedService->GetDiscoverFeedMetricsRecorder();
   }
   self.discoverFeedViewController = [self discoverFeed];
 
@@ -239,7 +243,6 @@
   self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init];
   self.metricsRecorder.delegate = self.contentSuggestionsMediator;
 
-  self.discoverFeedMetricsRecorder = [[DiscoverFeedMetricsRecorder alloc] init];
 
   // Offset to maintain Discover feed scroll position.
   CGFloat offset = 0;
@@ -266,6 +269,8 @@
       self.browser->GetCommandDispatcher(), SnackbarCommands);
   self.suggestionsViewController.dispatcher = dispatcher;
   self.suggestionsViewController.discoverFeedMenuHandler = self;
+  self.suggestionsViewController.discoverFeedMetricsRecorder =
+      self.discoverFeedMetricsRecorder;
 
   self.discoverFeedHeaderDelegate =
       self.suggestionsViewController.discoverFeedHeaderDelegate;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
index f0bc509a..a238ad0 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
@@ -21,6 +21,7 @@
 @protocol ContentSuggestionsViewControllerAudience;
 @protocol DiscoverFeedHeaderChanging;
 @protocol DiscoverFeedMenuCommands;
+@class DiscoverFeedMetricsRecorder;
 @protocol OverscrollActionsControllerDelegate;
 @protocol SnackbarCommands;
 @protocol SuggestedContent;
@@ -69,6 +70,9 @@
 // Provider of menu configurations for the contentSuggestions component.
 @property(nonatomic, weak) id<ContentSuggestionsMenuProvider> menuProvider
     API_AVAILABLE(ios(13.0));
+// Discover Feed metrics recorder.
+@property(nonatomic, strong)
+    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
 
 - (void)setDataSource:(id<ContentSuggestionsDataSource>)dataSource;
 - (void)setDispatcher:(id<SnackbarCommands>)dispatcher;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 2c6e706..4808b73 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -28,6 +28,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
 #import "ios/chrome/browser/ui/content_suggestions/discover_feed_menu_commands.h"
+#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/content_suggestions/theme_change_delegate.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
@@ -90,6 +91,9 @@
 // Whether this VC is observing the discoverFeedHeight using KVO or not.
 @property(nonatomic, assign) BOOL observingDiscoverFeedHeight;
 
+// The CollectionViewController scroll position when an scrolling event starts.
+@property(nonatomic, assign) int scrollStartPosition;
+
 @end
 
 @implementation ContentSuggestionsViewController
@@ -742,6 +746,7 @@
 
 - (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
   [self.overscrollActionsController scrollViewWillBeginDragging:scrollView];
+  self.scrollStartPosition = scrollView.contentOffset.y;
 }
 
 - (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
@@ -749,6 +754,8 @@
   [super scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
   [self.overscrollActionsController scrollViewDidEndDragging:scrollView
                                               willDecelerate:decelerate];
+  [self.discoverFeedMetricsRecorder
+      recordFeedScrolled:scrollView.contentOffset.y - self.scrollStartPosition];
 }
 
 - (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
index d3529ab..7284634b 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
+++ b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
@@ -10,6 +10,9 @@
 // Records different metrics for the NTP's Discover feed.
 @interface DiscoverFeedMetricsRecorder : NSObject
 
+// Record metrics for when the user has scrolled |scrollDistance| in the Feed.
+- (void)recordFeedScrolled:(int)scrollDistance;
+
 // Record metrics for when the user has reached the bottom of their current
 // feed.
 - (void)recordInfiniteFeedTriggered;
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
index 54fea54..4b4102dc 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 
+#import "base/mac/foundation_util.h"
 #import "base/metrics/histogram_macros.h"
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
@@ -78,6 +79,19 @@
   kMaxValue = kAddedToReadLater,
 };
 
+// Values for the UMA ContentSuggestions.Feed.EngagementType
+// histogram. These values are persisted to logs. Entries should not be
+// renumbered and numeric values should never be reused. This must be kept
+// in sync with FeedEngagementType in enums.xml.
+enum class FeedEngagementType {
+  kFeedEngaged = 0,
+  kFeedEngagedSimple = 1,
+  kFeedInteracted = 2,
+  kDeprecatedFeedScrolled = 3,
+  kFeedScrolled = 4,
+  kMaxValue = kFeedScrolled,
+};
+
 namespace {
 // Histogram name for the infinite feed trigger.
 const char kDiscoverFeedInfiniteFeedTriggered[] =
@@ -116,12 +130,48 @@
 // User action name for infinite feed triggering.
 const char kDiscoverFeedUserActionInfiniteFeedTriggered[] =
     "ContentSuggestions.Feed.InfiniteFeedTriggered";
+
+// Histogram name for the feed engagement types.
+const char kDiscoverFeedEngagementTypeHistogram[] =
+    "ContentSuggestions.Feed.EngagementType";
+
+// Minimum scrolling amount to record a FeedEngagementType::kFeedEngaged due to
+// scrolling.
+const int kMinScrollThreshold = 160;
+
+// Time between two metrics recorded to consider it a new session.
+const int kMinutesBetweenSessions = 5;
 }  // namespace
 
+@interface DiscoverFeedMetricsRecorder ()
+
+// Tracking property to avoid duplicate recordings of
+// FeedEngagementType::kFeedEngagedSimple.
+@property(nonatomic, assign) BOOL engagedSimpleReported;
+// Tracking property to avoid duplicate recordings of
+// FeedEngagementType::kFeedEngaged.
+@property(nonatomic, assign) BOOL engagedReported;
+// Tracking property to avoid duplicate recordings of
+// FeedEngagementType::kFeedScrolled.
+@property(nonatomic, assign) BOOL scrolledReported;
+// The time when the first metric is being recorded for this session.
+@property(nonatomic, assign) base::Time sessionStartTime;
+
+@end
+
 @implementation DiscoverFeedMetricsRecorder
 
 #pragma mark - Public
 
+- (void)recordFeedScrolled:(int)scrollDistance {
+  [self recordEngagement:scrollDistance interacted:NO];
+
+  if (!self.scrolledReported) {
+    [self recordEngagementTypeHistogram:FeedEngagementType::kFeedScrolled];
+    self.scrolledReported = YES;
+  }
+}
+
 - (void)recordInfiniteFeedTriggered {
   UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedInfiniteFeedTriggered,
                             FeedLoadStreamStatus::kLoadedFromNetwork);
@@ -202,6 +252,61 @@
 // Records histogram metrics for Discover feed user actions.
 - (void)recordDiscoverFeedUserActionHistogram:(FeedUserActionType)actionType {
   UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedUserActionHistogram, actionType);
+  [self recordInteraction];
+}
+
+// Records Feed engagement.
+- (void)recordEngagement:(int)scrollDistance interacted:(BOOL)interacted {
+  scrollDistance = abs(scrollDistance);
+
+  // Determine if this interaction is part of a new 'session'.
+  base::Time now = base::Time::Now();
+  base::TimeDelta visitTimeout =
+      base::TimeDelta::FromMinutes(kMinutesBetweenSessions);
+  if (now - self.sessionStartTime > visitTimeout) {
+    [self finalizeSession];
+  }
+  // Reset the last active time for session measurement.
+  self.sessionStartTime = now;
+
+  // Report the user as engaged-simple if they have scrolled any amount or
+  // interacted with the card, and we have not already reported it for this
+  // chrome run.
+  if (!self.engagedSimpleReported && (scrollDistance > 0 || interacted)) {
+    [self recordEngagementTypeHistogram:FeedEngagementType::kFeedEngagedSimple];
+    self.engagedSimpleReported = YES;
+  }
+
+  // Report the user as engaged if they have scrolled more than the threshold or
+  // interacted with the card, and we have not already reported it this chrome
+  // run.
+  if (!self.engagedReported &&
+      (scrollDistance > kMinScrollThreshold || interacted)) {
+    [self recordEngagementTypeHistogram:FeedEngagementType::kFeedEngaged];
+    self.engagedReported = YES;
+  }
+}
+
+// Records any direct interaction with the Feed, this doesn't include scrolling.
+- (void)recordInteraction {
+  [self recordEngagement:0 interacted:YES];
+  [self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted];
+}
+
+// Records Engagement histograms of |engagementType|.
+- (void)recordEngagementTypeHistogram:(FeedEngagementType)engagementType {
+  UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedEngagementTypeHistogram,
+                            engagementType);
+}
+
+// Resets the session tracking values, this occurs if there's been
+// kMinutesBetweenSessions minutes between sessions.
+- (void)finalizeSession {
+  if (!self.engagedSimpleReported)
+    return;
+  self.engagedReported = NO;
+  self.engagedSimpleReported = NO;
+  self.scrolledReported = NO;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 7a91f3f..f0d9af44 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -90,6 +90,7 @@
     "//ios/chrome/browser/crash_report/breadcrumbs:feature_flags",
     "//ios/chrome/browser/first_run",
     "//ios/chrome/browser/main",
+    "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/ntp:features",
     "//ios/chrome/browser/ntp_snippets:ntp_snippets",
     "//ios/chrome/browser/screenshot",
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 299bf3fe..12e217c4 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -38,6 +38,7 @@
 #import "ios/chrome/browser/main/browser_list.h"
 #import "ios/chrome/browser/main/browser_list_factory.h"
 #import "ios/chrome/browser/main/browser_util.h"
+#import "ios/chrome/browser/metrics/previous_session_info.h"
 #include "ios/chrome/browser/ntp/features.h"
 #import "ios/chrome/browser/ntp_snippets/content_suggestions_scheduler_notifications.h"
 #include "ios/chrome/browser/screenshot/screenshot_delegate.h"
@@ -296,6 +297,12 @@
       level > SceneActivationLevelBackground && !self.hasInitializedUI;
   if (initializingUIInColdStart) {
     [self initializeUI];
+    if (@available(iOS 13, *)) {
+      // Add the scene to the list of connected scene, to restore in case of
+      // crashes.
+      [[PreviousSessionInfo sharedInstance]
+          addSceneSessionID:sceneState.scene.session.persistentIdentifier];
+    }
   }
 
   if (level == SceneActivationLevelForegroundActive) {
@@ -340,6 +347,10 @@
   }
 
   if (self.hasInitializedUI && level == SceneActivationLevelUnattached) {
+    if (@available(iOS 13, *)) {
+      [[PreviousSessionInfo sharedInstance]
+          removeSceneSessionID:sceneState.scene.session.persistentIdentifier];
+    }
     [self teardownUI];
   }
 }
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index d72169d6..0ac4f31 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -5,38 +5,24 @@
 source_set("password") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "legacy_password_details_table_view_controller.h",
-    "legacy_password_details_table_view_controller.mm",
-    "legacy_password_details_table_view_controller_delegate.h",
-    "password_exporter.h",
-    "password_exporter.mm",
     "password_issue_with_form.h",
     "password_issue_with_form.mm",
     "password_issues_coordinator.h",
     "password_issues_coordinator.mm",
     "password_issues_mediator.h",
     "password_issues_mediator.mm",
-    "passwords_consumer.h",
+    "passwords_coordinator.h",
+    "passwords_coordinator.mm",
     "passwords_mediator.h",
     "passwords_mediator.mm",
-    "passwords_table_view_controller.h",
-    "passwords_table_view_controller.mm",
   ]
   deps = [
-    ":password_constants",
     ":password_ui",
     "//base",
     "//components/autofill/core/common",
-    "//components/google/core/common",
-    "//components/keyed_service/core",
-    "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
-    "//components/prefs",
-    "//components/strings",
-    "//components/url_formatter",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
-    "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/passwords",
@@ -45,6 +31,56 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/settings/password/password_details",
+    "//ios/chrome/common",
+    "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/reauthentication",
+    "//ui/base",
+    "//url",
+  ]
+  frameworks = [ "MobileCoreServices.framework" ]
+}
+
+source_set("password_ui") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "legacy_password_details_table_view_controller.h",
+    "legacy_password_details_table_view_controller.mm",
+    "legacy_password_details_table_view_controller_delegate.h",
+    "password_exporter.h",
+    "password_exporter.mm",
+    "password_issue.h",
+    "password_issue_content_item.h",
+    "password_issue_content_item.mm",
+    "password_issues_consumer.h",
+    "password_issues_presenter.h",
+    "password_issues_table_view_controller.h",
+    "password_issues_table_view_controller.mm",
+    "passwords_consumer.h",
+    "passwords_settings_commands.h",
+    "passwords_table_view_controller.h",
+    "passwords_table_view_controller.mm",
+    "passwords_table_view_controller_delegate.h",
+    "passwords_table_view_controller_presentation_delegate.h",
+  ]
+  deps = [
+    ":password_constants",
+    "//base",
+    "//components/autofill/core/common",
+    "//components/google/core/common",
+    "//components/password_manager/core/browser",
+    "//components/password_manager/core/common",
+    "//components/prefs",
+    "//components/strings",
+    "//components/url_formatter",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/cells",
@@ -53,6 +89,7 @@
     "//ios/chrome/browser/ui/settings/password/password_details",
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/browser/ui/table_view/cells:cells_constants",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common",
@@ -63,40 +100,7 @@
     "//ios/chrome/common/ui/util",
     "//ios/third_party/material_components_ios",
     "//ui/base",
-    "//ui/base",
     "//ui/base/clipboard:clipboard_types",
-    "//url",
-  ]
-  frameworks = [ "MobileCoreServices.framework" ]
-}
-
-source_set("password_ui") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "password_issue.h",
-    "password_issue_content_item.h",
-    "password_issue_content_item.mm",
-    "password_issues_consumer.h",
-    "password_issues_presenter.h",
-    "password_issues_table_view_controller.h",
-    "password_issues_table_view_controller.mm",
-  ]
-  deps = [
-    "//base",
-    "//components/autofill/core/common",
-    "//components/password_manager/core/browser",
-    "//components/password_manager/core/common",
-    "//components/prefs",
-    "//components/strings",
-    "//components/url_formatter",
-    "//ios/chrome/app/strings:ios_strings_grit",
-    "//ios/chrome/browser",
-    "//ios/chrome/browser/ui/settings:settings_root",
-    "//ios/chrome/browser/ui/settings/autofill",
-    "//ios/chrome/browser/ui/table_view/cells",
-    "//ios/chrome/browser/ui/util",
-    "//ios/chrome/common/ui/util",
-    "//ui/base",
   ]
 }
 
@@ -116,7 +120,7 @@
     "legacy_password_details_table_view_controller+testing.h",
     "password_exporter_for_testing.h",
   ]
-  deps = [ ":password" ]
+  deps = [ ":password_ui" ]
 }
 
 source_set("unit_tests") {
diff --git a/ios/chrome/browser/ui/settings/password/passwords_consumer.h b/ios/chrome/browser/ui/settings/password/passwords_consumer.h
index 4435559..2f5f689 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_consumer.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_consumer.h
@@ -7,6 +7,13 @@
 
 #import <Foundation/Foundation.h>
 
+#include <memory>
+#include <vector>
+
+namespace autofill {
+struct PasswordForm;
+}
+
 // Enum with all possible UI states of password check.
 typedef NS_ENUM(NSInteger, PasswordCheckUIState) {
   // When no compromised passwords were detected.
@@ -27,7 +34,8 @@
 @protocol PasswordsConsumer <NSObject>
 
 // Displays current password check UI state on screen.
-- (void)setPasswordCheckUIState:(PasswordCheckUIState)state;
+- (void)setPasswordCheckUIState:(PasswordCheckUIState)state
+      compromisedPasswordsCount:(NSInteger)count;
 
 // Displays password and blocked forms.
 - (void)setPasswordsForms:
diff --git a/ios/chrome/browser/ui/settings/password/passwords_coordinator.h b/ios/chrome/browser/ui/settings/password/passwords_coordinator.h
new file mode 100644
index 0000000..e0b1071
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/passwords_coordinator.h
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+class Browser;
+@class PasswordsCoordinator;
+
+// Delegate for PasswordsCoordinator.
+@protocol PasswordsCoordinatorDelegate
+
+// Called when the view controller is removed from navigation controller.
+- (void)passwordsCoordinatorDidRemove:(PasswordsCoordinator*)coordinator;
+
+@end
+
+// This coordinator presents a list of saved passwords and some passwords
+// related features.
+@interface PasswordsCoordinator : ChromeCoordinator
+
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// Starts password check. For example, used by PasswordBreachDialog to
+// automatically start the check.
+- (void)checkSavedPasswords;
+
+@property(nonatomic, weak) id<PasswordsCoordinatorDelegate> delegate;
+
+@property(nonatomic, strong, readonly) UIViewController* viewController;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
new file mode 100644
index 0000000..a04abc1
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
@@ -0,0 +1,229 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/password/passwords_coordinator.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#import "ios/chrome/browser/main/browser.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#include "ios/chrome/browser/signin/authentication_service_factory.h"
+#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_mediator.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface PasswordsCoordinator () <
+    LegacyPasswordDetailsTableViewControllerDelegate,
+    PasswordDetailsCoordinatorDelegate,
+    PasswordIssuesCoordinatorDelegate,
+    PasswordsSettingsCommands,
+    PasswordsTableViewControllerPresentationDelegate>
+
+// Main view controller for this coordinator.
+@property(nonatomic, strong)
+    PasswordsTableViewController* passwordsViewController;
+
+// Main mediator for this coordinator.
+@property(nonatomic, strong) PasswordsMediator* mediator;
+
+// Reauthentication module used by passwords export and password details.
+@property(nonatomic, strong) ReauthenticationModule* reauthModule;
+
+// The dispatcher used by |viewController|.
+@property(nonatomic, weak)
+    id<ApplicationCommands, BrowserCommands, BrowsingDataCommands>
+        dispatcher;
+
+// Coordinator for password details.
+@property(nonatomic, strong)
+    PasswordIssuesCoordinator* passwordIssuesCoordinator;
+
+// Coordinator for password details.
+@property(nonatomic, strong)
+    PasswordDetailsCoordinator* passwordDetailsCoordinator;
+
+@end
+
+@implementation PasswordsCoordinator
+
+@synthesize baseNavigationController = _baseNavigationController;
+
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser {
+  self = [super initWithBaseViewController:navigationController
+                                   browser:browser];
+  if (self) {
+    _baseNavigationController = navigationController;
+    _dispatcher = static_cast<
+        id<BrowserCommands, ApplicationCommands, BrowsingDataCommands>>(
+        browser->GetCommandDispatcher());
+  }
+  return self;
+}
+
+- (void)checkSavedPasswords {
+  [self.mediator startPasswordCheck];
+  base::UmaHistogramEnumeration(
+      "PasswordManager.BulkCheck.UserAction",
+      password_manager::metrics_util::PasswordCheckInteraction::
+          kAutomaticPasswordCheck);
+}
+
+- (UIViewController*)viewController {
+  return self.passwordsViewController;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  self.mediator = [[PasswordsMediator alloc]
+      initWithPasswordStore:IOSChromePasswordStoreFactory::GetForBrowserState(
+                                self.browser->GetBrowserState(),
+                                ServiceAccessType::EXPLICIT_ACCESS)
+       passwordCheckManager:[self passwordCheckManager]
+                authService:AuthenticationServiceFactory::GetForBrowserState(
+                                self.browser->GetBrowserState())
+                syncService:SyncSetupServiceFactory::GetForBrowserState(
+                                self.browser->GetBrowserState())];
+  self.reauthModule = [[ReauthenticationModule alloc]
+      initWithSuccessfulReauthTimeAccessor:self.mediator];
+
+  self.passwordsViewController =
+      [[PasswordsTableViewController alloc] initWithBrowser:self.browser];
+
+  self.passwordsViewController.handler = self;
+  self.passwordsViewController.delegate = self.mediator;
+  self.passwordsViewController.dispatcher = self.dispatcher;
+  self.passwordsViewController.presentationDelegate = self;
+  self.passwordsViewController.reauthenticationModule = self.reauthModule;
+
+  self.mediator.consumer = self.passwordsViewController;
+
+  [self.baseNavigationController pushViewController:self.passwordsViewController
+                                           animated:YES];
+}
+
+- (void)stop {
+  self.passwordsViewController = nil;
+
+  [self.passwordIssuesCoordinator stop];
+  self.passwordIssuesCoordinator.delegate = nil;
+  self.passwordIssuesCoordinator = nil;
+
+  [self.passwordDetailsCoordinator stop];
+  self.passwordDetailsCoordinator.delegate = nil;
+  self.passwordDetailsCoordinator = nil;
+}
+
+#pragma mark - PasswordsSettingsCommands
+
+- (void)showCompromisedPasswords {
+  DCHECK(!self.passwordIssuesCoordinator);
+  self.passwordIssuesCoordinator = [[PasswordIssuesCoordinator alloc]
+      initWithBaseNavigationController:self.baseNavigationController
+                               browser:self.browser
+                  passwordCheckManager:[self passwordCheckManager].get()];
+  self.passwordIssuesCoordinator.delegate = self;
+  self.passwordIssuesCoordinator.reauthModule = self.reauthModule;
+  [self.passwordIssuesCoordinator start];
+}
+
+- (void)showDetailedViewForForm:(const autofill::PasswordForm&)form {
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kPasswordCheck)) {
+    DCHECK(!self.passwordDetailsCoordinator);
+    self.passwordDetailsCoordinator = [[PasswordDetailsCoordinator alloc]
+        initWithBaseNavigationController:self.baseNavigationController
+                                 browser:self.browser
+                                password:form
+                            reauthModule:self.reauthModule
+                    passwordCheckManager:[self passwordCheckManager].get()];
+    self.passwordDetailsCoordinator.delegate = self;
+    [self.passwordDetailsCoordinator start];
+  } else {
+    LegacyPasswordDetailsTableViewController* controller =
+        [[LegacyPasswordDetailsTableViewController alloc]
+              initWithPasswordForm:form
+                          delegate:self
+            reauthenticationModule:self.reauthModule];
+    controller.dispatcher = self.dispatcher;
+    [self.baseNavigationController pushViewController:controller animated:YES];
+  }
+}
+
+#pragma mark - PasswordsTableViewControllerPresentationDelegate
+
+- (void)passwordsTableViewControllerDismissed {
+  [self.delegate passwordsCoordinatorDidRemove:self];
+}
+
+#pragma mark - PasswordIssuesCoordinatorDelegate
+
+- (void)passwordIssuesCoordinatorDidRemove:
+    (PasswordIssuesCoordinator*)coordinator {
+  DCHECK_EQ(self.passwordIssuesCoordinator, coordinator);
+  [self.passwordIssuesCoordinator stop];
+  self.passwordIssuesCoordinator.delegate = nil;
+  self.passwordIssuesCoordinator = nil;
+}
+
+- (BOOL)willHandlePasswordDeletion:(const autofill::PasswordForm&)password {
+  [self.passwordsViewController deletePasswordForm:password];
+  return YES;
+}
+
+#pragma mark PasswordDetailsCoordinatorDelegate
+
+- (void)passwordDetailsCoordinatorDidRemove:
+    (PasswordDetailsCoordinator*)coordinator {
+  DCHECK_EQ(self.passwordDetailsCoordinator, coordinator);
+  [self.passwordDetailsCoordinator stop];
+  self.passwordDetailsCoordinator.delegate = nil;
+  self.passwordDetailsCoordinator = nil;
+}
+
+- (void)passwordDetailsCoordinator:(PasswordDetailsCoordinator*)coordinator
+                    deletePassword:(const autofill::PasswordForm&)password {
+  DCHECK_EQ(self.passwordDetailsCoordinator, coordinator);
+  [self.passwordsViewController deletePasswordForm:password];
+}
+
+#pragma mark LegacyPasswordDetailsTableViewControllerDelegate
+
+- (void)passwordDetailsTableViewController:
+            (LegacyPasswordDetailsTableViewController*)controller
+                            deletePassword:(const autofill::PasswordForm&)form {
+  [self.passwordsViewController deletePasswordForm:form];
+}
+
+#pragma mark Private
+
+- (scoped_refptr<IOSChromePasswordCheckManager>)passwordCheckManager {
+  return IOSChromePasswordCheckManagerFactory::GetForBrowserState(
+      self.browser->GetBrowserState());
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.h b/ios/chrome/browser/ui/settings/password/passwords_mediator.h
index de7b8ad..de70e96 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.h
@@ -8,6 +8,8 @@
 #import <Foundation/Foundation.h>
 
 #include "base/memory/scoped_refptr.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h"
+#import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 
 class AuthenticationService;
 class IOSChromePasswordCheckManager;
@@ -19,7 +21,8 @@
 }
 
 // This mediator fetches and organises the passwords for its consumer.
-@interface PasswordsMediator : NSObject
+@interface PasswordsMediator : NSObject <PasswordsTableViewControllerDelegate,
+                                         SuccessfulReauthTimeAccessor>
 
 - (instancetype)
     initWithPasswordStore:
@@ -34,15 +37,6 @@
 
 @property(nonatomic, weak) id<PasswordsConsumer> consumer;
 
-// Returns detailed information about error if applicable.
-- (NSAttributedString*)passwordCheckErrorInfo;
-
-// Returns string containing the timestamp of the last password check. If the
-// check finished less than 1 minute ago string will look "Last check just
-// now.", otherwise "Last check X minutes/hours... ago.". If check never run
-// string will be "Check never run.".
-- (NSString*)formatElapsedTimeSinceLastCheck;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
index efb8c662..9021bfce 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
@@ -16,9 +16,7 @@
 #import "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/string_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
@@ -68,6 +66,12 @@
   PasswordCheckState _currentState;
 }
 
+// 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
+// of the Passwords Screen.
+@property(nonatomic, strong, readonly) NSDate* successfulReauthTime;
+
 @end
 
 @implementation PasswordsMediator
@@ -116,10 +120,44 @@
           password_manager::features::kPasswordCheck)) {
     _currentState = _passwordCheckManager->GetPasswordCheckState();
     [self.consumer setPasswordCheckUIState:
-                       [self computePasswordCheckUIStateWith:_currentState]];
+                       [self computePasswordCheckUIStateWith:_currentState]
+                 compromisedPasswordsCount:_passwordCheckManager
+                                               ->GetCompromisedCredentials()
+                                               .size()];
   }
 }
 
+#pragma mark - PasswordsTableViewControllerDelegate
+
+- (void)startPasswordCheck {
+  _passwordCheckManager->StartPasswordCheck();
+}
+
+- (NSString*)formatElapsedTimeSinceLastCheck {
+  base::Time lastCompletedCheck =
+      _passwordCheckManager->GetLastPasswordCheckTime();
+
+  // lastCompletedCheck is 0.0 in case the check never completely ran before.
+  if (lastCompletedCheck == base::Time())
+    return l10n_util::GetNSString(IDS_IOS_CHECK_NEVER_RUN);
+
+  base::TimeDelta elapsedTime = base::Time::Now() - lastCompletedCheck;
+
+  NSString* timestamp;
+  // If check finished in less than |kJustCheckedTimeThresholdInMinutes| show
+  // "just now" instead of timestamp.
+  if (elapsedTime < kJustCheckedTimeThresholdInMinutes)
+    timestamp = l10n_util::GetNSString(IDS_IOS_CHECK_FINISHED_JUST_NOW);
+  else
+    timestamp = base::SysUTF8ToNSString(
+        base::UTF16ToUTF8(ui::TimeFormat::SimpleWithMonthAndYear(
+            ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_LONG,
+            elapsedTime, true)));
+
+  return l10n_util::GetNSStringF(IDS_IOS_LAST_COMPLETED_CHECK,
+                                 base::SysNSStringToUTF16(timestamp));
+}
+
 - (NSAttributedString*)passwordCheckErrorInfo {
   if (!_passwordCheckManager->GetCompromisedCredentials().empty())
     return nil;
@@ -165,7 +203,10 @@
 
   DCHECK(self.consumer);
   [self.consumer
-      setPasswordCheckUIState:[self computePasswordCheckUIStateWith:state]];
+        setPasswordCheckUIState:[self computePasswordCheckUIStateWith:state]
+      compromisedPasswordsCount:_passwordCheckManager
+                                    ->GetCompromisedCredentials()
+                                    .size()];
 }
 
 - (void)compromisedCredentialsDidChange:
@@ -176,8 +217,10 @@
     return;
 
   DCHECK(self.consumer);
+
   [self.consumer setPasswordCheckUIState:
-                     [self computePasswordCheckUIStateWith:_currentState]];
+                     [self computePasswordCheckUIStateWith:_currentState]
+               compromisedPasswordsCount:credentials.size()];
 }
 
 #pragma mark - Private Methods
@@ -265,29 +308,14 @@
   [self.consumer setPasswordsForms:std::move(results)];
 }
 
-- (NSString*)formatElapsedTimeSinceLastCheck {
-  base::Time lastCompletedCheck =
-      _passwordCheckManager->GetLastPasswordCheckTime();
+#pragma mark SuccessfulReauthTimeAccessor
 
-  // lastCompletedCheck is 0.0 in case the check never completely ran before.
-  if (lastCompletedCheck == base::Time())
-    return l10n_util::GetNSString(IDS_IOS_CHECK_NEVER_RUN);
+- (void)updateSuccessfulReauthTime {
+  _successfulReauthTime = [[NSDate alloc] init];
+}
 
-  base::TimeDelta elapsedTime = base::Time::Now() - lastCompletedCheck;
-
-  NSString* timestamp;
-  // If check finished in less than |kJustCheckedTimeThresholdInMinutes| show
-  // "just now" instead of timestamp.
-  if (elapsedTime < kJustCheckedTimeThresholdInMinutes)
-    timestamp = l10n_util::GetNSString(IDS_IOS_CHECK_FINISHED_JUST_NOW);
-  else
-    timestamp = base::SysUTF8ToNSString(
-        base::UTF16ToUTF8(ui::TimeFormat::SimpleWithMonthAndYear(
-            ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_LONG,
-            elapsedTime, true)));
-
-  return l10n_util::GetNSStringF(IDS_IOS_LAST_COMPLETED_CHECK,
-                                 base::SysNSStringToUTF16(timestamp));
+- (NSDate*)lastSuccessfulReauthTime {
+  return [self successfulReauthTime];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
index 25c978b3..d01a9426 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
@@ -72,7 +72,8 @@
 
 @implementation FakePasswordsConsumer
 
-- (void)setPasswordCheckUIState:(PasswordCheckUIState)state {
+- (void)setPasswordCheckUIState:(PasswordCheckUIState)state
+      compromisedPasswordsCount:(NSInteger)count {
 }
 
 - (void)setPasswordsForms:
diff --git a/ios/chrome/browser/ui/settings/password/passwords_settings_commands.h b/ios/chrome/browser/ui/settings/password/passwords_settings_commands.h
new file mode 100644
index 0000000..02caa73
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_commands.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_SETTINGS_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_SETTINGS_COMMANDS_H_
+
+#import <Foundation/Foundation.h>
+
+// Commands relative to the passwords in the Settings.
+@protocol PasswordsSettingsCommands <NSObject>
+
+// Shows the screen with password issues.
+- (void)showCompromisedPasswords;
+
+// Shows passwords details.
+- (void)showDetailedViewForForm:(const autofill::PasswordForm&)form;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_SETTINGS_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
index e5edaac..7a98ab71 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
@@ -5,34 +5,45 @@
 #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
 #define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/settings_controller_protocol.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
+#import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 
 class Browser;
-@protocol ReauthenticationProtocol;
 @class PasswordExporter;
+@protocol PasswordsSettingsCommands;
+@protocol PasswordsTableViewControllerDelegate;
+@protocol PasswordsTableViewControllerPresentationDelegate;
 
 @interface PasswordsTableViewController
-    : SettingsRootTableViewController <SettingsControllerProtocol>
+    : SettingsRootTableViewController <PasswordsConsumer,
+                                       SettingsControllerProtocol>
 
 // The designated initializer. |browser| must not be nil.
 - (instancetype)initWithBrowser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
 
-// Starts password check.
-- (void)startPasswordCheck;
+// Deletes passed password form and updates list accordingly.
+- (void)deletePasswordForm:(const autofill::PasswordForm&)form;
+
+@property(nonatomic, weak) id<PasswordsSettingsCommands> handler;
+
+// Delegate.
+@property(nonatomic, weak) id<PasswordsTableViewControllerDelegate> delegate;
+
+@property(nonatomic, weak) id<PasswordsTableViewControllerPresentationDelegate>
+    presentationDelegate;
+
+// Reauthentication module.
+@property(nonatomic, strong) id<ReauthenticationProtocol>
+    reauthenticationModule;
 
 @end
 
-@interface PasswordsTableViewController (Testing) <
-    LegacyPasswordDetailsTableViewControllerDelegate>
-
-// Initializes the password exporter with a (fake) |reauthenticationModule|.
-- (void)setReauthenticationModuleForExporter:
-    (id<ReauthenticationProtocol>)reauthenticationModule;
+@interface PasswordsTableViewController (Testing)
 
 // Returns the password exporter to allow setting fake testing objects on it.
 - (PasswordExporter*)getPasswordExporter;
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 df5ce1638..11f1dd4 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
@@ -6,17 +6,11 @@
 
 #import <UIKit/UIKit.h>
 
-#include "base/check_op.h"
 #include "base/ios/ios_util.h"
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/notreached.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/google/core/common/google_util.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -28,41 +22,28 @@
 #include "components/password_manager/core/browser/ui/password_check_referrer.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
-#include "components/url_formatter/url_formatter.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/main/browser.h"
-#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h"
-#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
-#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/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/ui/elements/home_waiting_view.h"
-#import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h"
-#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/password_exporter.h"
-#import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_mediator.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
 #import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h"
@@ -71,7 +52,6 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -185,18 +165,12 @@
 @interface PasswordsTableViewController () <
     BooleanObserver,
     ChromeIdentityServiceObserver,
-    LegacyPasswordDetailsTableViewControllerDelegate,
-    PasswordDetailsCoordinatorDelegate,
     PasswordExporterDelegate,
     PasswordExportActivityViewControllerDelegate,
     PasswordsConsumer,
-    PasswordIssuesCoordinatorDelegate,
     PopoverLabelViewControllerDelegate,
-    UISearchControllerDelegate,
     UISearchBarDelegate,
-    SuccessfulReauthTimeAccessor> {
-  // Mediator is owned here because there is no coordinator.
-  PasswordsMediator* _mediator;
+    UISearchControllerDelegate> {
   // The observable boolean that binds to the password manager setting state.
   // Saved passwords are only on if the password manager is enabled.
   PrefBackedBoolean* _passwordManagerEnabled;
@@ -212,8 +186,6 @@
   TableViewTextItem* _checkForProblemsItem;
   // The item related to the button for exporting passwords.
   TableViewTextItem* _exportPasswordsItem;
-  // The service responsible for password check feature.
-  scoped_refptr<IOSChromePasswordCheckManager> _passwordCheck;
   // The interface for getting and manipulating a user's saved passwords.
   scoped_refptr<password_manager::PasswordStore> _passwordStore;
   // The list of the user's saved passwords.
@@ -230,14 +202,6 @@
   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
-  // of the Save Passwords Settings.
-  NSDate* _successfulReauthTime;
-  // Module containing the reauthentication mechanism for viewing and copying
-  // passwords.
-  ReauthenticationModule* _reauthenticationModule;
   // Boolean containing whether the export operation is ready. This implies that
   // the exporter is idle and there is at least one saved passwords to export.
   BOOL _exportReady;
@@ -247,8 +211,6 @@
   // Alert informing the user that passwords are being prepared for
   // export.
   UIAlertController* _preparingPasswordsAlert;
-  // Coordinator for passwords issues screen.
-  PasswordIssuesCoordinator* _passwordIssuesCoordinator;
 }
 
 // Object handling passwords export operations.
@@ -271,9 +233,8 @@
 // Current state of the Password Check.
 @property(nonatomic, assign) PasswordCheckUIState passwordCheckState;
 
-// Coordinator for password details.
-@property(nonatomic, strong)
-    PasswordDetailsCoordinator* passwordDetailsCoordinator;
+// Number of compromised passwords.
+@property(assign) NSInteger compromisedPasswordsCount;
 
 @end
 
@@ -290,11 +251,6 @@
   if (self) {
     _browser = browser;
     _browserState = browser->GetBrowserState();
-    _reauthenticationModule = [[ReauthenticationModule alloc]
-        initWithSuccessfulReauthTimeAccessor:self];
-    _passwordExporter = [[PasswordExporter alloc]
-        initWithReauthenticationModule:_reauthenticationModule
-                              delegate:self];
     self.exampleHeaders = [[NSMutableDictionary alloc] init];
     self.title = l10n_util::GetNSString(IDS_IOS_PASSWORDS);
     self.shouldHideDoneButton = YES;
@@ -302,16 +258,6 @@
     _passwordStore = IOSChromePasswordStoreFactory::GetForBrowserState(
         _browserState, ServiceAccessType::EXPLICIT_ACCESS);
     DCHECK(_passwordStore);
-    _passwordCheck =
-        IOSChromePasswordCheckManagerFactory::GetForBrowserState(_browserState);
-    _mediator = [[PasswordsMediator alloc]
-        initWithPasswordStore:_passwordStore
-         passwordCheckManager:_passwordCheck
-                  authService:AuthenticationServiceFactory::GetForBrowserState(
-                                  _browserState)
-                  syncService:SyncSetupServiceFactory::GetForBrowserState(
-                                  _browserState)];
-    _mediator.consumer = self;
     _passwordManagerEnabled = [[PrefBackedBoolean alloc]
         initWithPrefService:_browserState->GetPrefs()
                    prefName:password_manager::prefs::kCredentialsEnableService];
@@ -322,12 +268,12 @@
   return self;
 }
 
-- (void)startPasswordCheck {
-  if (_passwordCheck->GetPasswordCheckState() != PasswordCheckState::kRunning) {
-    _passwordCheck->StartPasswordCheck();
-    UmaHistogramEnumeration("PasswordManager.BulkCheck.UserAction",
-                            PasswordCheckInteraction::kAutomaticPasswordCheck);
-  }
+- (void)setReauthenticationModule:
+    (ReauthenticationModule*)reauthenticationModule {
+  _reauthenticationModule = reauthenticationModule;
+  _passwordExporter = [[PasswordExporter alloc]
+      initWithReauthenticationModule:_reauthenticationModule
+                            delegate:self];
 }
 
 #pragma mark - UIViewController
@@ -408,6 +354,13 @@
                              forBarMetrics:UIBarMetricsDefault];
 }
 
+- (void)didMoveToParentViewController:(UIViewController*)parent {
+  [super didMoveToParentViewController:parent];
+  if (!parent) {
+    [self.presentationDelegate passwordsTableViewControllerDismissed];
+  }
+}
+
 - (void)setEditing:(BOOL)editing animated:(BOOL)animated {
   [super setEditing:editing animated:animated];
   if (editing) {
@@ -551,14 +504,6 @@
   if (self.navigationItem.searchController.active == YES) {
     self.navigationItem.searchController.active = NO;
   }
-
-  [_passwordIssuesCoordinator stop];
-  _passwordIssuesCoordinator.delegate = nil;
-  _passwordIssuesCoordinator = nil;
-
-  [self.passwordDetailsCoordinator stop];
-  self.passwordDetailsCoordinator.delegate = nil;
-  self.passwordDetailsCoordinator = nil;
 }
 
 #pragma mark - Items
@@ -621,7 +566,7 @@
   TableViewLinkHeaderFooterItem* footerItem =
       [[TableViewLinkHeaderFooterItem alloc]
           initWithType:ItemTypeLastCheckTimestampFooter];
-  footerItem.text = [_mediator formatElapsedTimeSinceLastCheck];
+  footerItem.text = [self.delegate formatElapsedTimeSinceLastCheck];
   return footerItem;
 }
 
@@ -724,7 +669,7 @@
 // Called when user tapped on the information button of the password check
 // item. Shows popover with detailed description of an error.
 - (void)didTapPasswordCheckInfoButton:(UIButton*)buttonView {
-  NSAttributedString* info = [_mediator passwordCheckErrorInfo];
+  NSAttributedString* info = [self.delegate passwordCheckErrorInfo];
   // If no info returned by mediator handle this tap as tap on a cell.
   if (!info) {
     [self showPasswordIssuesPage];
@@ -745,7 +690,9 @@
 
 #pragma mark - PasswordsConsumer
 
-- (void)setPasswordCheckUIState:(PasswordCheckUIState)state {
+- (void)setPasswordCheckUIState:(PasswordCheckUIState)state
+      compromisedPasswordsCount:(NSInteger)count {
+  self.compromisedPasswordsCount = count;
   // Update password check status and check button with new state.
   [self updatePasswordCheckButtonWithState:state];
   [self updatePasswordCheckStatusLabelWithState:state];
@@ -923,21 +870,6 @@
   [self searchForTerm:searchText];
 }
 
-#pragma mark - PasswordIssuesCoordinatorDelegate
-
-- (void)passwordIssuesCoordinatorDidRemove:
-    (PasswordIssuesCoordinator*)coordinator {
-  DCHECK_EQ(_passwordIssuesCoordinator, coordinator);
-  [_passwordIssuesCoordinator stop];
-  _passwordIssuesCoordinator.delegate = nil;
-  _passwordIssuesCoordinator = nil;
-}
-
-- (BOOL)willHandlePasswordDeletion:(const autofill::PasswordForm&)password {
-  [self deletePasswordForm:password];
-  return YES;
-}
-
 #pragma mark - Private methods
 
 // Shows loading spinner background view.
@@ -1188,7 +1120,7 @@
       _passwordProblemsItem.detailText =
           base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
               IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT,
-              _passwordCheck->GetCompromisedCredentials().size()));
+              self.compromisedPasswordsCount));
       UIImage* unSafeIconImage = [[UIImage imageNamed:@"settings_unsafe_state"]
           imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
       _passwordProblemsItem.trailingImage = unSafeIconImage;
@@ -1199,7 +1131,7 @@
       break;
     }
     case PasswordCheckStateSafe: {
-      DCHECK(_passwordCheck->GetCompromisedCredentials().empty());
+      DCHECK(!self.compromisedPasswordsCount);
       UIImage* safeIconImage = [[UIImage imageNamed:@"settings_safe_state"]
           imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
       _passwordProblemsItem.detailText =
@@ -1302,29 +1234,6 @@
   }
 }
 
-- (void)openDetailedViewForForm:(const autofill::PasswordForm&)form {
-  if (base::FeatureList::IsEnabled(
-          password_manager::features::kPasswordCheck)) {
-    DCHECK(!self.passwordDetailsCoordinator);
-    self.passwordDetailsCoordinator = [[PasswordDetailsCoordinator alloc]
-        initWithBaseNavigationController:self.navigationController
-                                 browser:_browser
-                                password:form
-                            reauthModule:_reauthenticationModule
-                    passwordCheckManager:_passwordCheck.get()];
-    self.passwordDetailsCoordinator.delegate = self;
-    [self.passwordDetailsCoordinator start];
-  } else {
-    LegacyPasswordDetailsTableViewController* controller =
-        [[LegacyPasswordDetailsTableViewController alloc]
-              initWithPasswordForm:form
-                          delegate:self
-            reauthenticationModule:_reauthenticationModule];
-    controller.dispatcher = self.dispatcher;
-    [self.navigationController pushViewController:controller animated:YES];
-  }
-}
-
 - (void)deleteItemAtIndexPaths:(NSArray<NSIndexPath*>*)indexPaths {
   // Ensure indexPaths are sorted to maintain delete logic, and keep track of
   // number of items deleted to adjust index for accessing elements in the
@@ -1416,17 +1325,10 @@
 }
 
 - (void)showPasswordIssuesPage {
-  if (_passwordCheck->GetCompromisedCredentials().empty() ||
-      _passwordCheck->GetPasswordCheckState() == PasswordCheckState::kRunning)
+  if (!self.compromisedPasswordsCount ||
+      self.passwordCheckState == PasswordCheckStateRunning)
     return;
-  DCHECK(!_passwordIssuesCoordinator);
-  _passwordIssuesCoordinator = [[PasswordIssuesCoordinator alloc]
-      initWithBaseNavigationController:self.navigationController
-                               browser:_browser
-                  passwordCheckManager:_passwordCheck.get()];
-  _passwordIssuesCoordinator.delegate = self;
-  _passwordIssuesCoordinator.reauthModule = _reauthenticationModule;
-  [_passwordIssuesCoordinator start];
+  [self.handler showCompromisedPasswords];
   password_manager::LogPasswordCheckReferrer(
       password_manager::PasswordCheckReferrer::kPasswordSettings);
 }
@@ -1459,7 +1361,7 @@
       SavedFormContentItem* saveFormItem =
           base::mac::ObjCCastStrict<SavedFormContentItem>(
               [model itemAtIndexPath:indexPath]);
-      [self openDetailedViewForForm:*saveFormItem.form];
+      [self.handler showDetailedViewForForm:*saveFormItem.form];
       break;
     }
     case ItemTypeBlocked: {
@@ -1468,7 +1370,7 @@
       BlockedFormContentItem* blockedItem =
           base::mac::ObjCCastStrict<BlockedFormContentItem>(
               [model itemAtIndexPath:indexPath]);
-      [self openDetailedViewForForm:*blockedItem.form];
+      [self.handler showDetailedViewForForm:*blockedItem.form];
       break;
     }
     case ItemTypeExportPasswordsButton:
@@ -1480,7 +1382,7 @@
       break;
     case ItemTypeCheckForProblemsButton:
       if (self.passwordCheckState != PasswordCheckStateRunning) {
-        _passwordCheck->StartPasswordCheck();
+        [self.delegate startPasswordCheck];
         UmaHistogramEnumeration("PasswordManager.BulkCheck.UserAction",
                                 PasswordCheckInteraction::kManualPasswordCheck);
       }
@@ -1582,40 +1484,6 @@
   return cell;
 }
 
-#pragma mark PasswordDetailsCoordinatorDelegate
-
-- (void)passwordDetailsCoordinatorDidRemove:
-    (PasswordDetailsCoordinator*)coordinator {
-  DCHECK_EQ(self.passwordDetailsCoordinator, coordinator);
-  [self.passwordDetailsCoordinator stop];
-  self.passwordDetailsCoordinator.delegate = nil;
-  self.passwordDetailsCoordinator = nil;
-}
-
-- (void)passwordDetailsCoordinator:(PasswordDetailsCoordinator*)coordinator
-                    deletePassword:(const autofill::PasswordForm&)password {
-  DCHECK_EQ(self.passwordDetailsCoordinator, coordinator);
-  [self deletePasswordForm:password];
-}
-
-#pragma mark LegacyPasswordDetailsTableViewControllerDelegate
-
-- (void)passwordDetailsTableViewController:
-            (LegacyPasswordDetailsTableViewController*)controller
-                            deletePassword:(const autofill::PasswordForm&)form {
-  [self deletePasswordForm:form];
-}
-
-#pragma mark SuccessfulReauthTimeAccessor
-
-- (void)updateSuccessfulReauthTime {
-  _successfulReauthTime = [[NSDate alloc] init];
-}
-
-- (NSDate*)lastSuccessfulReauthTime {
-  return _successfulReauthTime;
-}
-
 #pragma mark PasswordExporterDelegate
 
 - (void)showSetPasscodeDialog {
@@ -1798,13 +1666,6 @@
 
 #pragma mark - Testing
 
-- (void)setReauthenticationModuleForExporter:
-    (id<ReauthenticationProtocol>)reauthenticationModule {
-  _passwordExporter = [[PasswordExporter alloc]
-      initWithReauthenticationModule:reauthenticationModule
-                            delegate:self];
-}
-
 - (PasswordExporter*)getPasswordExporter {
   return _passwordExporter;
 }
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h
new file mode 100644
index 0000000..efa2005
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+// Delegate for |PasswordsTableViewController|.
+@protocol PasswordsTableViewControllerDelegate
+
+// Starts password check.
+- (void)startPasswordCheck;
+
+// Returns string containing the timestamp of the last password check. If the
+// check finished less than 1 minute ago string will look "Last check just
+// now.", otherwise "Last check X minutes/hours... ago.". If check never run
+// string will be "Check never run.".
+- (NSString*)formatElapsedTimeSinceLastCheck;
+
+// Returns detailed information about Password Check error if applicable.
+- (NSAttributedString*)passwordCheckErrorInfo;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h
new file mode 100644
index 0000000..49bdcdf
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+// Presentation delegate for |PasswordsTableViewController|.
+@protocol PasswordsTableViewControllerPresentationDelegate
+
+// Called when |PasswordsTableViewController| is dismissed.
+- (void)passwordsTableViewControllerDismissed;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
index 2c3479d..c703455 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
@@ -21,13 +21,14 @@
 #include "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/passwords/ios_chrome_bulk_leak_check_service_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/passwords/password_check_observer_bridge.h"
 #include "ios/chrome/browser/passwords/save_passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
 #import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_mediator.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #include "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
@@ -57,7 +58,6 @@
 // this file working.
 @interface PasswordsTableViewController (Test) <
     UISearchBarDelegate,
-    PasswordIssuesCoordinatorDelegate,
     PasswordsConsumer>
 - (void)updateExportPasswordsButton;
 @end
@@ -111,9 +111,21 @@
 
     CreateController();
 
+    mediator_ = [[PasswordsMediator alloc]
+        initWithPasswordStore:IOSChromePasswordStoreFactory::GetForBrowserState(
+                                  browser_->GetBrowserState(),
+                                  ServiceAccessType::EXPLICIT_ACCESS)
+         passwordCheckManager:IOSChromePasswordCheckManagerFactory::
+                                  GetForBrowserState(
+                                      browser_->GetBrowserState())
+                  authService:nil
+                  syncService:nil];
+
     // Inject some fake passwords to pass the loading state.
     PasswordsTableViewController* passwords_controller =
         static_cast<PasswordsTableViewController*>(controller());
+    passwords_controller.delegate = mediator_;
+    mediator_.consumer = passwords_controller;
     [passwords_controller setPasswordsForms:{}];
   }
 
@@ -154,7 +166,9 @@
   void ChangePasswordCheckState(PasswordCheckUIState state) {
     PasswordsTableViewController* passwords_controller =
         static_cast<PasswordsTableViewController*>(controller());
-    [passwords_controller setPasswordCheckUIState:state];
+    NSInteger count = GetTestStore().compromised_credentials().size();
+    [passwords_controller setPasswordCheckUIState:state
+                        compromisedPasswordsCount:count];
   }
 
   // Adds a form to PasswordsTableViewController.
@@ -284,6 +298,7 @@
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<TestBrowser> browser_;
   base::test::ScopedFeatureList scoped_feature_list_;
+  PasswordsMediator* mediator_;
 };
 
 // Tests default case has no saved sites and no blocked sites.
@@ -502,35 +517,6 @@
                UIAccessibilityTraitNotEnabled);
 }
 
-TEST_P(PasswordsTableViewControllerTest, PropagateDeletionToStore) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
-  autofill::PasswordForm form;
-  form.url = GURL("http://www.example.com/accounts/LoginAuth");
-  form.action = GURL("http://www.example.com/accounts/Login");
-  form.username_element = base::ASCIIToUTF16("Email");
-  form.username_value = base::ASCIIToUTF16("test@egmail.com");
-  form.password_element = base::ASCIIToUTF16("Passwd");
-  form.password_value = base::ASCIIToUTF16("test");
-  form.submit_element = base::ASCIIToUTF16("signIn");
-  form.signon_realm = "http://www.example.com/";
-  form.scheme = autofill::PasswordForm::Scheme::kHtml;
-  form.blocked_by_user = false;
-
-  AddPasswordForm(std::make_unique<autofill::PasswordForm>(form));
-
-  if (GetParam().password_check_enabled) {
-    autofill::PasswordForm formFromStore =
-        GetTestStore().stored_passwords().at("http://www.example.com/")[0];
-    [passwords_controller passwordDetailsTableViewController:nil
-                                              deletePassword:formFromStore];
-    RunUntilIdle();
-  } else {
-    [passwords_controller passwordDetailsTableViewController:nil
-                                              deletePassword:form];
-  }
-}
-
 // Tests filtering of items.
 TEST_P(PasswordsTableViewControllerTest, FilterItems) {
   AddSavedForm1();
@@ -760,7 +746,7 @@
 
   auto password =
       GetTestStore().stored_passwords().at("http://www.example.com/").at(0);
-  EXPECT_TRUE([passwords_controller willHandlePasswordDeletion:password]);
+  [passwords_controller deletePasswordForm:password];
   EXPECT_EQ(1, NumberOfItemsInSection(GetSectionIndex(SavedPasswords)));
 }
 
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index b55a1fe..7ba7c23 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -18,7 +18,7 @@
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_view_controller.h"
 #import "ios/chrome/browser/ui/settings/import_data_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_coordinator.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
@@ -39,6 +39,7 @@
 
 @interface SettingsNavigationController () <
     GoogleServicesSettingsCoordinatorDelegate,
+    PasswordsCoordinatorDelegate,
     UIAdaptivePresentationControllerDelegate,
     UINavigationControllerDelegate>
 
@@ -46,6 +47,9 @@
 @property(nonatomic, strong)
     GoogleServicesSettingsCoordinator* googleServicesSettingsCoordinator;
 
+// Saved passwords settings coordinator.
+@property(nonatomic, strong) PasswordsCoordinator* savedPasswordsCoordinator;
+
 // Current UIViewController being presented by this Navigation Controller.
 // If nil it means the Navigation Controller is not presenting anything, or the
 // VC being presented doesn't conform to
@@ -143,22 +147,17 @@
                                           delegate
       startPasswordCheckAutomatically:(BOOL)startCheck {
   DCHECK(browser);
-  PasswordsTableViewController* controller =
-      [[PasswordsTableViewController alloc] initWithBrowser:browser];
-  controller.dispatcher = [delegate handlerForSettings];
-  if (startCheck) {
-    [controller startPasswordCheck];
-  }
 
   SettingsNavigationController* nc = [[SettingsNavigationController alloc]
-      initWithRootViewController:controller
+      initWithRootViewController:nil
                          browser:browser
                         delegate:delegate];
-  [controller navigationItem].rightBarButtonItem = [nc doneButton];
+  [nc showSavedPasswordsAndStartPasswordCheck:startCheck];
 
   // Make sure the cancel button is always present, as the Save Passwords screen
   // isn't just shown from Settings.
-  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
+  [nc.savedPasswordsCoordinator.viewController navigationItem]
+      .leftBarButtonItem = [nc cancelButton];
   return nc;
 }
 
@@ -315,9 +314,10 @@
     }
   }
 
-  // GoogleServicesSettingsCoordinator must be stopped before dismissing the
-  // sync settings view.
+  // GoogleServicesSettingsCoordinator and PasswordsCoordinator must be stopped
+  // before dismissing the sync settings view.
   [self stopGoogleServicesSettingsCoordinator];
+  [self stopPasswordsCoordinator];
 
   // Reset the delegate to prevent any queued transitions from attempting to
   // close the settings.
@@ -384,6 +384,26 @@
   self.googleServicesSettingsCoordinator = nil;
 }
 
+// Shows the saved passwords and starts the password check is
+// |startPasswordCheck| is true.
+- (void)showSavedPasswordsAndStartPasswordCheck:(BOOL)startPasswordCheck {
+  self.savedPasswordsCoordinator = [[PasswordsCoordinator alloc]
+      initWithBaseNavigationController:self
+                               browser:self.browser];
+  self.savedPasswordsCoordinator.delegate = self;
+  [self.savedPasswordsCoordinator start];
+  if (startPasswordCheck) {
+    [self.savedPasswordsCoordinator checkSavedPasswords];
+  }
+}
+
+// Stops the underlying passwords coordinator if it exists.
+- (void)stopPasswordsCoordinator {
+  [self.savedPasswordsCoordinator stop];
+  self.savedPasswordsCoordinator.delegate = nil;
+  self.savedPasswordsCoordinator = nil;
+}
+
 #pragma mark - GoogleServicesSettingsCoordinatorDelegate
 
 - (void)googleServicesSettingsCoordinatorDidRemove:
@@ -392,6 +412,13 @@
   [self stopGoogleServicesSettingsCoordinator];
 }
 
+#pragma mark - PasswordsCoordinatorDelegate
+
+- (void)passwordsCoordinatorDidRemove:(PasswordsCoordinator*)coordinator {
+  DCHECK_EQ(self.savedPasswordsCoordinator, coordinator);
+  [self stopPasswordsCoordinator];
+}
+
 #pragma mark - UIAdaptivePresentationControllerDelegate
 
 - (BOOL)presentationControllerShouldDismiss:
@@ -526,19 +553,12 @@
 // TODO(crbug.com/779791) : Do not pass |baseViewController| through dispatcher.
 - (void)showSavedPasswordsSettingsFromViewController:
     (UIViewController*)baseViewController {
-  PasswordsTableViewController* controller =
-      [[PasswordsTableViewController alloc] initWithBrowser:self.browser];
-  controller.dispatcher = [self.settingsNavigationDelegate handlerForSettings];
-  [self pushViewController:controller animated:YES];
+  [self showSavedPasswordsAndStartPasswordCheck:NO];
 }
 
 - (void)showSavedPasswordsSettingsAndStartPasswordCheckFromViewController:
     (UIViewController*)baseViewController {
-  PasswordsTableViewController* controller =
-      [[PasswordsTableViewController alloc] initWithBrowser:self.browser];
-  controller.dispatcher = [self.settingsNavigationDelegate handlerForSettings];
-  [controller startPasswordCheck];
-  [self pushViewController:controller animated:YES];
+  [self showSavedPasswordsAndStartPasswordCheck:YES];
 }
 
 // TODO(crbug.com/779791) : Do not pass |baseViewController| through dispatcher.
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index cfa1d1f..2fdd549 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -63,7 +63,7 @@
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_mediator.h"
 #import "ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_coordinator.h"
 #import "ios/chrome/browser/ui/settings/privacy/privacy_coordinator.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h"
 #import "ios/chrome/browser/ui/settings/search_engine_table_view_controller.h"
@@ -178,6 +178,7 @@
     GoogleServicesSettingsCoordinatorDelegate,
     IdentityManagerObserverBridgeDelegate,
     PasswordCheckObserver,
+    PasswordsCoordinatorDelegate,
     PopoverLabelViewControllerDelegate,
     PrefObserverDelegate,
     PrivacyCoordinatorDelegate,
@@ -225,6 +226,9 @@
   // Safety Check coordinator.
   SafetyCheckCoordinator* _safetyCheckCoordinator;
 
+  // Passwords coordinator.
+  PasswordsCoordinator* _passwordsCoordinator;
+
   // Cached resized profile image.
   UIImage* _resizedImage;
   __weak UIImage* _oldImage;
@@ -955,8 +959,7 @@
     case ItemTypePasswords:
       base::RecordAction(
           base::UserMetricsAction("Options_ShowPasswordManager"));
-      controller =
-          [[PasswordsTableViewController alloc] initWithBrowser:_browser];
+      [self showPasswords];
       break;
     case ItemTypeAutofillCreditCard:
       base::RecordAction(base::UserMetricsAction("AutofillCreditCardsViewed"));
@@ -1112,6 +1115,15 @@
   [_googleServicesSettingsCoordinator start];
 }
 
+- (void)showPasswords {
+  DCHECK(!_passwordsCoordinator);
+  _passwordsCoordinator = [[PasswordsCoordinator alloc]
+      initWithBaseNavigationController:self.navigationController
+                               browser:_browser];
+  _passwordsCoordinator.delegate = self;
+  [_passwordsCoordinator start];
+}
+
 // Shows Safety Check Screen.
 - (void)showSafetyCheck {
   DCHECK(!_safetyCheckCoordinator);
@@ -1337,6 +1349,10 @@
   [_safetyCheckCoordinator stop];
   _safetyCheckCoordinator = nil;
 
+  [_passwordsCoordinator stop];
+  _passwordsCoordinator.delegate = nil;
+  _passwordsCoordinator = nil;
+
   [_privacyCoordinator stop];
   _privacyCoordinator = nil;
 
@@ -1539,6 +1555,15 @@
   _safetyCheckCoordinator = nil;
 }
 
+#pragma mark - SafetyCheckCoordinatorDelegate
+
+- (void)passwordsCoordinatorDidRemove:(PasswordsCoordinator*)coordinator {
+  DCHECK_EQ(_passwordsCoordinator, coordinator);
+  [_passwordsCoordinator stop];
+  _passwordsCoordinator.delegate = nil;
+  _passwordsCoordinator = nil;
+}
+
 #pragma mark - PrivacyCoordinatorDelegate
 
 - (void)privacyCoordinatorViewControllerWasRemoved:
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index d823234..9538c02 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -79,6 +79,7 @@
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/password",
+    "//ios/chrome/browser/ui/settings/password:password_ui",
     "//ios/chrome/browser/ui/settings/password:test_support",
     "//ios/chrome/browser/ui/tab_grid",
     "//ios/chrome/browser/ui/tabs",
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
index 370884e..56b6cfe9 100644
--- a/ios/chrome/test/app/password_test_util.mm
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -76,8 +76,8 @@
   PasswordsTableViewController* passwords_table_view_controller =
       base::mac::ObjCCastStrict<PasswordsTableViewController>(
           settings_navigation_controller.topViewController);
-  [passwords_table_view_controller
-      setReauthenticationModuleForExporter:mock_reauthentication_module];
+  passwords_table_view_controller.reauthenticationModule =
+      mock_reauthentication_module;
   return mock_reauthentication_module;
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index f25c107..f3cc6d6 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -702,7 +702,8 @@
   variations::VariationsIdsProvider* provider =
       variations::VariationsIdsProvider::GetInstance();
   std::vector<variations::VariationID> ids = provider->GetVariationsVector(
-      variations::GOOGLE_WEB_PROPERTIES_ANY_CONTEXT);
+      {variations::GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
+       variations::GOOGLE_WEB_PROPERTIES_FIRST_PARTY});
   return std::find(ids.begin(), ids.end(), variationID) != ids.end();
 }
 
@@ -710,7 +711,8 @@
   variations::VariationsIdsProvider* provider =
       variations::VariationsIdsProvider::GetInstance();
   std::vector<variations::VariationID> ids = provider->GetVariationsVector(
-      variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT);
+      {variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
+       variations::GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY});
   return std::find(ids.begin(), ids.end(), variationID) != ids.end();
 }
 
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h b/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
index be398150..a929c62f 100644
--- a/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 
 class ChromeBrowserState;
+@class DiscoverFeedMetricsRecorder;
 
 // Configuration object used by the DiscoverFeedProvider.
 @interface DiscoverFeedConfiguration : NSObject
@@ -15,6 +16,9 @@
 // BrowserState used by DiscoverFeedProvider;
 @property(nonatomic, assign) ChromeBrowserState* browserState;
 
+// DiscoverFeed metrics recorder used by DiscoverFeedProvider;
+@property(nonatomic, strong) DiscoverFeedMetricsRecorder* metricsRecorder;
+
 @end
 
 #endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_CONFIGURATION_H_
diff --git a/ios/web/navigation/wk_navigation_util_unittest.mm b/ios/web/navigation/wk_navigation_util_unittest.mm
index 12a33fe..16c714d6 100644
--- a/ios/web/navigation/wk_navigation_util_unittest.mm
+++ b/ios/web/navigation/wk_navigation_util_unittest.mm
@@ -197,8 +197,6 @@
   // Extract session JSON from restoration URL.
   base::JSONReader::ValueWithError value_with_error =
       ExtractSessionDict(restore_session_url);
-  ASSERT_EQ(base::ValueDeserializer::kErrorCodeNoError,
-            value_with_error.error_code);
   ASSERT_TRUE(value_with_error.value.has_value());
 
   // Verify that all titles and URLs are present.
@@ -233,8 +231,6 @@
   // Extract session JSON from restoration URL.
   base::JSONReader::ValueWithError value_with_error =
       ExtractSessionDict(restore_session_url);
-  ASSERT_EQ(base::ValueDeserializer::kErrorCodeNoError,
-            value_with_error.error_code);
   ASSERT_TRUE(value_with_error.value.has_value());
 
   // Verify that first kMaxSessionSize titles and URLs are present.
@@ -279,8 +275,6 @@
   // Extract session JSON from restoration URL.
   base::JSONReader::ValueWithError value_with_error =
       ExtractSessionDict(restore_session_url);
-  ASSERT_EQ(base::ValueDeserializer::kErrorCodeNoError,
-            value_with_error.error_code);
   ASSERT_TRUE(value_with_error.value.has_value());
 
   // Verify that last kMaxSessionSize titles and URLs are present.
@@ -326,8 +320,6 @@
   // Extract session JSON from restoration URL.
   base::JSONReader::ValueWithError value_with_error =
       ExtractSessionDict(restore_session_url);
-  ASSERT_EQ(base::ValueDeserializer::kErrorCodeNoError,
-            value_with_error.error_code);
   ASSERT_TRUE(value_with_error.value.has_value());
 
   // Verify that last kMaxSessionSize titles and URLs are present.
diff --git a/ios/web/webui/mojo_facade.mm b/ios/web/webui/mojo_facade.mm
index c11b9c1..9014994 100644
--- a/ios/web/webui/mojo_facade.mm
+++ b/ios/web/webui/mojo_facade.mm
@@ -81,8 +81,6 @@
                                                     base::JSON_PARSE_RFC);
   CHECK(value_with_error.value);
   CHECK(value_with_error.value->is_dict());
-  CHECK_EQ(base::ValueDeserializer::kErrorCodeNoError,
-           value_with_error.error_code);
 
   const std::string* name = value_with_error.value->FindStringKey("name");
   CHECK(name);
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
index 7073f0c..460c56e 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -11,7 +11,6 @@
 #include <stdint.h>
 
 #include "base/location.h"
-#include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -31,6 +30,22 @@
 
 constexpr int kTimeToWaitBeforeStoppingStillImageCaptureInSeconds = 60;
 
+base::TimeDelta GetCMSampleBufferTimestamp(CMSampleBufferRef sampleBuffer) {
+  const CMTime cm_timestamp =
+      CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+  const base::TimeDelta timestamp =
+      CMTIME_IS_VALID(cm_timestamp)
+          ? base::TimeDelta::FromSecondsD(CMTimeGetSeconds(cm_timestamp))
+          : media::kNoTimestamp;
+  return timestamp;
+}
+
+std::string MacFourCCToString(OSType fourcc) {
+  char arr[] = {fourcc >> 24, (fourcc >> 16) & 255, (fourcc >> 8) & 255,
+                fourcc & 255, 0};
+  return arr;
+}
+
 }  // anonymous namespace
 
 @implementation VideoCaptureDeviceAVFoundation
@@ -120,6 +135,7 @@
     return NO;
   }
   [_captureVideoDataOutput setAlwaysDiscardsLateVideoFrames:true];
+
   [_captureVideoDataOutput
       setSampleBufferDelegate:self
                         queue:dispatch_get_global_queue(
@@ -157,6 +173,9 @@
     best_fourcc = kCMPixelFormat_422YpCbCr8;
   }
 
+  VLOG(2) << __func__ << ": configuring '" << MacFourCCToString(best_fourcc)
+          << "' " << width << "x" << height << "@" << frameRate;
+
   // The capture output has to be configured, despite Mac documentation
   // detailing that setting the sessionPreset would be enough. The reason for
   // this mismatch is probably because most of the AVFoundation docs are written
@@ -409,22 +428,69 @@
   }
 }
 
-// |captureOutput| is called by the capture device to deliver a new frame.
-// AVFoundation calls from a number of threads, depending on, at least, if
-// Chrome is on foreground or background.
-- (void)captureOutput:(AVCaptureOutput*)captureOutput
-    didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
-           fromConnection:(AVCaptureConnection*)connection {
+- (void)processRamSample:(CMSampleBufferRef)sampleBuffer
+             baseAddress:(const void*)baseAddress
+               frameSize:(size_t)frameSize
+         pixelFormatType:(OSType)pixelFormat {
+  VLOG(3) << __func__ << ": format: " << MacFourCCToString(pixelFormat);
   const CMFormatDescriptionRef formatDescription =
       CMSampleBufferGetFormatDescription(sampleBuffer);
-  const FourCharCode fourcc =
-      CMFormatDescriptionGetMediaSubType(formatDescription);
   const CMVideoDimensions dimensions =
       CMVideoFormatDescriptionGetDimensions(formatDescription);
   const media::VideoCaptureFormat captureFormat(
       gfx::Size(dimensions.width, dimensions.height), _frameRate,
-      media::FourCCToChromiumPixelFormat(fourcc));
-  gfx::ColorSpace colorSpace;
+      media::FourCCToChromiumPixelFormat(pixelFormat));
+  base::TimeDelta timestamp = GetCMSampleBufferTimestamp(sampleBuffer);
+  base::AutoLock lock(_lock);
+  if (_frameReceiver && baseAddress) {
+    gfx::ColorSpace colorSpace;
+    // TODO(julien.isorce): move GetImageBufferColorSpace(CVImageBufferRef)
+    // from media::VTVideoDecodeAccelerator to media/base/mac and call it
+    // here to get the color space. See https://crbug.com/959962.
+    // colorSpace = media::GetImageBufferColorSpace(videoFrame);
+    _frameReceiver->ReceiveFrame(reinterpret_cast<const uint8_t*>(baseAddress),
+                                 frameSize, captureFormat, colorSpace, 0, 0,
+                                 timestamp);
+  }
+}
+
+- (void)processRawSample:(CMSampleBufferRef)sampleBuffer {
+  VLOG(3) << __func__;
+  // Trust |_frameReceiver| to do decompression.
+  char* baseAddress = 0;
+  size_t frameSize = 0;
+  media::ExtractBaseAddressAndLength(&baseAddress, &frameSize, sampleBuffer);
+  [self processRamSample:sampleBuffer
+             baseAddress:baseAddress
+               frameSize:frameSize
+         pixelFormatType:CMFormatDescriptionGetMediaSubType(
+                             CMSampleBufferGetFormatDescription(sampleBuffer))];
+}
+
+- (void)processSample:(CMSampleBufferRef)sampleBuffer
+      withImageBuffer:(CVImageBufferRef)videoFrame {
+  if (CVPixelBufferLockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly) !=
+      kCVReturnSuccess) {
+    return [self processRawSample:sampleBuffer];
+  }
+  void* baseAddress =
+      static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame));
+  size_t frameSize = CVPixelBufferGetHeight(videoFrame) *
+                     CVPixelBufferGetBytesPerRow(videoFrame);
+  [self processRamSample:sampleBuffer
+             baseAddress:baseAddress
+               frameSize:frameSize
+         pixelFormatType:CVPixelBufferGetPixelFormatType(videoFrame)];
+  CVPixelBufferUnlockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly);
+}
+
+// |captureOutput| is called by the capture device to deliver a new frame.
+// Since the callback is configured to happen on a global dispatch queue, calls
+// may enter here concurrently and on any thread.
+- (void)captureOutput:(AVCaptureOutput*)captureOutput
+    didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+           fromConnection:(AVCaptureConnection*)connection {
+  VLOG(3) << __func__;
 
   // We have certain format expectation for capture output:
   // For MJPEG, |sampleBuffer| is expected to always be a CVBlockBuffer.
@@ -433,51 +499,12 @@
   // plugins/virtual cameras. In order to find out whether it is CVBlockBuffer
   // or CVImageBuffer we call CMSampleBufferGetImageBuffer() and check if the
   // return value is nil.
-  char* baseAddress = 0;
-  size_t frameSize = 0;
-  CVImageBufferRef videoFrame = nil;
-  if (fourcc != kCMVideoCodecType_JPEG_OpenDML) {
-    videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
-    // Lock the frame and calculate frame size.
-    if (videoFrame &&
-        CVPixelBufferLockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly) ==
-            kCVReturnSuccess) {
-      baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame));
-      frameSize = CVPixelBufferGetHeight(videoFrame) *
-                  CVPixelBufferGetBytesPerRow(videoFrame);
-
-      // TODO(julien.isorce): move GetImageBufferColorSpace(CVImageBufferRef)
-      // from media::VTVideoDecodeAccelerator to media/base/mac and call it
-      // here to get the color space. See https://crbug.com/959962.
-      // colorSpace = media::GetImageBufferColorSpace(videoFrame);
-    } else {
-      videoFrame = nil;
-    }
+  CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
+  if (videoFrame) {
+    [self processSample:sampleBuffer withImageBuffer:videoFrame];
+  } else {
+    [self processRawSample:sampleBuffer];
   }
-  if (!videoFrame) {
-    media::ExtractBaseAddressAndLength(&baseAddress, &frameSize, sampleBuffer);
-  }
-
-  {
-    base::AutoLock lock(_lock);
-    const CMTime cm_timestamp =
-        CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
-    const base::TimeDelta timestamp =
-        CMTIME_IS_VALID(cm_timestamp)
-            ? base::TimeDelta::FromMicroseconds(
-                  cm_timestamp.value * base::TimeTicks::kMicrosecondsPerSecond /
-                  cm_timestamp.timescale)
-            : media::kNoTimestamp;
-
-    if (_frameReceiver && baseAddress) {
-      _frameReceiver->ReceiveFrame(reinterpret_cast<uint8_t*>(baseAddress),
-                                   frameSize, captureFormat, colorSpace, 0, 0,
-                                   timestamp);
-    }
-  }
-
-  if (videoFrame)
-    CVPixelBufferUnlockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly);
 }
 
 - (void)onVideoError:(NSNotification*)errorNotification {
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
index 0a9b3073..a15cf13 100644
--- a/mojo/public/cpp/bindings/README.md
+++ b/mojo/public/cpp/bindings/README.md
@@ -1159,7 +1159,7 @@
 interface which itself already has a primary interface.
 
 If you want to test an associated interface endpoint without first
-associating it, you can use `AssociatedRemote::BindNewEndpointAndPassDedicatedReceiverForTesting`.
+associating it, you can use `AssociatedRemote::BindNewEndpointAndPassDedicatedReceiver`.
 This will create working associated interface endpoints which are not actually
 associated with anything else.
 
diff --git a/mojo/public/cpp/bindings/associated_receiver.h b/mojo/public/cpp/bindings/associated_receiver.h
index f2857dc..f03b37d 100644
--- a/mojo/public/cpp/bindings/associated_receiver.h
+++ b/mojo/public/cpp/bindings/associated_receiver.h
@@ -145,8 +145,8 @@
   //
   // For testing, where the returned request is bound to e.g. a mock and there
   // are no other interfaces involved.
-  PendingAssociatedRemote<Interface>
-  BindNewEndpointAndPassDedicatedRemoteForTesting() WARN_UNUSED_RESULT {
+  PendingAssociatedRemote<Interface> BindNewEndpointAndPassDedicatedRemote()
+      WARN_UNUSED_RESULT {
     DCHECK(!is_bound()) << "AssociatedReceiver is already bound";
 
     MessagePipe pipe;
diff --git a/mojo/public/cpp/bindings/associated_remote.h b/mojo/public/cpp/bindings/associated_remote.h
index 32b010c..d8e09bc9 100644
--- a/mojo/public/cpp/bindings/associated_remote.h
+++ b/mojo/public/cpp/bindings/associated_remote.h
@@ -180,8 +180,8 @@
   //
   // For testing, where the returned request is bound to e.g. a mock and there
   // are no other interfaces involved.
-  PendingAssociatedReceiver<Interface>
-  BindNewEndpointAndPassDedicatedReceiverForTesting() WARN_UNUSED_RESULT {
+  PendingAssociatedReceiver<Interface> BindNewEndpointAndPassDedicatedReceiver()
+      WARN_UNUSED_RESULT {
     MessagePipe pipe;
     scoped_refptr<internal::MultiplexRouter> router0 =
         new internal::MultiplexRouter(
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 1e36aded..6d1592f 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -7601,7 +7601,6 @@
     { "name": "riaucybersolution.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rk6.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "royzez.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "rtd.uk.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "runtondev.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "safcstore.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sakaserver.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17235,7 +17234,6 @@
     { "name": "exehack.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "frolova.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "firebounty.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "fistu.la", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "foo.hamburg", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "frolov.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fushee.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24423,7 +24421,6 @@
     { "name": "openrainbow.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "openspace.xxx", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "opiates.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "opiates.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "opin.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "opioids.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "opioids.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -32084,7 +32081,6 @@
     { "name": "telos-analytics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "test.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "the-body-shop.hu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "thestoryshack.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "timco.cloud", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tsurezurematome.ga", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "type1joe.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -39201,7 +39197,6 @@
     { "name": "api.biz.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "appeldorn.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "appliancerepairlosangeles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "aqualysis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "archmediamarketing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "area4pro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "artificial.army", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42626,7 +42621,6 @@
     { "name": "linext.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linkat4.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lk-hardware.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "localblitz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lsws.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lukas-gorr.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luminaires-online.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45495,7 +45489,6 @@
     { "name": "silke-hunde.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "silvacor-ziegel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "simplecoding.click", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sitebuilderreport.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sitehome.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sitesource101.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sitevandaag.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45949,7 +45942,6 @@
     { "name": "gdprhallofshame.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gepps.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "getprivacy.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "getprivacy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gfe.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gfe.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gfelite.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46173,7 +46165,6 @@
     { "name": "mpa-pro.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mtasa.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "musicompare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "muzhijy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "my-stuff-online.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mygigabitnation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "myjudo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47465,7 +47456,6 @@
     { "name": "full-stack.ninja", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fyretrine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "g0881.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "garbomuffin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "getweloop.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gippert-klein.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gogroopie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -48515,7 +48505,6 @@
     { "name": "zlaty-tyden.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zlatytyden.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zq789.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "1288fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "12photos.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "20zq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "319k3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50094,7 +50083,6 @@
     { "name": "antiekboerderijgraafland.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "apo-deutschland.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aquelarreweb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "arnevankauter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arose.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "atelierfantazie.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "austinlockout.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50618,7 +50606,6 @@
     { "name": "udancy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uhssl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uniontestprep.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "uptakedigital.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "upwardtraining.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "upyourfinances.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uraimo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -51740,7 +51727,6 @@
     { "name": "rochakhand-knitcraft.com.np", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rocketsandtutus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "roninitconsulting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "rosnertexte.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "roundtablekzn.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "royal806.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "royal810.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -53055,7 +53041,6 @@
     { "name": "elielaloum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eligibilis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "epicdowney.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "esc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "estetistarimini.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "estudio21pattern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eurodentaire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -55613,7 +55598,6 @@
     { "name": "interguard.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "interlijn.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "invoicehippo.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "inwao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irgwebsites.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irish.radio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irishradioplayer.radio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58584,7 +58568,6 @@
     { "name": "openstandia.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "opportunityliu.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "optimaner.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ordoh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ore.cool", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "osolutionscorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ostachstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58604,7 +58587,6 @@
     { "name": "panamatrippin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paniodpolskiego.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paragontasarim.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "partyshop.ge", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "parys.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "patrykwegrzynek.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pause-canap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59213,7 +59195,6 @@
     { "name": "miamaibaum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mifibra.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mindsetatx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mistaken.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mjpak.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "moecraft.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mora.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59770,7 +59751,6 @@
     { "name": "dadadani.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "danads.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dapianw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dare.deals", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "darf.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dead-letter.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deepspace4.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -61105,7 +61085,6 @@
     { "name": "sipyuru.lk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sklepvoip.tel", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skremovals.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "smits.frl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "somethingsomething.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sophiahatstudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "speakersbusiness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63139,7 +63118,6 @@
     { "name": "acupunturamadrid.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "acupunturavalencia.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adamsasphaltpaving.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "administratie-smits.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ae86sb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aero.parts", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ag88.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -68449,7 +68427,6 @@
     { "name": "italiataxi.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itaporanga.se.gov.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itmx.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "izs8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "japanese-cuisine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jecurranpc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jobit.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -68500,7 +68477,6 @@
     { "name": "k88801.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88870.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k88891.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k89188.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k89388.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k89999.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k8dc01.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -68540,7 +68516,6 @@
     { "name": "lc8guidance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md03.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc8md30.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc98.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9852.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc9910.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lecheng.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79070,39 +79045,6 @@
     { "name": "00168365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "00228555.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "00228999.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228bb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228cc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228d.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228dd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228ee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228gg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228hh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228jj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228kk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228m.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228mm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228pp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228rr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228s.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228ss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228tt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228v.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228vip1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "00228vip3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "00365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "01365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "02365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79121,7 +79063,6 @@
     { "name": "1111365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "111365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "11168365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "1119968.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "11365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "123365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "12365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79237,80 +79178,15 @@
     { "name": "89365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "96002e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "99365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968131.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968166.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968282.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968363.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968389.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968393.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968404.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968505.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968508.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968535.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968595.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968600.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968656.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968707.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968717.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968838.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968959.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968969.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968aaa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968abc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968app.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968bbb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968caipiao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968cc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ccc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968com.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968dd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ddd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968eee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968ff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968fff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968gg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ggg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968go.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968hh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968hhh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968iii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968jj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968jjj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968lll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968mm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968mmm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968nnn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ok.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968oo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ooo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968pp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ppp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968qq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968qqq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968rr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968rrr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968sss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ttt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968uu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968uuu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968vv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968vvv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968www.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9968xl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968xx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968xxx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968yy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968yyy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968zz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968zzz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "999321365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "999365t.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "99qp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79563,7 +79439,6 @@
     { "name": "geohoney.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "getbookked.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "getsmarterinsurance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "go9968.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "goc4wraps.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "grand-city38.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "grimm.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79677,7 +79552,6 @@
     { "name": "penconsultants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pj1100.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "planhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "platform39.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "playinfinityvr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79824,7 +79698,6 @@
     { "name": "x00738.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "x00776.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "x00786.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "x668.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "x9016.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xinnermedia.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "y68aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80151,7 +80024,6 @@
     { "name": "interstateremovalists.sydney", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "inu.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "investigatore.torino.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "itvaatlik.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ixit.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jaisiam.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jamesusandra.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81017,7 +80889,6 @@
     { "name": "bet444423.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bet444427.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bet444428.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bet444429.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bet444430.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "biasmath.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "biggles.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81289,7 +81160,6 @@
     { "name": "lc040.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc18.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc1800.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lc1818.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2121g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc245.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lc2500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -82725,8 +82595,6 @@
     { "name": "2habc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "5icsb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9118.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968.ag", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9968.love", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9jabase.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a-tes-cotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "abdelaliezzyn.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -83025,7 +82893,6 @@
     { "name": "mdclass.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "me7878.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "medasset.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "meganruggiero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "metanumbers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "metzgermark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mi1k.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -83159,7 +83026,6 @@
     { "name": "signup.ly", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "siteweb-seo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "siwek.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "skilloutlook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skynetstores.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "slymak.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "snowrippers.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -83477,7 +83343,6 @@
     { "name": "bamboehof.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bao-in.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bao-in.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bapha.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "basel-gynaecology.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "basel-gynaekologie.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "basilsys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85411,7 +85276,6 @@
     { "name": "p82365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "panamarealestatebrokers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "parkercs.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "parketdoska.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "patentchallenges.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paulineetaugustin.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pcrabme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87077,14 +86941,6 @@
     { "name": "svrx.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swiftcom.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sys21.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59970.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59971.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59972.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59973.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59983.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59984.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59985.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "t59986.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "talesbazaar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tattooli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tax-brain.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -87400,7 +87256,6 @@
     { "name": "ekogroszekpieklo.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "empowersimcoe.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ergowish.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "espirituracer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "estudioaguiar.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eva42.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "extremebros.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -90568,7 +90423,6 @@
     { "name": "caletka.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "campona.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cancer-rose.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cantical.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cardiosportsilvinadelgado.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cardloan-center.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "careercapital.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -93481,7 +93335,6 @@
     { "name": "zaferaniehearing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zamor.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zeglujemy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zhoujianghan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zivver.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0x12.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "0x22.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -94913,7 +94766,6 @@
     { "name": "libhttp.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "libpdf.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "libscpi.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lig.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linocomm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linocomm.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linocomm.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -98992,7 +98844,6 @@
     { "name": "imoxin.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "imperiyashop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "impossiblechoisir.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "impra.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "imprezer.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "impudence.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "in-books.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -105177,7 +105028,6 @@
     { "name": "50.gy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "528sss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "557bbb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "722sss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "795sss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "991ccc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aaflalo.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108323,7 +108173,6 @@
     { "name": "optitaxes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "orcz.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "orsal.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "osorio.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "our-store.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ouroboros.world", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p3.marketing", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108594,7 +108443,6 @@
     { "name": "yvonne-stingel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yy30019.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z30019.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zaigar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "znfinnews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zorox.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zporno.porn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108677,7 +108525,6 @@
     { "name": "carolinelanthier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "casaruralcincoleyendas.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "casasincreibles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "catpic.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cccanna.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "celebrate-creativity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "celebritypictures.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108759,7 +108606,6 @@
     { "name": "etbtoursegypt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eteachbd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "europeanbizhealthcare.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "exbasi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "extrasauber.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "extrasauber.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "extrasauber.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -109123,7 +108969,6 @@
     { "name": "wolrdwidessl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wordwidessl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "worldwidessl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wpboys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wxw.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xanzhu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xgys.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110384,7 +110229,6 @@
     { "name": "katerinastudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kathrin-maren-enders.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kgky.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kidan.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kimino-school.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kinetikos.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kiseki.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110673,7 +110517,6 @@
     { "name": "zotan.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zotan.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "17avolemsaberlaveritat.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "2q.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "2vp-an.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "360kuvia.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "360prokuvat.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110829,7 +110672,6 @@
     { "name": "emxvn.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "encontro.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "entropyofdelicatewonders.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "epitafija.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "erpollo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "erysonhandel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eslove.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110945,7 +110787,6 @@
     { "name": "kra2laiz.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kratom-k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kryptoforce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kuraga.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "la-vraie-histoire.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "laby.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lacatta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110958,7 +110799,6 @@
     { "name": "liveteachers.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "logopaedie-sandkrug.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "looop.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "loukkos.ma", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lovegpl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lovemoon.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lowcarbspark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111056,7 +110896,6 @@
     { "name": "poezja.art", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "positivefocus.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "postex.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pqd.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "principalstest.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "problempaws.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "processout.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111240,7 +111079,6 @@
     { "name": "3sixtydutyfree.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "604windswell.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "70mpg.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "8800.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8885asknick.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "99laptops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aboutyou.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111395,7 +111233,6 @@
     { "name": "draft.cards", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drivehub.win", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drrenointerior.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dymovskiy.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ecocleanpower.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eggbay.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eldiedesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111512,7 +111349,6 @@
     { "name": "intiveo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "investors.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ireta.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "iritual.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irmo.hr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ironbarnyc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isharryworking.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111969,7 +111805,6 @@
     { "name": "freebasics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "funtimeusabiloxi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gabe.download", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gabrielgroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gamecss.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ganardinerotrabajandoporinternet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ganglioslinfaticos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112474,7 +112309,6 @@
     { "name": "bivi.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blackforlife.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bloggingsaif.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bmzm.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bopyx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "boringnews.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bracebridgechiro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -113929,7 +113763,6 @@
     { "name": "ngpest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nigelvm.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nils.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "nocoffeetech.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "noel.wf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "noel.yt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "notisecit.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114144,7 +113977,6 @@
     { "name": "whyfeedthegreed.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wisemoney.com.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wpshop.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wulel.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--grnstrm-r1ae.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yaencasa.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yc1820.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114639,7 +114471,6 @@
     { "name": "thewest.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thumbnail.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tianbo1988.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tietsikka.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tigernero.duckdns.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tipslab.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tlctrades.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116010,7 +115841,6 @@
     { "name": "mannhaarkunst.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marcannmentalhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marco-s.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "margmusic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "markisa.ninja", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mathavuzteknolojileri.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maxopolyworldnews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116233,7 +116063,6 @@
     { "name": "wildanfauzy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wondercorner.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "woodward-vets.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wpnesia.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wvhin.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xemcanho.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xiahdeh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116310,7 +116139,6 @@
     { "name": "aircomet.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "airfield.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "airtel.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "aitci.com.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ajitp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "akepayu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "akinavn.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116897,7 +116725,6 @@
     { "name": "laboiteasous.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "labworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lacnesidlo.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lad-china.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ladige.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lamergameryt.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lanekoll.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -117860,7 +117687,6 @@
     { "name": "tianyis.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tiga-design.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "timmerbedrijfpauel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "toanz.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tobostop.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tog.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "togoweed.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -118041,7 +117867,6 @@
     { "name": "eggy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eichendorffschule.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elbrus360.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "elftoy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elixirbih.ba", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elypia.art", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elypia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -118299,7 +118124,6 @@
     { "name": "smartzona.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smokycigars.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "socialplanet.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "softyak.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "somosgesath.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "soydxn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sparklewindowcleaners.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -118402,6 +118226,482 @@
     { "name": "zorgenvoorandrea.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zwofroue.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zyciedirect.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "12grid.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "18onlypixel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "18onlypixels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2dk.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "381485.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4927a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4bengineering.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "817209.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abazola.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "actdigital.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adiraku.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agaclinicaltrials.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agent-imobiliar.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agespisa.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ahenkerp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "airportal.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "albertonplumber.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alexandrastrauss.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "allenturley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "americanitpartners.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amgreatness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andreaalloway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anhcuti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "antoniojr.adv.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anywhereworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "apsrustandtint.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arcari.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arthousecarousel.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aruo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asdainfomanager.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "askmetutoring.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asko.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atc-fr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atlascloud.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "avatype.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "awsl.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b0x0.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "b2b-leads.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "balicyclingtours.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bamaland.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "banffcanmorespeedskating.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "barkingmadpetproducts.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "berthaundcarlbenzpreis.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "best-tickets.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bianyanan.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bioghalm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bitbincomputers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blazebd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blivdj.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluebirdjc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bobvincent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boingboing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bootspropertycentral.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boris64.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "browfai.casa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "busqueautonomo.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cafeferrovia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "calaix.click", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "candlemakingexplained.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "capybaraowner.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carl.land", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carlcsaposs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caspianrentcar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ccsaposs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cecil.coop", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cgmbacklot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cgps.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chaise-de-gamer.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chargifi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "charlescwcooke.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cidgomes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cielo-thefilm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cityacademyslc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cocreaciones.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conceptcompany.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conscientia.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conservadoraembh.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "covid19dataportal.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cptechsupport.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cr-it.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "craigsaper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "criandosites.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "csaposs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "curaprox.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "czteryporyroku.edu.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dakotacil.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danielittlewood.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "darcyinspired.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "denizsartdiary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "designconformitylibrary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "desiskinscience.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dieselfiltersonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digi-tec.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dijitalzade.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "direct.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "directeca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diysonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "djanemagbrasil.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dlgraphic.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doganoglu.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doggo-staging.herokuapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dovecraft.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drgregroberts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dsorter.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dugwood.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dysautonomia-postsyndrome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edgeless.pp.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "egm-sakura.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elkhalillaw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emeetattd.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eniwa-eye.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "enmieux.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ersbodabilvard.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "erzaehlwerkstatt-heilbronn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "espcafe.uno", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "etoile-rc.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evbn.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "excelbroadcast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip114.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip115.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip116.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip117.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip118.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip13.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip14.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip16.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip17.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip18.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip29.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip30.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip31.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip32.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip33.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "f88vip34.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fairtrade.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fattoriabio.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fidesic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "files.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "finsify.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "firmfunding.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fitchdesigncompany.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fitnessplanet.best", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flybis.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "forumofld.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "francoislaude.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "franfoto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frbg.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frenchbluecottage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frosty.style", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "futa.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "galacticaos.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gatemotorsmidrand.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gatemotorssandton.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gazvangaz.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "genzi.win", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "germain.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "germaintechnologies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gogem.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gogemini.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goldnull.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goosementor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gravelshooters.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "groupdcc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grupoune.com.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hacerjabon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "handy.lc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hardnode.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heinrich5991.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hermofit.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hoanghaiauto.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hostinkos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hoymedivorcio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "i2capmark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ichtushosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "idigovs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ihgcontrolbook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ineedweb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infopreneur.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "instrumentalverein-tueddern.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "internetpoem.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inwerx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iqtek.solutions", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iriscddg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ivetdata.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jaredkunz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jave.asia", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jetlaggroup.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jiaxitian.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "johyn.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jollytotschildminder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joshuakin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "julian.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jwbworks.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kapilarya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kdcinfo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "keskikorpimotorsport.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ketopower.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "khakiblossom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kibrit.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kmassociations.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "knowyour.place", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "konturconference.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koolisw.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kozansa.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "krenstetter.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kylese.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lacoccinelle.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lafsc.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lapeinturequichangetout.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ledgy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lepetitkids.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leshe.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leshetu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leskoalasenvoyage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lesportesdelascension.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lestrokeofgenius.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libremedia.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lidodecor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lifeconnections.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linux.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lmdev.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "logobravo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "love4taylor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luxuryhome.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luxuryitaly.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lyme-regis-accommodation.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "m1hax.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "madonnadellafibra.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "magodasredes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mandalatantra.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marielinepitre.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "masterpieceitaly.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matras.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matrixbricks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maxmaharashtra.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mayflowerfairytales.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mcsdatum.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mdcallianceparty.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mdnailspa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medicardlimited.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meetanshi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "megalogi.ma", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meinevorlagen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mengma.pub", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mepassport.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meupatrocinio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "microl.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "midnightcity.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "midyatotantik.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "miku.bar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "milon-apps.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mindthe5.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mindthe5.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mineskopia.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minka.net.bo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "misakaloli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mizrahi-tefahot.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mkbd.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mobincube.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mobincube.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monarch.security", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "morganhome.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mostertadmin.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mpk-chayka.org.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mta-sts.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mudanzasjuniorh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mundoamatista.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mylover.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mynic.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myparadigm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mzrme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nanaimo.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nandonoire.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "napych.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nationalfleetparts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ndibba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neweggsoft.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newsunited.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nextsfd.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nibiru.com.uy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ninjacomputing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nirvana-esport.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nordaccount.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nozaka-k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "olivereats.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "only.lc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "openchronicles.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opticamasvision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "originalgyms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "owncloud.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paclease.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paganismguide.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paintlabcustom.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paperopedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pb-eatz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "peckandweis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "petersonchiropractic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "philis-oenologie.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pmnd.rs", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pogomate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "posoco.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "postofficenear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "poterscy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "powerspeaking.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "praizeej.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prepa-benjam.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "presly.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primark.guru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "primekinoshita.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "princess-vip-escort.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "privatmeet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "procalc.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "proconnectengenharia.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "project-merlin.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "promo.lc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "propertech.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "proshnotori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "proto.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psdfindia.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pswatcher.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "punkrockpsychology.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rabbitsstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raceevents.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raistrick.art", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rawbeautysource.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rdienclosures.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "re-presented.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rebull.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "recetasboricuas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "recursos.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reddcrypt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "registerforhappywellth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ricochet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rje-hub.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rndtool.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robben.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rsb.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sahccareers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sainokuni-eng.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "salasbanquetes.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sallycooke.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "samsreseller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sangobion.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schooldatasquad.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schreinerei-schwenk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "searx.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "securix.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sexdolls.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sftkey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shemsharples.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shinetsuamerica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shinetsusilicones.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shoplyft.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "signicat.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simplifixed.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sisters.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sl0.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "slopi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "socialeyesthailand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "solidariedadecultura.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "soloparaguas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "solvin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sosgate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "soundrelief.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "southmarengo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sova.st", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "specialtyjets.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spind.energy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spnsv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sportschoolgeelhoed.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sprawdz-nip.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ssasociety.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ssl.pink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ssl.red", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sslcentral.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stamit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strongprorealty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "suenotek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "swissdomaintrustee.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "symoteb.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tagalliances.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teccozed.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "technoyl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "techquintal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "techstartup.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tekmoloji.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tellet.tel", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tests-und-tipps.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tfsrcymru.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thealchemistatelier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theantarcticx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thecigarlibrary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theeuropeanlibrary.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thehomeofthefuture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thehorsesadvocate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "themevilles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thevirtuousdog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thewebguru.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tianjiaxi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ticketswap.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tiptoptransmissions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toepferei-langerwehe.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomik.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toptechs.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "totalsell.marketing", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tradition-immobilier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trigate.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "troqueladoras.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "truckscout24.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trydoggo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tureceta.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "turobot.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "twizzle.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tyroola.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uni-cleaner.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uni-watch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "upc-point.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vacacionestours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "valueourmind.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "valueourminds.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vanvanlines.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vashmatrass.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vineethavarma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vlice.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wadebet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wake.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wartabangsa.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "waukeshairon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wealthyspeakerschool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wearesuma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wemakeit.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whatiexpose.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wigglestudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "willflies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wisconsinhomemaker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wizardofvegas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wizdomonwheels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wmspropertyportal.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wobako.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wolfeco.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wrfalimentos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--80ah4f.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--soloatrapasueos-brb.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yakovmanshin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ymeadows.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yooand.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yourcfo.co.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "z3ven.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zachhay.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ziprecruiter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zmc.com.sa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zrobysama.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 6756dbe..f0871c2 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1654,6 +1654,15 @@
   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttState", state);
   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttReason", early_data_reason,
                             ssl_early_data_reason_max_value + 1);
+  if (IsGoogleHost(session_key_.host())) {
+    UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttReasonGoogle",
+                              early_data_reason,
+                              ssl_early_data_reason_max_value + 1);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ZeroRttReasonNonGoogle",
+                              early_data_reason,
+                              ssl_early_data_reason_max_value + 1);
+  }
 }
 
 void QuicChromiumClientSession::OnCryptoHandshakeMessageSent(
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index d74e286..0ad3ea2 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -284,14 +284,6 @@
 // If true, disable QUIC version h3-29.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_draft_29, false)
 
-// If true, improve Bbr2Sender::AdjustNetworkParameters by 1) do not inject a
-// bandwidth sample to the bandwidth filter, and 2) re-calculate pacing rate
-// after cwnd updated..
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_bbr2_improve_adjust_network_parameters,
-    true)
-
 // If true, try to coalesce packet of higher space with retransmissions to
 // mitigate RTT inflations.
 QUIC_FLAG(bool,
@@ -304,28 +296,11 @@
           FLAGS_quic_reloadable_flag_quic_record_received_min_ack_delay,
           false)
 
-// If true, QuicSession will no longer need streams_waiting_for_acks_.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_remove_streams_waiting_for_acks,
-          true)
-
-// When true, ParsedQuicVersionToString will print IETF drafts with format
-// draft29 instead of ff00001d.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_print_draft_version, true)
-
 // If true, disable blackhole detection on server side.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_disable_server_blackhole_detection,
           false)
 
-// Remove ACK_DECIMATION_WITH_REORDERING mode and fast_ack_after_quiescence
-// option in QUIC received packet manager.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_remove_unused_ack_options, true)
-
-// If true, QUIC subclasses will no longer directly access stream_map for its
-// content.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_do_not_use_stream_map, true)
-
 // If true,
 //   server accepts GOAWAY (draft-28 behavior),
 //   client receiving GOAWAY with stream ID that is not client-initiated
@@ -346,11 +321,6 @@
 // exists) after two PTOs.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_revert_mtu_after_two_ptos, true)
 
-// Simplify the ACK code in quic_received_packet_manager.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_simplify_received_packet_manager_ack,
-          true)
-
 // If true, when TLPR copt is used, enable half RTT as first PTO timeout.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_half_rtt_as_first_pto, true)
 
@@ -360,9 +330,6 @@
           FLAGS_quic_reloadable_flag_quic_enable_overshooting_detection,
           true)
 
-// If true, enable QUIC version h3-T051.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_t051, true)
-
 // If true, fix a case where data is marked lost in HANDSHAKE level but
 // HANDSHAKE key gets decrypted later.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_neuter_handshake_data, true)
@@ -462,3 +429,28 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail,
           true)
+
+// If true, use IETF QUIC application error codes in STOP_SENDING frames.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_stop_sending_uses_ietf_error_code,
+          false)
+
+// If true, QuicSpdySession's destructor won't need to do cleanup.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_clean_up_spdy_session_destructor,
+          true)
+
+// If true, discard INITIAL packet if the key has been dropped.
+QUIC_FLAG(
+    bool,
+    FLAGS_quic_reloadable_flag_quic_discard_initial_packet_with_key_dropped,
+    true)
+
+// If true, disable QUIC version h3-T051.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_t051, false)
+
+// If true, make sure there is pending timer credit when trying to PTO
+// retransmit any packets.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_fix_pto_pending_timer_count,
+          true)
diff --git a/net/quiche/common/platform/impl/DEPS b/net/quiche/common/platform/impl/DEPS
new file mode 100644
index 0000000..46f9ab7
--- /dev/null
+++ b/net/quiche/common/platform/impl/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  # This is a temporary rule to simplify migrating QUICHE to using Abseil
+  # directly.
+  # TODO(b/166325009): remove this rule.
+  "+third_party/abseil-cpp/absl/container/node_hash_map.h",
+]
diff --git a/net/quiche/common/platform/impl/quiche_unordered_containers_impl.h b/net/quiche/common/platform/impl/quiche_unordered_containers_impl.h
index fe48f20c..d08485f 100644
--- a/net/quiche/common/platform/impl/quiche_unordered_containers_impl.h
+++ b/net/quiche/common/platform/impl/quiche_unordered_containers_impl.h
@@ -7,20 +7,16 @@
 
 #include <unordered_map>
 
+#include "third_party/abseil-cpp/absl/container/node_hash_map.h"
+
 namespace quiche {
 
 // The default hasher used by hash tables.
 template <typename Key>
-using QuicheDefaultHasherImpl = std::hash<Key>;
+using QuicheDefaultHasherImpl = absl::Hash<Key>;
 
-template <typename Key,
-          typename Value,
-          typename Hash,
-          typename Eq =
-              typename std::unordered_map<Key, Value, Hash>::key_equal,
-          typename Alloc =
-              typename std::unordered_map<Key, Value, Hash>::allocator_type>
-using QuicheUnorderedMapImpl = std::unordered_map<Key, Value, Hash, Eq, Alloc>;
+template <typename Key, typename Value, typename Hash, typename Eq>
+using QuicheUnorderedMapImpl = absl::node_hash_map<Key, Value, Hash, Eq>;
 
 }  // namespace quiche
 
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index 5cffa0e0..24c34ff 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/check_op.h"
@@ -18,12 +19,15 @@
 #include "cc/paint/paint_canvas.h"
 #include "pdf/pdf_engine.h"
 #include "pdf/pdf_init.h"
+#include "pdf/pdfium/pdfium_engine.h"
 #include "pdf/ppapi_migration/url_loader.h"
+#include "ppapi/c/pp_errors.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-shared.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/web_associated_url_loader.h"
@@ -85,16 +89,28 @@
 
 }  // namespace
 
-PdfViewWebPlugin::PdfViewWebPlugin(const blink::WebPluginParams& params) {}
+PdfViewWebPlugin::PdfViewWebPlugin(const blink::WebPluginParams& params)
+    : initial_params_(params) {}
 
 PdfViewWebPlugin::~PdfViewWebPlugin() = default;
 
+// Modeled on `OutOfProcessInstance::Init()`.
 bool PdfViewWebPlugin::Initialize(blink::WebPluginContainer* container) {
   DCHECK_EQ(container->Plugin(), this);
   container_ = container;
 
+  std::string stream_url;
+  for (size_t i = 0; i < initial_params_.attribute_names.size(); ++i) {
+    if (initial_params_.attribute_names[i] == "stream-url")
+      stream_url = initial_params_.attribute_values[i].Utf8();
+  }
+
+  // Contents of `initial_params_` no longer needed.
+  initial_params_ = {};
+
   PerProcessInitializer::GetInstance().Acquire();
   InitializeEngine(/*enable_javascript=*/false);
+  LoadUrl(stream_url, /*is_print_preview=*/false);
   return true;
 }
 
@@ -225,9 +241,13 @@
 }
 
 void PdfViewWebPlugin::DocumentLoadComplete(
-    const PDFEngine::DocumentFeatures& document_features) {}
+    const PDFEngine::DocumentFeatures& document_features) {
+  NOTIMPLEMENTED();
+}
 
-void PdfViewWebPlugin::DocumentLoadFailed() {}
+void PdfViewWebPlugin::DocumentLoadFailed() {
+  NOTIMPLEMENTED();
+}
 
 pp::Instance* PdfViewWebPlugin::GetPluginInstance() {
   return nullptr;
@@ -287,9 +307,15 @@
   return loader;
 }
 
+// Modeled on `OutOfProcessInstance::DidOpen()`.
 void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader,
                                int32_t result) {
-  NOTIMPLEMENTED();
+  if (result == PP_OK) {
+    if (!engine()->HandleDocumentLoad(std::move(loader)))
+      DocumentLoadFailed();
+  } else {
+    NOTIMPLEMENTED();
+  }
 }
 
 void PdfViewWebPlugin::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h
index dec9811..cedc3b3 100644
--- a/pdf/pdf_view_web_plugin.h
+++ b/pdf/pdf_view_web_plugin.h
@@ -9,10 +9,10 @@
 #include "pdf/pdf_view_plugin_base.h"
 #include "pdf/ppapi_migration/url_loader.h"
 #include "third_party/blink/public/web/web_plugin.h"
+#include "third_party/blink/public/web/web_plugin_params.h"
 
 namespace blink {
 class WebPluginContainer;
-struct WebPluginParams;
 }  // namespace blink
 
 namespace chrome_pdf {
@@ -117,6 +117,7 @@
   // Call `Destroy()` instead.
   ~PdfViewWebPlugin() override;
 
+  blink::WebPluginParams initial_params_;
   blink::WebPluginContainer* container_ = nullptr;
 
   base::WeakPtrFactory<PdfViewWebPlugin> weak_factory_{this};
diff --git a/sandbox/policy/mac/gpu_v2.sb b/sandbox/policy/mac/gpu_v2.sb
index 543a2724..4d7222c 100644
--- a/sandbox/policy/mac/gpu_v2.sb
+++ b/sandbox/policy/mac/gpu_v2.sb
@@ -27,6 +27,7 @@
   (global-name "com.apple.PowerManagement.control")
   (global-name "com.apple.SecurityServer")
   (global-name "com.apple.system.notification_center")
+  (global-name "com.apple.system.opendirectoryd.membership") ; https://crbug.com/1126350#c5
   (global-name "com.apple.tsm.uiserver")
   (global-name "com.apple.windowserver.active")
 )
diff --git a/services/service_manager/embedder/BUILD.gn b/services/service_manager/embedder/BUILD.gn
index fbbf2166..2379f07f 100644
--- a/services/service_manager/embedder/BUILD.gn
+++ b/services/service_manager/embedder/BUILD.gn
@@ -12,7 +12,6 @@
     public = [
       "main.h",
       "main_delegate.h",
-      "process_type.h",
       "set_process_title.h",
       "shared_file_util.h",
     ]
diff --git a/services/service_manager/embedder/main.cc b/services/service_manager/embedder/main.cc
index 607618c..1c84ca5 100644
--- a/services/service_manager/embedder/main.cc
+++ b/services/service_manager/embedder/main.cc
@@ -35,13 +35,9 @@
 #include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "sandbox/policy/sandbox_type.h"
 #include "services/service_manager/embedder/main_delegate.h"
-#include "services/service_manager/embedder/process_type.h"
 #include "services/service_manager/embedder/set_process_title.h"
 #include "services/service_manager/embedder/shared_file_util.h"
 #include "services/service_manager/embedder/switches.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_executable/service_executable_environment.h"
-#include "services/service_manager/public/cpp/service_executable/switches.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
@@ -150,100 +146,6 @@
 #endif
 }
 
-void NonEmbedderProcessInit() {
-  logging::LoggingSettings settings;
-  settings.logging_dest =
-      logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
-  logging::InitLogging(settings);
-  // To view log output with IDs and timestamps use "adb logcat -v threadtime".
-  logging::SetLogItems(true,   // Process ID
-                       true,   // Thread ID
-                       true,   // Timestamp
-                       true);  // Tick count
-
-#if !defined(OFFICIAL_BUILD)
-  // Initialize stack dumping before initializing sandbox to make sure symbol
-  // names in all loaded libraries will be cached.
-  // NOTE: On Chrome OS, crash reporting for the root process and non-browser
-  // service processes is handled by the OS-level crash_reporter.
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableInProcessStackTraces)) {
-    base::debug::EnableInProcessStackDumping();
-  }
-#endif
-
-  base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
-      "ServiceManagerProcess");
-}
-
-int RunServiceManager(MainDelegate* delegate) {
-  NonEmbedderProcessInit();
-
-  base::SingleThreadTaskExecutor main_thread_task_executor(
-      base::MessagePumpType::UI);
-
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessagePumpType::IO, 0));
-  mojo::core::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
-
-  service_manager::BackgroundServiceManager background_service_manager(
-      delegate->GetServiceManifests());
-
-  base::RunLoop run_loop;
-  delegate->OnServiceManagerInitialized(run_loop.QuitClosure(),
-                                        &background_service_manager);
-  run_loop.Run();
-
-  ipc_thread.Stop();
-  base::ThreadPoolInstance::Get()->Shutdown();
-
-  return 0;
-}
-
-void InitializeResources() {
-  const std::string locale =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          ::switches::kLang);
-  // This loads the embedder's common resources (e.g. chrome_100_percent.pak for
-  // Chrome.)
-  ui::ResourceBundle::InitSharedInstanceWithLocale(
-      locale, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
-}
-
-int RunService(MainDelegate* delegate) {
-  NonEmbedderProcessInit();
-
-  InitializeResources();
-
-  service_manager::ServiceExecutableEnvironment environment;
-
-  base::SingleThreadTaskExecutor main_thread_task_executor(
-      base::MessagePumpType::UI);
-  base::RunLoop run_loop;
-
-  std::string service_name =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kServiceName);
-  if (service_name.empty()) {
-    LOG(ERROR) << "Service process requires --service-name";
-    return 1;
-  }
-
-  std::unique_ptr<Service> service =
-      delegate->CreateEmbeddedService(service_name);
-  if (!service) {
-    LOG(ERROR) << "Failed to start embedded service: " << service_name;
-    return 1;
-  }
-
-  service->set_termination_closure(run_loop.QuitClosure());
-  run_loop.Run();
-  return 0;
-}
-
 }  // namespace
 
 MainParams::MainParams(MainDelegate* delegate) : delegate(delegate) {}
@@ -256,7 +158,6 @@
 
   int exit_code = -1;
   base::debug::GlobalActivityTracker* tracker = nullptr;
-  ProcessType process_type = delegate->OverrideProcessType();
 #if defined(OS_MAC)
   std::unique_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool;
 #endif
@@ -336,10 +237,8 @@
     SetupSignalHandlers();
 #endif
 
-    const auto& command_line = *base::CommandLine::ForCurrentProcess();
-
 #if defined(OS_WIN)
-    base::win::SetupCRT(command_line);
+    base::win::SetupCRT(*base::CommandLine::ForCurrentProcess());
 #endif
 
     MainDelegate::InitializeParams init_params;
@@ -355,11 +254,6 @@
 #endif
 
     mojo::core::Configuration mojo_config;
-    if (process_type == ProcessType::kDefault &&
-        command_line.GetSwitchValueASCII(switches::kProcessType) ==
-            switches::kProcessTypeServiceManager) {
-      mojo_config.is_broker_process = true;
-    }
     mojo_config.max_message_num_bytes = kMaximumMojoMessageSize;
     delegate->InitializeMojo(&mojo_config);
 
@@ -388,7 +282,8 @@
     // implementation of mojo::NodeController::CreateSharedBuffer().
 #if !defined(OS_MAC) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA)
     if (sandbox::policy::IsUnsandboxedSandboxType(
-            sandbox::policy::SandboxTypeFromCommandLine(command_line))) {
+            sandbox::policy::SandboxTypeFromCommandLine(
+                *base::CommandLine::ForCurrentProcess()))) {
       // Unsandboxed processes don't need shared memory brokering... because
       // they're not sandboxed.
     } else if (mojo_config.force_direct_shared_memory_allocation) {
@@ -421,38 +316,9 @@
     }
   }
 
-  const auto& command_line = *base::CommandLine::ForCurrentProcess();
-  if (process_type == ProcessType::kDefault) {
-    std::string type_switch =
-        command_line.GetSwitchValueASCII(switches::kProcessType);
-    if (type_switch == switches::kProcessTypeServiceManager) {
-      process_type = ProcessType::kServiceManager;
-    } else if (type_switch == switches::kProcessTypeService) {
-      process_type = ProcessType::kService;
-    } else {
-      process_type = ProcessType::kEmbedder;
-    }
-  }
-  switch (process_type) {
-    case ProcessType::kDefault:
-      NOTREACHED();
-      break;
-
-    case ProcessType::kServiceManager:
-      exit_code = RunServiceManager(delegate);
-      break;
-
-    case ProcessType::kService:
-      CommonSubprocessInit();
-      exit_code = RunService(delegate);
-      break;
-
-    case ProcessType::kEmbedder:
-      if (delegate->IsEmbedderSubprocess())
-        CommonSubprocessInit();
-      exit_code = delegate->RunEmbedderProcess();
-      break;
-  }
+  if (delegate->IsEmbedderSubprocess())
+    CommonSubprocessInit();
+  exit_code = delegate->RunEmbedderProcess();
 
   if (tracker) {
     if (exit_code == 0) {
@@ -469,8 +335,7 @@
   autorelease_pool.reset();
 #endif
 
-  if (process_type == ProcessType::kEmbedder)
-    delegate->ShutDownEmbedderProcess();
+  delegate->ShutDownEmbedderProcess();
 
   return exit_code;
 }
diff --git a/services/service_manager/embedder/main_delegate.cc b/services/service_manager/embedder/main_delegate.cc
index 65dec35..011b386 100644
--- a/services/service_manager/embedder/main_delegate.cc
+++ b/services/service_manager/embedder/main_delegate.cc
@@ -20,31 +20,6 @@
 
 void MainDelegate::ShutDownEmbedderProcess() {}
 
-ProcessType MainDelegate::OverrideProcessType() {
-  return ProcessType::kDefault;
-}
-
 void MainDelegate::InitializeMojo(mojo::core::Configuration* config) {}
 
-std::vector<Manifest> MainDelegate::GetServiceManifests() {
-  return std::vector<Manifest>();
-}
-
-bool MainDelegate::ShouldLaunchAsServiceProcess(const Identity& identity) {
-  return true;
-}
-
-void MainDelegate::AdjustServiceProcessCommandLine(
-    const Identity& identity,
-    base::CommandLine* command_line) {}
-
-void MainDelegate::OnServiceManagerInitialized(
-    base::OnceClosure quit_closure,
-    BackgroundServiceManager* service_manager) {}
-
-std::unique_ptr<Service> MainDelegate::CreateEmbeddedService(
-    const std::string& service_name) {
-  return nullptr;
-}
-
 }  // namespace service_manager
diff --git a/services/service_manager/embedder/main_delegate.h b/services/service_manager/embedder/main_delegate.h
index fc3d453..27da85656 100644
--- a/services/service_manager/embedder/main_delegate.h
+++ b/services/service_manager/embedder/main_delegate.h
@@ -5,23 +5,10 @@
 #ifndef SERVICES_SERVICE_MANAGER_EMBEDDER_MAIN_DELEGATE_H_
 #define SERVICES_SERVICE_MANAGER_EMBEDDER_MAIN_DELEGATE_H_
 
-#include <memory>
-#include <vector>
-
-#include "base/callback_forward.h"
 #include "base/component_export.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/single_thread_task_runner.h"
 #include "mojo/core/embedder/configuration.h"
-#include "services/service_manager/background_service_manager.h"
-#include "services/service_manager/embedder/process_type.h"
-#include "services/service_manager/public/cpp/identity.h"
-#include "services/service_manager/public/cpp/manifest.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
 
 namespace base {
-class CommandLine;
 namespace mac {
 class ScopedNSAutoreleasePool;
 }
@@ -67,43 +54,9 @@
   // Called just before process exit if RunEmbedderProcess() was called.
   virtual void ShutDownEmbedderProcess();
 
-  // Force execution of the current process as a specific process type. May
-  // return |ProcessType::kDefault| to avoid overriding.
-  virtual ProcessType OverrideProcessType();
-
   // Allows the embedder to override the process-wide Mojo configuration and
   // initialization.
   virtual void InitializeMojo(mojo::core::Configuration* config);
-
-  // Gets the list of service manifests with which to initialize the Service
-  // Manager. This list must describe the complete set of usable services in
-  // the system and remains fixed for the lifetime of the Service Manager.
-  virtual std::vector<Manifest> GetServiceManifests();
-
-  // Indicates whether a process started by the service manager for a given
-  // target service identity should be run as a real service process (|true|)
-  // or if the service manager should delegate to the embedder to initialize the
-  // new process (|false|).
-  virtual bool ShouldLaunchAsServiceProcess(const Identity& identity);
-
-  // Allows the embedder to override command line switches for a service process
-  // to be launched.
-  virtual void AdjustServiceProcessCommandLine(const Identity& identity,
-                                               base::CommandLine* command_line);
-
-  // Allows the embedder to perform arbitrary initialization within the Service
-  // Manager process immediately before the Service Manager runs its main loop.
-  //
-  // |quit_closure| is a callback the embedder may retain and invoke at any time
-  // to cleanly terminate Service Manager execution.
-  virtual void OnServiceManagerInitialized(
-      base::OnceClosure quit_closure,
-      BackgroundServiceManager* service_manager);
-
-  // Runs an embedded service by name. If the embedder does not know how to
-  // create an instance of the named service, it should return null.
-  virtual std::unique_ptr<Service> CreateEmbeddedService(
-      const std::string& service_name);
 };
 
 }  // namespace service_manager
diff --git a/services/service_manager/embedder/process_type.h b/services/service_manager/embedder/process_type.h
deleted file mode 100644
index ca9d9c1..0000000
--- a/services/service_manager/embedder/process_type.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_SERVICE_MANAGER_EMBEDDER_PROCESS_TYPE_H_
-#define SERVICES_SERVICE_MANAGER_EMBEDDER_PROCESS_TYPE_H_
-
-namespace service_manager {
-
-enum class ProcessType {
-  // An unspecified process type. If this is given anywhere a ProcessType is
-  // expected, it must be interpreted as some reasonable default based on
-  // context.
-  kDefault,
-
-  // A standalone Service Manager process. There can be only one.
-  kServiceManager,
-
-  // A service process. A service process hosts one or more embedded service
-  // instances.
-  kService,
-
-  // An embedder process. The Service Manager implementation does not control
-  // any aspect of the process's logic beyond primitive process initialization
-  // and shutdown.
-  kEmbedder,
-};
-
-}  // namespace service_manager
-
-#endif  // SERVICES_SERVICE_MANAGER_EMBEDDER_PROCESS_TYPE_H_
diff --git a/services/service_manager/embedder/switches.cc b/services/service_manager/embedder/switches.cc
index 7bc83d1a..efceb3b 100644
--- a/services/service_manager/embedder/switches.cc
+++ b/services/service_manager/embedder/switches.cc
@@ -35,14 +35,6 @@
 // "service-runner", or any other arbitrary value supported by the embedder.
 const char kProcessType[] = "type";
 
-// The value of the |kProcessType| switch which tells the executable to assume
-// the role of a standalone Service Manager instance.
-const char kProcessTypeServiceManager[] = "service-manager";
-
-// The value of the |kProcessType| switch which tells the executable to assume
-// the role of a service instance.
-const char kProcessTypeService[] = "service-runner";
-
 // The token to use to construct the message pipe for a service in a child
 // process.
 const char kServiceRequestChannelToken[] = "service-request-channel-token";
diff --git a/services/service_manager/embedder/switches.h b/services/service_manager/embedder/switches.h
index 3b6690aa..3ed8212 100644
--- a/services/service_manager/embedder/switches.h
+++ b/services/service_manager/embedder/switches.h
@@ -26,12 +26,6 @@
 extern const char kProcessType[];
 
 COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER_SWITCHES)
-extern const char kProcessTypeServiceManager[];
-
-COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER_SWITCHES)
-extern const char kProcessTypeService[];
-
-COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER_SWITCHES)
 extern const char kServiceRequestChannelToken[];
 
 COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER_SWITCHES)
diff --git a/storage/browser/blob/blob_builder_from_stream_unittest.cc b/storage/browser/blob/blob_builder_from_stream_unittest.cc
index 184f335..3c27c86 100644
--- a/storage/browser/blob/blob_builder_from_stream_unittest.cc
+++ b/storage/browser/blob/blob_builder_from_stream_unittest.cc
@@ -416,8 +416,7 @@
   mojo::AssociatedRemote<blink::mojom::ProgressClient> progress_client_remote;
   mojo::AssociatedReceiver<blink::mojom::ProgressClient> progress_receiver(
       &progress_client,
-      progress_client_remote
-          .BindNewEndpointAndPassDedicatedReceiverForTesting());
+      progress_client_remote.BindNewEndpointAndPassDedicatedReceiver());
 
   mojo::DataPipe pipe;
   base::RunLoop loop;
diff --git a/testing/buildbot/chrome.ci.json b/testing/buildbot/chrome.ci.json
index 44ce26d..851d8d1c 100644
--- a/testing/buildbot/chrome.ci.json
+++ b/testing/buildbot/chrome.ci.json
@@ -3670,6 +3670,39 @@
       }
     ]
   },
+  "linux-chromeos-chrome-easwa": {
+    "additional_compile_targets": [
+      "chrome",
+      "chrome_sandbox",
+      "linux_symbols",
+      "symupload"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest-filter=*HelpApp*:*MediaApp*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "easwa_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      }
+    ]
+  },
   "linux-rel-swarming": {
     "gtest_tests": [
       {
@@ -4033,6 +4066,34 @@
       }
     ]
   },
+  "soda-linux": {
+    "additional_compile_targets": [
+      "unit_tests"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=SodaClient*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Linux",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "unit_tests",
+        "test_id_prefix": "ninja://chrome/test:unit_tests/"
+      }
+    ]
+  },
   "win-chrome": {
     "additional_compile_targets": [
       "chrome",
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index a72cc93..d52d44e 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -3040,6 +3040,39 @@
       }
     ]
   },
+  "linux-chromeos-chrome-easwa": {
+    "additional_compile_targets": [
+      "chrome",
+      "chrome_sandbox",
+      "linux_symbols",
+      "symupload"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest-filter=*HelpApp*:*MediaApp*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "easwa_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      }
+    ]
+  },
   "mac-chrome": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index fd9ca78..7fb870f 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -1442,7 +1442,7 @@
   def get_internal_waterfalls(self):
     # Similar to get_builders_that_do_not_actually_exist above, but for
     # waterfalls defined in internal configs.
-    return ['chrome', 'chrome.pgo']
+    return ['chrome', 'chrome.pgo', 'internal.soda']
 
   def check_input_file_consistency(self, verbose=False):
     self.check_input_files_sorting(verbose)
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 002aaaca..0b9b99b 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -529,6 +529,7 @@
       "../../chrome/test/chromedriver/test/run_webdriver_tests.py",
       "-v",
       "--chromedriver=chromedriver",
+      "--log-path=${ISOLATED_OUTDIR}/chromedriver.log",
       "--output-dir=${ISOLATED_OUTDIR}",
       "--test-path=../../third_party/blink/web_tests/external/wpt/webdriver/tests/",
     ],
diff --git a/testing/buildbot/internal.soda.json b/testing/buildbot/internal.soda.json
new file mode 100644
index 0000000..d8969d0
--- /dev/null
+++ b/testing/buildbot/internal.soda.json
@@ -0,0 +1,32 @@
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "soda-linux": {
+    "additional_compile_targets": [
+      "unit_tests"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=SodaClient*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Linux",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "unit_tests",
+        "test_id_prefix": "ninja://chrome/test:unit_tests/"
+      }
+    ]
+  }
+}
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d339b9c..5094b1028 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -23,7 +23,6 @@
     'android_ar_gtests': {
       'monochrome_public_test_ar_apk': {},
     },
-
     'android_ddready_vr_gtests': {
       'chrome_public_test_vr_apk-ddready-cardboard': {
         'args': [
@@ -1235,6 +1234,18 @@
       },
     },
 
+    'easwa_browser_tests' : {
+      'easwa_browser_tests': {
+        'args': [
+          '--gtest-filter=*HelpApp*:*MediaApp*'
+        ],
+        'swarming': {
+          'shards': 1,
+        },
+        'test': 'browser_tests',
+      }
+    },
+
     'fieldtrial_browser_tests': {
       'no_fieldtrial_browser_tests': {
         'args': [
@@ -3998,7 +4009,11 @@
         'results_handler': 'layout tests',
       },
     },
-
+    'soda_gtests': {
+      'unit_tests': {
+        'args': ['--gtest_filter=SodaClient*'],
+      },
+    },
     # TODO(https://crbug.com/1057802): Remove these tests once Out-of-Process
     # Storage is on by default.
     'storage_service_gtests': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 1d6e69b..fcce948 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -174,6 +174,28 @@
           'isolated_scripts': 'chrome_sizes',
         },
       },
+      'linux-chromeos-chrome-easwa': {
+        'additional_compile_targets': [
+          'chrome',
+          'chrome_sandbox',
+          'linux_symbols',
+          'symupload'
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+          'linux-xenial',
+        ],
+        'swarming': {
+          'dimension_sets': [
+            {
+              'ssd': '0',
+            },
+          ],
+        },
+        'test_suites': {
+          'gtest_tests': 'easwa_browser_tests',
+        },
+      },
       'mac-chrome': {
         'additional_compile_targets': [
           'chrome',
@@ -5515,6 +5537,30 @@
     },
   },
   {
+    'project': 'chrome',
+    'bucket': 'ci',
+    'name': 'internal.soda',
+    'machines': {
+      'soda-linux': {
+        'additional_compile_targets': [
+          'unit_tests',
+        ],
+        'test_suites': {
+            'gtest_tests': 'soda_gtests',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Linux',
+              'pool': 'chrome.tests',
+            },
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
+        },
+      },
+    },
+  },
+  {
     'project': 'chromium',
     'bucket': 'try',
     'name': 'tryserver.chromium.android',
diff --git a/testing/scripts/host_info.py b/testing/scripts/host_info.py
index f23352d..6aa9b8a 100755
--- a/testing/scripts/host_info.py
+++ b/testing/scripts/host_info.py
@@ -68,7 +68,7 @@
                      'tools',
                      'device_status.py'),
         '--json-output', tempfile_path,
-        '--blacklist-file', os.path.join(
+        '--denylist-file', os.path.join(
               args.paths['checkout'], 'out', 'bad_devices.json')
     ]
     if args.args:
@@ -86,7 +86,7 @@
   results['devices'] = sorted(v['serial'] for v in device_info)
 
   details = [
-      v['ro.build.fingerprint'] for v in device_info if not v['blacklisted']]
+      v['ro.build.fingerprint'] for v in device_info if not v['denylisted']]
 
   def unique_build_details(index):
     return sorted(list(set([v.split(':')[index] for v in details])))
@@ -106,8 +106,8 @@
       failures.append(k)
 
   for v in device_info:
-    if v['blacklisted']:
-      failures.append('Device %s blacklisted' % v['serial'])
+    if v['denylisted']:
+      failures.append('Device %s denylisted' % v['serial'])
 
   return results
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 07ab6d4..c94dca45 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -87,6 +87,25 @@
             ]
         }
     ],
+    "AndroidAppMenuUiRework": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "RegroupBackwardButton",
+                    "params": {
+                        "action_bar": "backward_button"
+                    },
+                    "enable_features": [
+                        "TabbedAppOverflowMenuIcons",
+                        "TabbedAppOverflowMenuRegroup"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidDarkSearch": [
         {
             "platforms": [
@@ -650,6 +669,22 @@
             ]
         }
     ],
+    "AutofillCreditCardAuthentication": [
+        {
+            "platforms": [
+                "android",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillCreditCardAuthentication"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillEnableCardNicknameManagementAndUpstream": [
         {
             "platforms": [
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 5a75561..2375a0b 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -11,9 +11,6 @@
     ":androidx_drawerlayout_drawerlayout_java",
     ":androidx_fragment_fragment_java",
     ":androidx_interpolator_interpolator_java",
-    ":androidx_legacy_legacy_support_core_ui_java",
-    ":androidx_legacy_legacy_support_core_utils_java",
-    ":androidx_legacy_legacy_support_v4_java",
     ":androidx_lifecycle_lifecycle_common_java",
     ":androidx_lifecycle_lifecycle_viewmodel_java",
     ":androidx_media_media_java",
@@ -65,20 +62,6 @@
   ]
 }
 
-# The dependencies below are used by chromecast internal.
-java_group("android_support_v7_preference_java") {
-  deps = [ ":androidx_preference_preference_java" ]
-}
-java_group("android_support_v14_preference_java") {
-  deps = [ ":androidx_legacy_legacy_preference_v14_java" ]
-}
-java_group("android_support_v17_leanback_java") {
-  deps = [ ":androidx_leanback_leanback_java" ]
-}
-java_group("android_support_v17_preference_java") {
-  deps = [ ":androidx_leanback_leanback_preference_java" ]
-}
-
 java_group("material_design_java") {
   deps = [ "$material_design_target" ]
 }
@@ -334,6 +317,11 @@
     ":androidx_viewpager_viewpager_java",
   ]
   resource_overlay = true
+  deps += [ "//third_party/android_deps/local_modifications/androidx_fragment_fragment:androidx_fragment_fragment_prebuilt_java" ]
+
+  # Omit this file since we use our own copy, included above.
+  # We can remove this once we migrate to AndroidX master for all libraries.
+  jar_excluded_patterns = [ "androidx/fragment/app/DialogFragment*" ]
 
   ignore_proguard_configs = true
 }
@@ -375,51 +363,6 @@
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-android_aar_prebuilt("androidx_legacy_legacy_support_core_ui_java") {
-  aar_path = "libs/androidx_legacy_legacy_support_core_ui/legacy-support-core-ui-1.0.0.aar"
-  info_path = "libs/androidx_legacy_legacy_support_core_ui/androidx_legacy_legacy_support_core_ui.info"
-  deps = [
-    ":androidx_annotation_annotation_java",
-    ":androidx_asynclayoutinflater_asynclayoutinflater_java",
-    ":androidx_coordinatorlayout_coordinatorlayout_java",
-    ":androidx_core_core_java",
-    ":androidx_cursoradapter_cursoradapter_java",
-    ":androidx_customview_customview_java",
-    ":androidx_drawerlayout_drawerlayout_java",
-    ":androidx_interpolator_interpolator_java",
-    ":androidx_legacy_legacy_support_core_utils_java",
-    ":androidx_slidingpanelayout_slidingpanelayout_java",
-    ":androidx_swiperefreshlayout_swiperefreshlayout_java",
-    ":androidx_viewpager_viewpager_java",
-  ]
-  resource_overlay = true
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-android_aar_prebuilt("androidx_legacy_legacy_support_core_utils_java") {
-  aar_path = "libs/androidx_legacy_legacy_support_core_utils/legacy-support-core-utils-1.0.0.aar"
-  info_path = "libs/androidx_legacy_legacy_support_core_utils/androidx_legacy_legacy_support_core_utils.info"
-  deps = [
-    ":androidx_annotation_annotation_java",
-    ":androidx_core_core_java",
-    ":androidx_documentfile_documentfile_java",
-    ":androidx_loader_loader_java",
-    ":androidx_localbroadcastmanager_localbroadcastmanager_java",
-    ":androidx_print_print_java",
-  ]
-  resource_overlay = true
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-android_aar_prebuilt("androidx_legacy_legacy_support_v13_java") {
-  aar_path =
-      "libs/androidx_legacy_legacy_support_v13/legacy-support-v13-1.0.0.aar"
-  info_path = "libs/androidx_legacy_legacy_support_v13/androidx_legacy_legacy_support_v13.info"
-  deps = [ ":androidx_legacy_legacy_support_v4_java" ]
-  resource_overlay = true
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
 android_aar_prebuilt("androidx_legacy_legacy_support_v4_java") {
   aar_path =
       "libs/androidx_legacy_legacy_support_v4/legacy-support-v4-1.0.0.aar"
@@ -1988,6 +1931,50 @@
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+android_aar_prebuilt("androidx_legacy_legacy_support_core_ui_java") {
+  aar_path = "libs/androidx_legacy_legacy_support_core_ui/legacy-support-core-ui-1.0.0.aar"
+  info_path = "libs/androidx_legacy_legacy_support_core_ui/androidx_legacy_legacy_support_core_ui.info"
+
+  # To remove visibility constraint, add this dependency to
+  # //third_party/android_deps/build.gradle.
+  visibility = [ ":*" ]
+  deps = [
+    ":androidx_annotation_annotation_java",
+    ":androidx_asynclayoutinflater_asynclayoutinflater_java",
+    ":androidx_coordinatorlayout_coordinatorlayout_java",
+    ":androidx_core_core_java",
+    ":androidx_cursoradapter_cursoradapter_java",
+    ":androidx_customview_customview_java",
+    ":androidx_drawerlayout_drawerlayout_java",
+    ":androidx_interpolator_interpolator_java",
+    ":androidx_legacy_legacy_support_core_utils_java",
+    ":androidx_slidingpanelayout_slidingpanelayout_java",
+    ":androidx_swiperefreshlayout_swiperefreshlayout_java",
+    ":androidx_viewpager_viewpager_java",
+  ]
+  resource_overlay = true
+}
+
+# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+android_aar_prebuilt("androidx_legacy_legacy_support_core_utils_java") {
+  aar_path = "libs/androidx_legacy_legacy_support_core_utils/legacy-support-core-utils-1.0.0.aar"
+  info_path = "libs/androidx_legacy_legacy_support_core_utils/androidx_legacy_legacy_support_core_utils.info"
+
+  # To remove visibility constraint, add this dependency to
+  # //third_party/android_deps/build.gradle.
+  visibility = [ ":*" ]
+  deps = [
+    ":androidx_annotation_annotation_java",
+    ":androidx_core_core_java",
+    ":androidx_documentfile_documentfile_java",
+    ":androidx_loader_loader_java",
+    ":androidx_localbroadcastmanager_localbroadcastmanager_java",
+    ":androidx_print_print_java",
+  ]
+  resource_overlay = true
+}
+
+# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
 android_aar_prebuilt("androidx_lifecycle_lifecycle_livedata_java") {
   aar_path =
       "libs/androidx_lifecycle_lifecycle_livedata/lifecycle-livedata-2.0.0.aar"
diff --git a/third_party/android_deps/additional_readme_paths.json b/third_party/android_deps/additional_readme_paths.json
index 712e39a..a2d7fb7c 100644
--- a/third_party/android_deps/additional_readme_paths.json
+++ b/third_party/android_deps/additional_readme_paths.json
@@ -33,7 +33,6 @@
     "libs/androidx_legacy_legacy_preference_v14",
     "libs/androidx_legacy_legacy_support_core_ui",
     "libs/androidx_legacy_legacy_support_core_utils",
-    "libs/androidx_legacy_legacy_support_v13",
     "libs/androidx_legacy_legacy_support_v4",
     "libs/androidx_lifecycle_lifecycle_common",
     "libs/androidx_lifecycle_lifecycle_common_java8",
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle
index bd4cab8..5eaf0e5 100644
--- a/third_party/android_deps/build.gradle
+++ b/third_party/android_deps/build.gradle
@@ -51,10 +51,6 @@
     def androidXSupportLibVersion = '1.0.0'
     compile "androidx.core:core:1.1.0"
     compile "androidx.activity:activity:1.1.0"
-    compile "androidx.legacy:legacy-support-core-ui:${androidXSupportLibVersion}"
-    compile "androidx.legacy:legacy-support-core-utils:${androidXSupportLibVersion}"
-    compile "androidx.legacy:legacy-support-v4:${androidXSupportLibVersion}"
-    compile "androidx.legacy:legacy-support-v13:${androidXSupportLibVersion}"
     compile "androidx.annotation:annotation:1.1.0"
     compile "androidx.appcompat:appcompat:1.2.0-beta01"
     compile "androidx.appcompat:appcompat-resources:1.2.0-beta01"
@@ -95,6 +91,9 @@
     compile "androidx.multidex:multidex:2.0.0"
     compile "androidx.webkit:webkit:1.3.0-rc01"
 
+    // Used by 1p Play Services.
+    compile "androidx.legacy:legacy-support-v4:${androidXSupportLibVersion}"
+
     // Replacement for com.android.support:design
     compile "com.google.android.material:material:1.2.0-alpha06"
 
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index b6bf87ce..8be1857 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -299,8 +299,16 @@
                 sb.append('  ignore_proguard_configs = true\n')
                 break
             case 'androidx_fragment_fragment':
-                sb.append('\n')
-                sb.append('  ignore_proguard_configs = true\n')
+                sb.append("""\
+                |  deps += [ "//third_party/android_deps/local_modifications/androidx_fragment_fragment:androidx_fragment_fragment_prebuilt_java" ]
+                |  # Omit this file since we use our own copy, included above.
+                |  # We can remove this once we migrate to AndroidX master for all libraries.
+                |  jar_excluded_patterns = [
+                |    "androidx/fragment/app/DialogFragment.java",
+                |  ]
+                |
+                |  ignore_proguard_configs = true
+                |""".stripMargin())
                 break
             case 'androidx_media_media':
             case 'androidx_versionedparcelable_versionedparcelable':
diff --git a/third_party/android_deps/local_modifications/androidx_fragment_fragment/BUILD.gn b/third_party/android_deps/local_modifications/androidx_fragment_fragment/BUILD.gn
new file mode 100644
index 0000000..33eb2bef
--- /dev/null
+++ b/third_party/android_deps/local_modifications/androidx_fragment_fragment/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# This rule contains the jar file produced by building
+# :androidx_fragment_fragment_partial_java. After building the
+# aforementioned target, the jar file can be found in:
+# out/<dir>/lib.java/third_party/android_deps/local_modifications/androidx_fragment_fragment/androidx_fragment_fragment_partial_java.jar
+android_java_prebuilt("androidx_fragment_fragment_prebuilt_java") {
+  jar_path = "androidx_fragment_fragment_java.jar"
+
+  # Normally this would depend on //third_party/android_deps:androidx_fragment_fragment_java,
+  # but the dependency is reversed to keep the .class file customization
+  # transparent to dependents. Disable bytecode checks, which would otherwise
+  # complain about this.
+  enable_bytecode_checks = false
+}
+
+android_library("androidx_fragment_fragment_partial_java") {
+  sources = [ "java/androidx/fragment/app/DialogFragment.java" ]
+  deps = [
+    "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_fragment_fragment_java",
+  ]
+}
diff --git a/third_party/android_deps/local_modifications/androidx_fragment_fragment/README b/third_party/android_deps/local_modifications/androidx_fragment_fragment/README
new file mode 100644
index 0000000..5412163
--- /dev/null
+++ b/third_party/android_deps/local_modifications/androidx_fragment_fragment/README
@@ -0,0 +1,11 @@
+This directory contains AndroidX's DialogFragment.java, copied from commit
+88c22bfb, with c07daa2 merged in, which removes a problematic
+Fragment.getActivity() call.
+
+To pull in these changes, we exclude DialogFragment from the
+androidx_fragment_fragment library in CIPD (which is from an official
+release), and add a dependency from that library to a prebuilt jar file
+containing our customized DialogFragment.java.
+
+Note that this customization can be removed once we start pulling our
+AndroidX dependencies from master.
diff --git a/third_party/android_deps/local_modifications/androidx_fragment_fragment/androidx_fragment_fragment_java.jar b/third_party/android_deps/local_modifications/androidx_fragment_fragment/androidx_fragment_fragment_java.jar
new file mode 100644
index 0000000..9e7f008
--- /dev/null
+++ b/third_party/android_deps/local_modifications/androidx_fragment_fragment/androidx_fragment_fragment_java.jar
Binary files differ
diff --git a/third_party/android_deps/local_modifications/androidx_fragment_fragment/java/androidx/fragment/app/DialogFragment.java b/third_party/android_deps/local_modifications/androidx_fragment_fragment/java/androidx/fragment/app/DialogFragment.java
new file mode 100644
index 0000000..018dba5
--- /dev/null
+++ b/third_party/android_deps/local_modifications/androidx_fragment_fragment/java/androidx/fragment/app/DialogFragment.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.fragment.app;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import androidx.annotation.IntDef;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StyleRes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+/**
+ * Static library support version of the framework's {@link android.app.DialogFragment}.
+ * Used to write apps that run on platforms prior to Android 3.0.  When running
+ * on Android 3.0 or above, this implementation is still used; it does not try
+ * to switch to the framework's implementation.  See the framework SDK
+ * documentation for a class overview.
+ */
+public class DialogFragment extends Fragment
+        implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface DialogStyle {}
+    /**
+     * Style for {@link #setStyle(int, int)}: a basic,
+     * normal dialog.
+     */
+    public static final int STYLE_NORMAL = 0;
+    /**
+     * Style for {@link #setStyle(int, int)}: don't include
+     * a title area.
+     */
+    public static final int STYLE_NO_TITLE = 1;
+    /**
+     * Style for {@link #setStyle(int, int)}: don't draw
+     * any frame at all; the view hierarchy returned by {@link #onCreateView}
+     * is entirely responsible for drawing the dialog.
+     */
+    public static final int STYLE_NO_FRAME = 2;
+    /**
+     * Style for {@link #setStyle(int, int)}: like
+     * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
+     * The user can not touch it, and its window will not receive input focus.
+     */
+    public static final int STYLE_NO_INPUT = 3;
+    private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
+    private static final String SAVED_STYLE = "android:style";
+    private static final String SAVED_THEME = "android:theme";
+    private static final String SAVED_CANCELABLE = "android:cancelable";
+    private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
+    private static final String SAVED_BACK_STACK_ID = "android:backStackId";
+    private Handler mHandler;
+    private Runnable mDismissRunnable = new Runnable() {
+        @SuppressLint("SyntheticAccessor")
+        @Override
+        public void run() {
+            mOnDismissListener.onDismiss(mDialog);
+        }
+    };
+    private DialogInterface.OnCancelListener mOnCancelListener =
+            new DialogInterface.OnCancelListener() {
+        @SuppressLint("SyntheticAccessor")
+        @Override
+        public void onCancel(@Nullable DialogInterface dialog) {
+            if (mDialog != null) {
+                DialogFragment.this.onCancel(mDialog);
+            }
+        }
+    };
+    private DialogInterface.OnDismissListener mOnDismissListener =
+            new DialogInterface.OnDismissListener() {
+        @SuppressLint("SyntheticAccessor")
+        @Override
+        public void onDismiss(@Nullable DialogInterface dialog) {
+            if (mDialog != null) {
+                DialogFragment.this.onDismiss(mDialog);
+            }
+        }
+    };
+    private int mStyle = STYLE_NORMAL;
+    private int mTheme = 0;
+    private boolean mCancelable = true;
+    private boolean mShowsDialog = true;
+    private int mBackStackId = -1;
+    private boolean mCreatingDialog;
+    @Nullable
+    private Dialog mDialog;
+    private boolean mViewDestroyed;
+    private boolean mDismissed;
+    private boolean mShownByMe;
+    public DialogFragment() {
+    }
+    /**
+     * Call to customize the basic appearance and behavior of the
+     * fragment's dialog.  This can be used for some common dialog behaviors,
+     * taking care of selecting flags, theme, and other options for you.  The
+     * same effect can be achieve by manually setting Dialog and Window
+     * attributes yourself.  Calling this after the fragment's Dialog is
+     * created will have no effect.
+     *
+     * @param style Selects a standard style: may be {@link #STYLE_NORMAL},
+     * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
+     * {@link #STYLE_NO_INPUT}.
+     * @param theme Optional custom theme.  If 0, an appropriate theme (based
+     * on the style) will be selected for you.
+     */
+    public void setStyle(@DialogStyle int style, @StyleRes int theme) {
+        mStyle = style;
+        if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
+            mTheme = android.R.style.Theme_Panel;
+        }
+        if (theme != 0) {
+            mTheme = theme;
+        }
+    }
+    /**
+     * Display the dialog, adding the fragment to the given FragmentManager.  This
+     * is a convenience for explicitly creating a transaction, adding the
+     * fragment to it with the given tag, and {@link FragmentTransaction#commit() committing} it.
+     * This does <em>not</em> add the transaction to the fragment back stack.  When the fragment
+     * is dismissed, a new transaction will be executed to remove it from
+     * the activity.
+     * @param manager The FragmentManager this fragment will be added to.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     */
+    public void show(@NonNull FragmentManager manager, @Nullable String tag) {
+        mDismissed = false;
+        mShownByMe = true;
+        FragmentTransaction ft = manager.beginTransaction();
+        ft.add(this, tag);
+        ft.commit();
+    }
+    /**
+     * Display the dialog, adding the fragment using an existing transaction
+     * and then {@link FragmentTransaction#commit() committing} the transaction.
+     * @param transaction An existing transaction in which to add the fragment.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     * @return Returns the identifier of the committed transaction, as per
+     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
+     */
+    public int show(@NonNull FragmentTransaction transaction, @Nullable String tag) {
+        mDismissed = false;
+        mShownByMe = true;
+        transaction.add(this, tag);
+        mViewDestroyed = false;
+        mBackStackId = transaction.commit();
+        return mBackStackId;
+    }
+    /**
+     * Display the dialog, immediately adding the fragment to the given FragmentManager.  This
+     * is a convenience for explicitly creating a transaction, adding the
+     * fragment to it with the given tag, and calling {@link FragmentTransaction#commitNow()}.
+     * This does <em>not</em> add the transaction to the fragment back stack.  When the fragment
+     * is dismissed, a new transaction will be executed to remove it from
+     * the activity.
+     * @param manager The FragmentManager this fragment will be added to.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     */
+    public void showNow(@NonNull FragmentManager manager, @Nullable String tag) {
+        mDismissed = false;
+        mShownByMe = true;
+        FragmentTransaction ft = manager.beginTransaction();
+        ft.add(this, tag);
+        ft.commitNow();
+    }
+    /**
+     * Dismiss the fragment and its dialog.  If the fragment was added to the
+     * back stack, all back stack state up to and including this entry will
+     * be popped.  Otherwise, a new transaction will be committed to remove
+     * the fragment.
+     */
+    public void dismiss() {
+        dismissInternal(false, false);
+    }
+    /**
+     * Version of {@link #dismiss()} that uses
+     * {@link FragmentTransaction#commitAllowingStateLoss()
+     * FragmentTransaction.commitAllowingStateLoss()}. See linked
+     * documentation for further details.
+     */
+    public void dismissAllowingStateLoss() {
+        dismissInternal(true, false);
+    }
+    private void dismissInternal(boolean allowStateLoss, boolean fromOnDismiss) {
+        if (mDismissed) {
+            return;
+        }
+        mDismissed = true;
+        mShownByMe = false;
+        if (mDialog != null) {
+            // Instead of waiting for a posted onDismiss(), null out
+            // the listener and call onDismiss() manually to ensure
+            // that the callback happens before onDestroy()
+            mDialog.setOnDismissListener(null);
+            mDialog.dismiss();
+            if (!fromOnDismiss) {
+                // onDismiss() is always called on the main thread, so
+                // we mimic that behavior here. The difference here is that
+                // we don't post the message to ensure that the onDismiss()
+                // callback still happens before onDestroy()
+                if (Looper.myLooper() == mHandler.getLooper()) {
+                    onDismiss(mDialog);
+                } else {
+                    mHandler.post(mDismissRunnable);
+                }
+            }
+        }
+        mViewDestroyed = true;
+        if (mBackStackId >= 0) {
+            getParentFragmentManager().popBackStack(mBackStackId,
+                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
+            mBackStackId = -1;
+        } else {
+            FragmentTransaction ft = getParentFragmentManager().beginTransaction();
+            ft.remove(this);
+            if (allowStateLoss) {
+                ft.commitAllowingStateLoss();
+            } else {
+                ft.commit();
+            }
+        }
+    }
+    /**
+     * Return the {@link Dialog} this fragment is currently controlling.
+     *
+     * @see #requireDialog()
+     */
+    @Nullable
+    public Dialog getDialog() {
+        return mDialog;
+    }
+    /**
+     * Return the {@link Dialog} this fragment is currently controlling.
+     *
+     * @throws IllegalStateException if the Dialog has not yet been created (before
+     * {@link #onCreateDialog(Bundle)}) or has been destroyed (after {@link #onDestroyView()}.
+     * @see #getDialog()
+     */
+    @NonNull
+    public final Dialog requireDialog() {
+        Dialog dialog = getDialog();
+        if (dialog == null) {
+            throw new IllegalStateException("DialogFragment " + this + " does not have a Dialog.");
+        }
+        return dialog;
+    }
+    @StyleRes
+    public int getTheme() {
+        return mTheme;
+    }
+    /**
+     * Control whether the shown Dialog is cancelable.  Use this instead of
+     * directly calling {@link Dialog#setCancelable(boolean)
+     * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
+     * its behavior based on this.
+     *
+     * @param cancelable If true, the dialog is cancelable.  The default
+     * is true.
+     */
+    public void setCancelable(boolean cancelable) {
+        mCancelable = cancelable;
+        if (mDialog != null) mDialog.setCancelable(cancelable);
+    }
+    /**
+     * Return the current value of {@link #setCancelable(boolean)}.
+     */
+    public boolean isCancelable() {
+        return mCancelable;
+    }
+    /**
+     * Controls whether this fragment should be shown in a dialog.  If not
+     * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
+     * and the fragment's view hierarchy will thus not be added to it.  This
+     * allows you to instead use it as a normal fragment (embedded inside of
+     * its activity).
+     *
+     * <p>This is normally set for you based on whether the fragment is
+     * associated with a container view ID passed to
+     * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
+     * If the fragment was added with a container, setShowsDialog will be
+     * initialized to false; otherwise, it will be true.
+     *
+     * @param showsDialog If true, the fragment will be displayed in a Dialog.
+     * If false, no Dialog will be created and the fragment's view hierarchy
+     * left undisturbed.
+     */
+    public void setShowsDialog(boolean showsDialog) {
+        mShowsDialog = showsDialog;
+    }
+    /**
+     * Return the current value of {@link #setShowsDialog(boolean)}.
+     */
+    public boolean getShowsDialog() {
+        return mShowsDialog;
+    }
+    @MainThread
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        if (!mShownByMe) {
+            // If not explicitly shown through our API, take this as an
+            // indication that the dialog is no longer dismissed.
+            mDismissed = false;
+        }
+    }
+    @MainThread
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        if (!mShownByMe && !mDismissed) {
+            // The fragment was not shown by a direct call here, it is not
+            // dismissed, and now it is being detached...  well, okay, thou
+            // art now dismissed.  Have fun.
+            mDismissed = true;
+        }
+    }
+    @MainThread
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // This assumes that onCreate() is being called on the main thread
+        mHandler = new Handler();
+        mShowsDialog = mContainerId == 0;
+        if (savedInstanceState != null) {
+            mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
+            mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
+            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
+            mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
+        }
+    }
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * If this is called from within {@link #onCreateDialog(Bundle)}, the layout inflater from
+     * {@link Fragment#onGetLayoutInflater(Bundle)}, without the dialog theme, will be returned.
+     */
+    @Override
+    @NonNull
+    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
+        LayoutInflater layoutInflater = super.onGetLayoutInflater(savedInstanceState);
+        if (!mShowsDialog || mCreatingDialog) {
+            return layoutInflater;
+        }
+        try {
+            mCreatingDialog = true;
+            mDialog = onCreateDialog(savedInstanceState);
+            setupDialog(mDialog, mStyle);
+        } finally {
+            mCreatingDialog = false;
+        }
+        return layoutInflater.cloneInContext(requireDialog().getContext());
+    }
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    public void setupDialog(@NonNull Dialog dialog, int style) {
+        switch (style) {
+            case STYLE_NO_INPUT:
+                Window window = dialog.getWindow();
+                if (window != null) {
+                    window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+                }
+                // fall through...
+            case STYLE_NO_FRAME:
+            case STYLE_NO_TITLE:
+                dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        }
+    }
+    /**
+     * Override to build your own custom Dialog container.  This is typically
+     * used to show an AlertDialog instead of a generic Dialog; when doing so,
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need
+     * to be implemented since the AlertDialog takes care of its own content.
+     *
+     * <p>This method will be called after {@link #onCreate(Bundle)} and
+     * before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.  The
+     * default implementation simply instantiates and returns a {@link Dialog}
+     * class.
+     *
+     * <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener
+     * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener
+     * Dialog.setOnDismissListener} callbacks.  You must not set them yourself.</em>
+     * To find out about these events, override {@link #onCancel(DialogInterface)}
+     * and {@link #onDismiss(DialogInterface)}.</p>
+     *
+     * @param savedInstanceState The last saved instance state of the Fragment,
+     * or null if this is a freshly created Fragment.
+     *
+     * @return Return a new Dialog instance to be displayed by the Fragment.
+     */
+    @MainThread
+    @NonNull
+    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+        return new Dialog(requireContext(), getTheme());
+    }
+    @Override
+    public void onCancel(@NonNull DialogInterface dialog) {
+    }
+    @Override
+    public void onDismiss(@NonNull DialogInterface dialog) {
+        if (!mViewDestroyed) {
+            // Note: we need to use allowStateLoss, because the dialog
+            // dispatches this asynchronously so we can receive the call
+            // after the activity is paused.  Worst case, when the user comes
+            // back to the activity they see the dialog again.
+            dismissInternal(true, true);
+        }
+    }
+    @MainThread
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (!mShowsDialog) {
+            return;
+        }
+        View view = getView();
+        if (mDialog != null) {
+            if (view != null) {
+                if (view.getParent() != null) {
+                    throw new IllegalStateException(
+                            "DialogFragment can not be attached to a container view");
+                }
+                mDialog.setContentView(view);
+            }
+			final Context context = getContext();
+            if (context instanceof Activity) {
+                mDialog.setOwnerActivity((Activity) context);
+            }
+            mDialog.setCancelable(mCancelable);
+            mDialog.setOnCancelListener(mOnCancelListener);
+            mDialog.setOnDismissListener(mOnDismissListener);
+            if (savedInstanceState != null) {
+                Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
+                if (dialogState != null) {
+                    mDialog.onRestoreInstanceState(dialogState);
+                }
+            }
+        }
+    }
+    @MainThread
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mDialog != null) {
+            mViewDestroyed = false;
+            mDialog.show();
+        }
+    }
+    @MainThread
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mDialog != null) {
+            Bundle dialogState = mDialog.onSaveInstanceState();
+            outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
+        }
+        if (mStyle != STYLE_NORMAL) {
+            outState.putInt(SAVED_STYLE, mStyle);
+        }
+        if (mTheme != 0) {
+            outState.putInt(SAVED_THEME, mTheme);
+        }
+        if (!mCancelable) {
+            outState.putBoolean(SAVED_CANCELABLE, mCancelable);
+        }
+        if (!mShowsDialog) {
+            outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+        }
+        if (mBackStackId != -1) {
+            outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+        }
+    }
+    @MainThread
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mDialog != null) {
+            mDialog.hide();
+        }
+    }
+    /**
+     * Remove dialog.
+     */
+    @MainThread
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        if (mDialog != null) {
+            // Set removed here because this dismissal is just to hide
+            // the dialog -- we don't want this to cause the fragment to
+            // actually be removed.
+            mViewDestroyed = true;
+            // Instead of waiting for a posted onDismiss(), null out
+            // the listener and call onDismiss() manually to ensure
+            // that the callback happens before onDestroy()
+            mDialog.setOnDismissListener(null);
+            mDialog.dismiss();
+            if (!mDismissed) {
+                // Don't send a second onDismiss() callback if we've already
+                // dismissed the dialog manually in dismissInternal()
+                onDismiss(mDialog);
+            }
+            mDialog = null;
+        }
+    }
+}
diff --git a/third_party/android_swipe_refresh/BUILD.gn b/third_party/android_swipe_refresh/BUILD.gn
index 6352c443..8224213 100644
--- a/third_party/android_swipe_refresh/BUILD.gn
+++ b/third_party/android_swipe_refresh/BUILD.gn
@@ -16,6 +16,5 @@
   deps = [
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_interpolator_interpolator_java",
-    "//third_party/android_deps:androidx_legacy_legacy_support_core_ui_java",
   ]
 }
diff --git a/third_party/blink/common/associated_interfaces/associated_interface_provider.cc b/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
index 226a471..93b82ccf 100644
--- a/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
+++ b/third_party/blink/common/associated_interfaces/associated_interface_provider.cc
@@ -20,7 +20,7 @@
   explicit LocalProvider(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
     associated_interface_provider_receiver_.Bind(
-        remote_.BindNewEndpointAndPassDedicatedReceiverForTesting(),
+        remote_.BindNewEndpointAndPassDedicatedReceiver(),
         std::move(task_runner));
   }
 
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 8df7dd6f..7bebf4d 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -7698,7 +7698,7 @@
       optional StreamCompression streamCompression
 
 # A domain for letting clients substitute browser's network layer with client code.
-experimental domain Fetch
+domain Fetch
   depends on Network
   depends on IO
   depends on Page
@@ -7709,12 +7709,12 @@
   # Stages of the request to handle. Request will intercept before the request is
   # sent. Response will intercept after the response is received (but before response
   # body is received.
-  experimental type RequestStage extends string
+  type RequestStage extends string
     enum
       Request
       Response
 
-  experimental type RequestPattern extends object
+  type RequestPattern extends object
     properties
       # Wildcards ('*' -> zero or more, '?' -> exactly one) are allowed. Escape character is
       # backslash. Omitting is equivalent to "*".
@@ -7731,7 +7731,7 @@
       string value
 
   # Authorization challenge for HTTP status code 401 or 407.
-  experimental type AuthChallenge extends object
+  type AuthChallenge extends object
     properties
       # Source of the authentication challenge.
       optional enum source
@@ -7745,7 +7745,7 @@
       string realm
 
   # Response to an AuthChallenge.
-  experimental type AuthChallengeResponse extends object
+  type AuthChallengeResponse extends object
     properties
       # The decision on what to do in response to the authorization challenge.  Default means
       # deferring to the default behavior of the net stack, which will likely either the Cancel
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 361bc12..d263afb 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -77,6 +77,7 @@
     "insecure_input/insecure_input_service.mojom",
     "keyboard_lock/keyboard_lock.mojom",
     "leak_detector/leak_detector.mojom",
+    "link_to_text/link_to_text.mojom",
     "loader/code_cache.mojom",
     "loader/content_security_notifier.mojom",
     "loader/fetch_client_settings_object.mojom",
diff --git a/third_party/blink/public/mojom/android_font_lookup/android_font_lookup.mojom b/third_party/blink/public/mojom/android_font_lookup/android_font_lookup.mojom
index c81d20c..0602d8c 100644
--- a/third_party/blink/public/mojom/android_font_lookup/android_font_lookup.mojom
+++ b/third_party/blink/public/mojom/android_font_lookup/android_font_lookup.mojom
@@ -11,8 +11,6 @@
 interface AndroidFontLookup {
   // Returns a list of ICU case folded full font names available to be fetched
   // locally from on-device storage, without a network roundtrip.
-  // TODO(crbug.com/1111148): Complete implementation of this method in
-  // AndroidFontLookupImpl.java. Currently returns empty list.
   GetUniqueNameLookupTable()
     => (array<string> available_unique_font_names);
 
diff --git a/third_party/blink/public/mojom/link_to_text/OWNERS b/third_party/blink/public/mojom/link_to_text/OWNERS
new file mode 100644
index 0000000..bf01ee9e
--- /dev/null
+++ b/third_party/blink/public/mojom/link_to_text/OWNERS
@@ -0,0 +1,4 @@
+# COMPONENT: UI>Browser>Sharing
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/third_party/blink/public/mojom/link_to_text/link_to_text.mojom b/third_party/blink/public/mojom/link_to_text/link_to_text.mojom
new file mode 100644
index 0000000..342f4a1
--- /dev/null
+++ b/third_party/blink/public/mojom/link_to_text/link_to_text.mojom
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.blink.mojom"]
+module blink.mojom;
+
+// TextFragmentSelectorProducer is used for requesting renderer to generate
+// text fragment selector for the latest text selection. Implemented in renderer.
+interface TextFragmentSelectorProducer {
+  // Generates text fragment selector according to
+  // https://github.com/WICG/scroll-to-text-fragment#proposed-solution.
+  GenerateSelector() => (string selector);
+};
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 50241098..6388c4b4 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2794,6 +2794,7 @@
   kCSSContainAllWithoutContentVisibility = 3467,
   kTimerInstallFromBeforeUnload = 3468,
   kTimerInstallFromUnload = 3469,
+  kElementAttachInternalsBeforeConstructor = 3470,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
index 763fdc7e..82d310fd 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
@@ -214,6 +214,9 @@
     return false;
   }
 
+  // 8.1.new: set custom element state to kPreCustomized.
+  element.SetCustomElementState(CustomElementState::kPreCustomized);
+
   Element* result = CallConstructor();
 
   // To report exception thrown from callConstructor()
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 2d88b0c4..897dc26 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -804,6 +804,8 @@
   // legacy layout on the entire subtree, unless this is overridden by
   // ShouldForceNGLayout().
   bool ShouldForceLegacyLayout() const {
+    if (ShouldForceNGLayout())
+      return false;
     if (TypeShouldForceLegacyLayout())
       return true;
     if (!HasRareData())
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 91b64e82..cef59fe 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -3232,12 +3232,17 @@
 
     case CustomElementState::kCustom:
       DCHECK(old_state == CustomElementState::kUndefined ||
-             old_state == CustomElementState::kFailed);
+             old_state == CustomElementState::kFailed ||
+             old_state == CustomElementState::kPreCustomized);
       break;
 
     case CustomElementState::kFailed:
       DCHECK_NE(CustomElementState::kFailed, old_state);
       break;
+
+    case CustomElementState::kPreCustomized:
+      DCHECK_EQ(CustomElementState::kFailed, old_state);
+      break;
   }
 
   DCHECK(IsHTMLElement());
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index c5679a7..261c41a 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -101,8 +101,9 @@
   // https://dom.spec.whatwg.org/#concept-element-custom-element-state
   kUncustomized = 0,
   kCustom = 1 << kNodeCustomElementShift,
-  kUndefined = 2 << kNodeCustomElementShift,
-  kFailed = 3 << kNodeCustomElementShift,
+  kPreCustomized = 2 << kNodeCustomElementShift,
+  kUndefined = 3 << kNodeCustomElementShift,
+  kFailed = 4 << kNodeCustomElementShift,
 };
 
 enum class SlotChangeType {
@@ -970,24 +971,24 @@
     kChildNeedsStyleRecalcFlag = 1 << 16,
     kStyleChangeMask = 0x3 << kNodeStyleChangeShift,
 
-    kCustomElementStateMask = 0x3 << kNodeCustomElementShift,
+    kCustomElementStateMask = 0x7 << kNodeCustomElementShift,
 
-    kHasNameOrIsEditingTextFlag = 1 << 21,
-    kHasEventTargetDataFlag = 1 << 22,
+    kHasNameOrIsEditingTextFlag = 1 << 22,
+    kHasEventTargetDataFlag = 1 << 23,
 
-    kV0CustomElementFlag = 1 << 23,
-    kV0CustomElementUpgradedFlag = 1 << 24,
+    kV0CustomElementFlag = 1 << 24,
+    kV0CustomElementUpgradedFlag = 1 << 25,
 
-    kNeedsReattachLayoutTree = 1 << 25,
-    kChildNeedsReattachLayoutTree = 1 << 26,
+    kNeedsReattachLayoutTree = 1 << 26,
+    kChildNeedsReattachLayoutTree = 1 << 27,
 
-    kHasDuplicateAttributes = 1 << 27,
+    kHasDuplicateAttributes = 1 << 28,
 
-    kForceReattachLayoutTree = 1 << 28,
+    kForceReattachLayoutTree = 1 << 29,
 
     kDefaultNodeFlags = kIsFinishedParsingChildrenFlag,
 
-    // 4 bits remaining.
+    // 3 bits remaining.
   };
 
   ALWAYS_INLINE bool GetFlag(NodeFlags mask) const {
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 2bd09ae7..ea9483da 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -4115,8 +4115,7 @@
   mojo::PendingAssociatedRemote<mojom::blink::FrameWidgetHost>
   BindNewFrameWidgetInterfaces() {
     frame_widget_host_receiver_.reset();
-    return frame_widget_host_receiver_
-        .BindNewEndpointAndPassDedicatedRemoteForTesting();
+    return frame_widget_host_receiver_.BindNewEndpointAndPassDedicatedRemote();
   }
 
   int GetAndResetHasTouchEventHandlerCallCount(bool state) {
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc b/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
index daba5f4..1489d43 100644
--- a/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc
@@ -77,7 +77,7 @@
 
     HeapMojoAssociatedRemote<BlobURLStore> url_store_remote(execution_context_);
     url_store_receiver_.Bind(
-        url_store_remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+        url_store_remote.BindNewEndpointAndPassDedicatedReceiver());
     url_manager().SetURLStoreForTesting(std::move(url_store_remote));
   }
 
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index d1712bd..49991d5 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -67,6 +67,7 @@
   kM86 = 86,
   kM87 = 87,
   kM88 = 88,
+  kM89 = 89,
 };
 
 // Returns estimated milestone dates as milliseconds since January 1, 1970.
@@ -112,25 +113,17 @@
     case kM83:
       return {2020, 5, 0, 18, 4};
     case kM84:
-      // This release is not yet scheduled, so this date is a guess.
-      // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/N1NxbSVOZas/ySlEKDKkBgAJ
       return {2020, 7, 0, 14, 4};
     case kM85:
-      // This release is not yet scheduled, so this date is a guess.
-      // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/N1NxbSVOZas/ySlEKDKkBgAJ
       return {2020, 8, 0, 25, 4};
     case kM86:
-      // This release is not yet scheduled, so this date is a guess.
-      // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/N1NxbSVOZas/ySlEKDKkBgAJ
       return {2020, 10, 0, 6, 4};
     case kM87:
-      // This release is not yet scheduled, so this date is a guess.
-      // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/N1NxbSVOZas/ySlEKDKkBgAJ
       return {2020, 11, 0, 17, 4};
     case kM88:
-      // This release is not yet scheduled, so this date is a guess.
-      // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/N1NxbSVOZas/ySlEKDKkBgAJ
       return {2021, 1, 0, 19, 4};
+    case kM89:
+      return {2021, 3, 0, 2, 4};
   }
 
   NOTREACHED();
@@ -575,6 +568,11 @@
                   "RTCConfiguration.encodedInsertableStreams", kM88,
                   "6321945865879552")};
 
+    case WebFeature::kCommaSeparatorInAllowAttribute:
+      return {"CommaSeparatorInAllowAttribute", kM89,
+              ReplacedWillBeRemoved("Comma separator in iframe allow attribute",
+                                    "semicolons", kM89, "5740835259809792")};
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
       return {"NotDeprecated", kUnknown, ""};
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index e6ac373..c440d6f 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -257,17 +257,16 @@
   mojo::AssociatedRemote<mojom::blink::FrameWidget> frame_widget_remote;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidget>
       frame_widget_receiver =
-          frame_widget_remote
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::FrameWidgetHost> frame_widget_host;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost>
       frame_widget_host_receiver =
-          frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::Widget> widget_remote;
   mojo::PendingAssociatedReceiver<mojom::blink::Widget> widget_receiver =
-      widget_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   // Create a local root, if necessary.
   if (!frame->Parent()) {
@@ -346,17 +345,16 @@
   mojo::AssociatedRemote<mojom::blink::FrameWidget> frame_widget_remote;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidget>
       frame_widget_receiver =
-          frame_widget_remote
-              .BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::FrameWidgetHost> frame_widget_host;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost>
       frame_widget_host_receiver =
-          frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::Widget> widget_remote;
   mojo::PendingAssociatedReceiver<mojom::blink::Widget> widget_receiver =
-      widget_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   WebFrameWidget* frame_widget = WebFrameWidget::CreateForChildLocalRoot(
       widget_client, frame, frame_widget_host.Unbind(),
@@ -434,16 +432,16 @@
   mojo::AssociatedRemote<mojom::blink::FrameWidget> frame_widget;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidget>
       frame_widget_receiver =
-          frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::FrameWidgetHost> frame_widget_host;
   mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost>
       frame_widget_host_receiver =
-          frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+          frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::AssociatedRemote<mojom::blink::Widget> widget_remote;
   mojo::PendingAssociatedReceiver<mojom::blink::Widget> widget_receiver =
-      widget_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      widget_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   // TODO(dcheng): The main frame widget currently has a special case.
   // Eliminate this once WebView is no longer a WebWidget.
@@ -760,7 +758,7 @@
 mojo::PendingAssociatedRemote<mojom::blink::WidgetHost>
 TestWebWidgetClient::BindNewWidgetHost() {
   receiver_.reset();
-  return receiver_.BindNewEndpointAndPassDedicatedRemoteForTesting();
+  return receiver_.BindNewEndpointAndPassDedicatedRemote();
 }
 
 bool TestWebWidgetClient::HaveScrollEventHandlers() const {
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 29079e5c..137aade 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -152,6 +152,7 @@
 #include "third_party/blink/renderer/core/page/plugin_script_forbidden_scope.h"
 #include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
+#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 #include "third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/object_painter.h"
@@ -357,6 +358,13 @@
       WTF::BindRepeating(&LocalFrame::BindToHighPriorityReceiver,
                          WrapWeakPersistent(this)),
       GetTaskRunner(blink::TaskType::kInternalHighPriorityLocalFrame));
+
+  if (IsMainFrame()) {
+    GetInterfaceRegistry()->AddInterface(
+        WTF::BindRepeating(&LocalFrame::BindTextFragmentSelectorProducer,
+                           WrapWeakPersistent(this)));
+  }
+
   SetOpenerDoNotNotify(opener);
   loader_.Init();
 }
@@ -455,6 +463,7 @@
   visitor->Trace(receiver_);
   visitor->Trace(main_frame_receiver_);
   visitor->Trace(high_priority_frame_receiver_);
+  visitor->Trace(text_fragment_selector_generator_);
   Frame::Trace(visitor);
   Supplementable<LocalFrame>::Trace(visitor);
 }
@@ -592,7 +601,11 @@
   // up calling back to LocalFrameClient via WindowProxy.
   GetScriptController().ClearForClose();
 
-  DCHECK(!view_->IsAttached());
+  if (text_fragment_selector_generator_)
+    text_fragment_selector_generator_->ClearSelection();
+
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!view_->IsAttached());
   SetView(nullptr);
 
   GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
@@ -1386,6 +1399,10 @@
   }
   DCHECK(ad_tracker_ ? RuntimeEnabledFeatures::AdTaggingEnabled()
                      : !RuntimeEnabledFeatures::AdTaggingEnabled());
+  if (IsMainFrame()) {
+    text_fragment_selector_generator_ =
+        MakeGarbageCollected<TextFragmentSelectorGenerator>();
+  }
 
   Initialize();
 
@@ -3166,6 +3183,16 @@
       GetTaskRunner(blink::TaskType::kInternalHighPriorityLocalFrame));
 }
 
+void LocalFrame::BindTextFragmentSelectorProducer(
+    mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+        receiver) {
+  if (IsDetached() || !text_fragment_selector_generator_)
+    return;
+
+  text_fragment_selector_generator_->BindTextFragmentSelectorProducer(
+      std::move(receiver));
+}
+
 SpellChecker& LocalFrame::GetSpellChecker() const {
   DCHECK(DomWindow());
   return DomWindow()->GetSpellChecker();
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 785b893..213f430 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -45,6 +45,7 @@
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/reporting_observer.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/optimization_guide/optimization_guide.mojom-blink.h"
 #include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h"
@@ -130,6 +131,7 @@
 class ScriptController;
 class SmoothScrollSequencer;
 class SpellChecker;
+class TextFragmentSelectorGenerator;
 class TextSuggestionController;
 class VirtualKeyboardOverlayChangedObserver;
 class WebContentSettingsClient;
@@ -684,6 +686,10 @@
     return LocalFrameToken(GetFrameToken());
   }
 
+  TextFragmentSelectorGenerator* GetTextFragmentSelectorGenerator() const {
+    return text_fragment_selector_generator_;
+  }
+
  private:
   friend class FrameNavigationDisabler;
   FRIEND_TEST_ALL_PREFIXES(LocalFrameTest, CharacterIndexAtPointWithPinchZoom);
@@ -756,6 +762,10 @@
   void BindToHighPriorityReceiver(
       mojo::PendingReceiver<mojom::blink::HighPriorityLocalFrame> receiver);
 
+  void BindTextFragmentSelectorProducer(
+      mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+          receiver);
+
   std::unique_ptr<FrameScheduler> frame_scheduler_;
 
   // Holds all PauseSubresourceLoadingHandles allowing either |this| to delete
@@ -897,6 +907,8 @@
   Member<RawSystemClipboard> raw_system_clipboard_;
 
   mojom::blink::BlinkOptimizationGuideHintsPtr optimization_guide_hints_;
+
+  Member<TextFragmentSelectorGenerator> text_fragment_selector_generator_;
 };
 
 inline FrameLoader& LocalFrame::Loader() const {
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
index dbc617f5..6c647a44 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc
@@ -196,10 +196,10 @@
 
 // https://html.spec.whatwg.org/C/#concept-upgrade-an-element
 void CustomElementDefinition::Upgrade(Element& element) {
-  // 4.13.5.1 If element is custom, then return.
-  // 4.13.5.2 If element's custom element state is "failed", then return.
-  if (element.GetCustomElementState() == CustomElementState::kCustom ||
-      element.GetCustomElementState() == CustomElementState::kFailed) {
+  // 4.13.5.1 If element's custom element state is not "undefined" or
+  // "uncustomized", then return.
+  if (element.GetCustomElementState() != CustomElementState::kUndefined &&
+      element.GetCustomElementState() != CustomElementState::kUncustomized) {
     return;
   }
 
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.cc b/third_party/blink/renderer/core/html/custom/element_internals.cc
index 70f3934..9f6f846d 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.cc
+++ b/third_party/blink/renderer/core/html/custom/element_internals.cc
@@ -329,10 +329,11 @@
   if (Target().IsFormAssociatedCustomElement())
     return true;
   // Custom element could be in the process of upgrading here, during which
-  // it will have state kFailed according to:
+  // it will have state kFailed or kPreCustomized according to:
   // https://html.spec.whatwg.org/multipage/custom-elements.html#upgrades
   if (Target().GetCustomElementState() != CustomElementState::kUndefined &&
-      Target().GetCustomElementState() != CustomElementState::kFailed) {
+      Target().GetCustomElementState() != CustomElementState::kFailed &&
+      Target().GetCustomElementState() != CustomElementState::kPreCustomized) {
     return false;
   }
   // An element is in "undefined" state in its constructor JavaScript code.
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 28f1d9a..500eb4e6 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1583,6 +1583,7 @@
         "Unable to attach ElementInternals to a customized built-in element.");
     return nullptr;
   }
+
   CustomElementRegistry* registry = CustomElement::Registry(*this);
   auto* definition =
       registry ? registry->DefinitionForName(localName()) : nullptr;
@@ -1592,6 +1593,7 @@
         "Unable to attach ElementInternals to non-custom elements.");
     return nullptr;
   }
+
   if (definition->DisableInternals()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
@@ -1604,6 +1606,23 @@
         "ElementInternals for the specified element was already attached.");
     return nullptr;
   }
+
+  // If element's custom element state is not "precustomized" or "custom",
+  // throw "NotSupportedError" DOMException.
+  if (GetCustomElementState() != CustomElementState::kCustom &&
+      GetCustomElementState() != CustomElementState::kPreCustomized) {
+    if (RuntimeEnabledFeatures::DeclarativeShadowDOMEnabled(
+            GetExecutionContext())) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kNotSupportedError,
+          "The attachInternals() function cannot be called prior to the "
+          "execution of the custom element constructor.");
+      return nullptr;
+    }
+    UseCounter::Count(GetDocument(),
+                      WebFeature::kElementAttachInternalsBeforeConstructor);
+  }
+
   UseCounter::Count(GetDocument(), WebFeature::kElementAttachInternals);
   SetDidAttachInternals();
   return &EnsureElementInternals();
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index 3d708f4..a367672 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/feature_policy/iframe_policy.h"
 #include "third_party/blink/renderer/core/fetch/trust_token_issuance_authorization.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
+#include "third_party/blink/renderer/core/frame/deprecation.h"
 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/html/trust_token_attribute_parsing.h"
@@ -271,8 +272,9 @@
                           WebFeature::kFeaturePolicyAllowAttribute);
       }
       if (value.Contains(',')) {
-        UseCounter::Count(GetDocument(),
-                          WebFeature::kCommaSeparatorInAllowAttribute);
+        Deprecation::CountDeprecation(
+            GetDocument().GetExecutionContext(),
+            WebFeature::kCommaSeparatorInAllowAttribute);
       }
     }
   } else if (name == html_names::kDisallowdocumentaccessAttr &&
diff --git a/third_party/blink/renderer/core/html/html_iframe_element_test.cc b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
index 5dfeb16..f7ed45f 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element_test.cc
@@ -403,7 +403,7 @@
       << ConsoleMessages().front();
 }
 
-TEST_F(HTMLIFrameElementSimTest, CommaSeparatorIsCounted) {
+TEST_F(HTMLIFrameElementSimTest, CommaSeparatorIsDeprecated) {
   EXPECT_FALSE(
       GetDocument().Loader()->GetUseCounterHelper().HasRecordedMeasurement(
           WebFeature::kCommaSeparatorInAllowAttribute));
@@ -414,6 +414,12 @@
       allow="fullscreen, geolocation"></iframe>
   )");
 
+  EXPECT_EQ(ConsoleMessages().size(), 1u)
+      << "Comma separator in allow attribute should log a deprecation message "
+         "to the console.";
+  EXPECT_TRUE(ConsoleMessages().front().Contains("5740835259809792"))
+      << "Console message should mention the chromestatus entry.";
+
   EXPECT_TRUE(
       GetDocument().Loader()->GetUseCounterHelper().HasRecordedMeasurement(
           WebFeature::kCommaSeparatorInAllowAttribute));
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index b7644d32..870c7f5 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -989,8 +989,10 @@
           document, tag_name, GetCreateElementFlags(), is);
     }
     // Definition for the created element does not exist here and it cannot be
-    // custom or failed.
+    // custom, precustomized, or failed.
     DCHECK_NE(element->GetCustomElementState(), CustomElementState::kCustom);
+    DCHECK_NE(element->GetCustomElementState(),
+              CustomElementState::kPreCustomized);
     DCHECK_NE(element->GetCustomElementState(), CustomElementState::kFailed);
 
     // TODO(dominicc): Move these steps so they happen for custom
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc
index 4ad7df7..cd9a5bf5 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 
 namespace blink {
@@ -144,4 +145,8 @@
          HasTagName(mathml_names::kMsTag);
 }
 
+bool MathMLElement::ShouldForceNGLayout() const {
+  return ComputedStyleRef().IsDisplayMathType();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.h b/third_party/blink/renderer/core/mathml/mathml_element.h
index 9b2fc77..b4ab8cd4 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.h
+++ b/third_party/blink/renderer/core/mathml/mathml_element.h
@@ -55,8 +55,7 @@
 
  private:
   // Force NG layout as MathML elements don't have legacy layout implementation.
-  // TODO(crbug.com/1127197): Check the display of the computed style.
-  bool ShouldForceNGLayout() const final { return true; }
+  bool ShouldForceNGLayout() const final;
 };
 
 template <typename T>
diff --git a/third_party/blink/renderer/core/mathml/mathml_padded_element.cc b/third_party/blink/renderer/core/mathml/mathml_padded_element.cc
index 858cc37..6f54654d 100644
--- a/third_party/blink/renderer/core/mathml/mathml_padded_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_padded_element.cc
@@ -85,7 +85,7 @@
 LayoutObject* MathMLPaddedElement::CreateLayoutObject(
     const ComputedStyle& style,
     LegacyLayout legacy) {
-  DCHECK_NE(legacy, LegacyLayout::kForce);
+  DCHECK(!style.IsDisplayMathType() || legacy != LegacyLayout::kForce);
   if (!RuntimeEnabledFeatures::MathMLCoreEnabled() ||
       !style.IsDisplayMathType())
     return MathMLElement::CreateLayoutObject(style, legacy);
diff --git a/third_party/blink/renderer/core/mathml/mathml_radical_element.cc b/third_party/blink/renderer/core/mathml/mathml_radical_element.cc
index fcabbd39..5409492b 100644
--- a/third_party/blink/renderer/core/mathml/mathml_radical_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_radical_element.cc
@@ -19,7 +19,7 @@
 LayoutObject* MathMLRadicalElement::CreateLayoutObject(
     const ComputedStyle& style,
     LegacyLayout legacy) {
-  DCHECK_NE(legacy, LegacyLayout::kForce);
+  DCHECK(!style.IsDisplayMathType() || legacy != LegacyLayout::kForce);
   if (!RuntimeEnabledFeatures::MathMLCoreEnabled() ||
       !style.IsDisplayMathType())
     return MathMLElement::CreateLayoutObject(style, legacy);
diff --git a/third_party/blink/renderer/core/mathml/mathml_row_element.cc b/third_party/blink/renderer/core/mathml/mathml_row_element.cc
index 535f9ed..57abe138 100644
--- a/third_party/blink/renderer/core/mathml/mathml_row_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_row_element.cc
@@ -15,9 +15,9 @@
 
 LayoutObject* MathMLRowElement::CreateLayoutObject(const ComputedStyle& style,
                                                    LegacyLayout legacy) {
-  DCHECK_NE(legacy, LegacyLayout::kForce);
+  DCHECK(!style.IsDisplayMathType() || legacy != LegacyLayout::kForce);
   if (!RuntimeEnabledFeatures::MathMLCoreEnabled() ||
-      (!style.IsDisplayMathType() && !HasTagName(mathml_names::kMathTag)))
+      !style.IsDisplayMathType())
     return MathMLElement::CreateLayoutObject(style, legacy);
   return new LayoutNGMathMLBlock(this);
 }
diff --git a/third_party/blink/renderer/core/page/DEPS b/third_party/blink/renderer/core/page/DEPS
index 3799876..1ddb8e26 100644
--- a/third_party/blink/renderer/core/page/DEPS
+++ b/third_party/blink/renderer/core/page/DEPS
@@ -7,4 +7,7 @@
   "chrome_client_impl_test\.cc": [
     "+base/run_loop.h",
   ],
+  "text_fragment_selector_generator_test\.cc": [
+    "+base/run_loop.h",
+  ],
 }
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 34bae4f..dc69a0c 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -524,11 +524,14 @@
 
 void ContextMenuController::UpdateTextFragmentSelectorGenerator(
     LocalFrame* selected_frame) {
+  if (!selected_frame->GetTextFragmentSelectorGenerator())
+    return;
+
   VisibleSelectionInFlatTree selection =
       selected_frame->Selection().ComputeVisibleSelectionInFlatTree();
   EphemeralRangeInFlatTree selection_range(selection.Start(), selection.End());
-  page_->GetTextFragmentSelectorGenerator().UpdateSelection(selected_frame,
-                                                            selection_range);
+  selected_frame->GetTextFragmentSelectorGenerator()->UpdateSelection(
+      selected_frame, selection_range);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 4acfdcfa..97cb430 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -75,7 +75,6 @@
 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
 #include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
-#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 #include "third_party/blink/renderer/core/page/validation_message_client_impl.h"
@@ -226,9 +225,7 @@
       next_related_page_(this),
       prev_related_page_(this),
       autoplay_flags_(0),
-      web_text_autosizer_page_info_({0, 0, 1.f}),
-      text_fragment_selector_generator_(
-          MakeGarbageCollected<TextFragmentSelectorGenerator>()) {
+      web_text_autosizer_page_info_({0, 0, 1.f}) {
   DCHECK(!AllPages().Contains(this));
   AllPages().insert(this);
 
@@ -343,7 +340,6 @@
 
 void Page::DocumentDetached(Document* document) {
   pointer_lock_controller_->DocumentDetached(document);
-  text_fragment_selector_generator_->DocumentDetached(document);
   context_menu_controller_->DocumentDetached(document);
   if (validation_message_client_)
     validation_message_client_->DocumentDetached(*document);
@@ -921,7 +917,6 @@
   visitor->Trace(plugins_changed_observers_);
   visitor->Trace(next_related_page_);
   visitor->Trace(prev_related_page_);
-  visitor->Trace(text_fragment_selector_generator_);
   Supplementable<Page>::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index b6a1caa..4e4e496 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -76,7 +76,6 @@
 class PluginData;
 class PluginsChangedObserver;
 class PointerLockController;
-class TextFragmentSelectorGenerator;
 class ScopedPagePauser;
 class ScrollingCoordinator;
 class ScrollbarTheme;
@@ -377,10 +376,6 @@
 
   static void PrepareForLeakDetection();
 
-  TextFragmentSelectorGenerator& GetTextFragmentSelectorGenerator() const {
-    return *text_fragment_selector_generator_;
-  }
-
  private:
   friend class ScopedPagePauser;
 
@@ -502,8 +497,6 @@
 
   WebScopedVirtualTimePauser history_navigation_virtual_time_pauser_;
 
-  const Member<TextFragmentSelectorGenerator> text_fragment_selector_generator_;
-
   DISALLOW_COPY_AND_ASSIGN(Page);
 };
 
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
index 42c93d9..787fb1b 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
@@ -81,4 +82,28 @@
 
 TextFragmentSelector::TextFragmentSelector(SelectorType type) : type_(type) {}
 
+String TextFragmentSelector::ToString() const {
+  StringBuilder selector;
+  if (!prefix_.IsEmpty()) {
+    selector.Append(EncodeWithURLEscapeSequences(prefix_));
+    selector.Append("-,");
+  }
+
+  if (!start_.IsEmpty()) {
+    selector.Append(EncodeWithURLEscapeSequences(start_));
+  }
+
+  if (!end_.IsEmpty()) {
+    selector.Append(",");
+    selector.Append(EncodeWithURLEscapeSequences(end_));
+  }
+
+  if (!suffix_.IsEmpty()) {
+    selector.Append(",-");
+    selector.Append(EncodeWithURLEscapeSequences(suffix_));
+  }
+
+  return selector.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
index 06013500..3161748 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
@@ -34,10 +34,11 @@
   ~TextFragmentSelector() = default;
 
   SelectorType Type() const { return type_; }
-  String Start() const { return start_; }
-  String End() const { return end_; }
-  String Prefix() const { return prefix_; }
-  String Suffix() const { return suffix_; }
+  const String& Start() const { return start_; }
+  const String& End() const { return end_; }
+  const String& Prefix() const { return prefix_; }
+  const String& Suffix() const { return suffix_; }
+  String ToString() const;
 
  private:
   const SelectorType type_;
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
index 5164e55..bcecdb3 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/interface_registry.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/finder/find_buffer.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
@@ -18,6 +20,11 @@
 void TextFragmentSelectorGenerator::UpdateSelection(
     LocalFrame* selection_frame,
     const EphemeralRangeInFlatTree& selection_range) {
+  DCHECK(selection_frame);
+
+  // Scroll-to-text doesn't support iframes.
+  DCHECK(selection_frame->IsMainFrame());
+
   selection_frame_ = selection_frame;
   selection_range_ = MakeGarbageCollected<Range>(
       selection_range.GetDocument(),
@@ -25,7 +32,21 @@
       ToPositionInDOMTree(selection_range.EndPosition()));
 }
 
-void TextFragmentSelectorGenerator::GenerateSelector() {
+void TextFragmentSelectorGenerator::BindTextFragmentSelectorProducer(
+    mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+        producer) {
+  selector_producer_.reset();
+  selector_producer_.Bind(
+      std::move(producer),
+      selection_frame_->GetTaskRunner(blink::TaskType::kInternalDefault));
+}
+
+void TextFragmentSelectorGenerator::GenerateSelector(
+    GenerateSelectorCallback callback) {
+  DCHECK(selection_range_);
+  DCHECK(callback);
+
+  pending_generate_selector_callback_ = std::move(callback);
   EphemeralRangeInFlatTree ephemeral_range(selection_range_);
 
   const TextFragmentSelector kInvalidSelector(
@@ -38,8 +59,10 @@
       FindBuffer::GetFirstBlockLevelAncestorInclusive(
           *ephemeral_range.EndPosition().AnchorNode());
 
-  if (!start_first_block_ancestor.isSameNode(&end_first_block_ancestor))
+  if (!start_first_block_ancestor.isSameNode(&end_first_block_ancestor)) {
     NotifySelectorReady(kInvalidSelector);
+    return;
+  }
 
   // TODO(gayane): If same node, need to check if start and end are interrupted
   // by a block. Example: <div>start of the selection <div> sub block </div>end
@@ -50,8 +73,10 @@
   String selected_text = PlainText(ephemeral_range);
 
   if (selected_text.length() < kNoContextMinChars ||
-      selected_text.length() > kExactTextMaxChars)
+      selected_text.length() > kExactTextMaxChars) {
     NotifySelectorReady(kInvalidSelector);
+    return;
+  }
 
   selector_ = std::make_unique<TextFragmentSelector>(
       TextFragmentSelector::SelectorType::kExact, selected_text, "", "", "");
@@ -72,19 +97,14 @@
   }
 }
 
-void TextFragmentSelectorGenerator::SetCallbackForTesting(
-    base::OnceCallback<void(const TextFragmentSelector&)> callback) {
-  callback_for_tests_ = std::move(callback);
-}
-
 void TextFragmentSelectorGenerator::NotifySelectorReady(
     const TextFragmentSelector& selector) {
-  if (!callback_for_tests_.is_null())
-    std::move(callback_for_tests_).Run(selector);
+  DCHECK(pending_generate_selector_callback_);
+  std::move(pending_generate_selector_callback_).Run(selector.ToString());
 }
 
-void TextFragmentSelectorGenerator::DocumentDetached(Document* document) {
-  if (selection_range_ && selection_range_->OwnerDocument() == *document) {
+void TextFragmentSelectorGenerator::ClearSelection() {
+  if (selection_range_) {
     selection_range_->Dispose();
     selection_range_ = nullptr;
     selection_frame_ = nullptr;
@@ -94,6 +114,7 @@
 void TextFragmentSelectorGenerator::Trace(Visitor* visitor) const {
   visitor->Trace(selection_frame_);
   visitor->Trace(selection_range_);
+  visitor->Trace(selector_producer_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
index 18220769..a0de513 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
@@ -5,9 +5,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_GENERATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_GENERATOR_H_
 
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
 
 namespace blink {
 
@@ -21,30 +23,33 @@
 // triggered when users request "link to text" for the selected text.
 class CORE_EXPORT TextFragmentSelectorGenerator final
     : public GarbageCollected<TextFragmentSelectorGenerator>,
-      public TextFragmentFinder::Client {
+      public TextFragmentFinder::Client,
+      public blink::mojom::blink::TextFragmentSelectorProducer {
  public:
   explicit TextFragmentSelectorGenerator() = default;
 
+  void BindTextFragmentSelectorProducer(
+      mojo::PendingReceiver<mojom::blink::TextFragmentSelectorProducer>
+          producer);
+
   // Sets the frame and range of the current selection.
   void UpdateSelection(LocalFrame* selection_frame,
                        const EphemeralRangeInFlatTree& selection_range);
 
+  // blink::mojom::blink::TextFragmentSelectorProducer interface
   // Generates selector for current selection.
-  void GenerateSelector();
+  void GenerateSelector(GenerateSelectorCallback callback) override;
 
   // TextFragmentFinder::Client interface
   void DidFindMatch(const EphemeralRangeInFlatTree& match,
                     const TextFragmentAnchorMetrics::Match match_metrics,
                     bool is_unique) override;
 
-  // Sets the callback used for notifying test results of |GenerateSelector|.
-  void SetCallbackForTesting(
-      base::OnceCallback<void(const TextFragmentSelector&)> callback);
-
   // Notifies the results of |GenerateSelector|.
   void NotifySelectorReady(const TextFragmentSelector& selector);
 
-  void DocumentDetached(Document* document);
+  // Releases members if necessary.
+  void ClearSelection();
 
   void Trace(Visitor*) const;
 
@@ -53,7 +58,12 @@
   Member<Range> selection_range_;
   std::unique_ptr<TextFragmentSelector> selector_;
 
-  base::OnceCallback<void(const TextFragmentSelector&)> callback_for_tests_;
+  // Used for communication between |TextFragmentSelectorGenerator| in renderer
+  // and |TextFragmentSelectorClientImpl| in browser.
+  HeapMojoReceiver<blink::mojom::blink::TextFragmentSelectorProducer,
+                   TextFragmentSelectorGenerator>
+      selector_producer_{this, nullptr};
+  GenerateSelectorCallback pending_generate_selector_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(TextFragmentSelectorGenerator);
 };
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
index ddb466f7..4a3e33c 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator_test.cc
@@ -6,8 +6,14 @@
 
 #include <gtest/gtest.h>
 
+#include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 
@@ -19,6 +25,33 @@
     SimTest::SetUp();
     WebView().MainFrameWidget()->Resize(WebSize(800, 600));
   }
+
+  void GenerateAndVerifySelector(Position selected_start,
+                                 Position selected_end,
+                                 String expected_selector) {
+    GetDocument()
+        .GetFrame()
+        ->GetTextFragmentSelectorGenerator()
+        ->UpdateSelection(GetDocument().GetFrame(),
+                          ToEphemeralRangeInFlatTree(
+                              EphemeralRange(selected_start, selected_end)));
+
+    bool callback_called = false;
+    auto lambda = [](bool& callback_called, const String& expected_selector,
+                     const String& selector) {
+      EXPECT_EQ(selector, expected_selector);
+      callback_called = true;
+    };
+    auto callback =
+        WTF::Bind(lambda, std::ref(callback_called), expected_selector);
+    GetDocument()
+        .GetFrame()
+        ->GetTextFragmentSelectorGenerator()
+        ->GenerateSelector(std::move(callback));
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_TRUE(callback_called);
+  }
 };
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector) {
@@ -33,22 +66,9 @@
   Node* first_paragraph = GetDocument().getElementById("first")->firstChild();
   const auto& selected_start = Position(first_paragraph, 0);
   const auto& selected_end = Position(first_paragraph, 28);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "First paragraph text that is");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "First%20paragraph%20text%20that%20is");
 }
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithNestedTextNodes) {
@@ -64,22 +84,9 @@
   const auto& selected_start = Position(first_paragraph->firstChild(), 0);
   const auto& selected_end =
       Position(first_paragraph->firstChild()->nextSibling()->firstChild(), 6);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "First paragraph text that is longer");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "First%20paragraph%20text%20that%20is%20longer");
 }
 
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithExtraSpace) {
@@ -94,22 +101,9 @@
   Node* second_paragraph = GetDocument().getElementById("second")->firstChild();
   const auto& selected_start = Position(second_paragraph, 0);
   const auto& selected_end = Position(second_paragraph, 23);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(), TextFragmentSelector::SelectorType::kExact);
-        EXPECT_EQ(selector.Start(), "Second paragraph text");
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end,
+                            "Second%20paragraph%20text");
 }
 
 // Multi-block selection is currently not implemented.
@@ -126,22 +120,8 @@
   Node* second_paragraph = GetDocument().getElementById("second")->firstChild();
   const auto& selected_start = Position(first_paragraph, 0);
   const auto& selected_end = Position(second_paragraph, 6);
-  bool callback_called = false;
-  base::OnceCallback<void(const TextFragmentSelector&)> callback =
-      base::BindLambdaForTesting([&](const TextFragmentSelector& selector) {
-        EXPECT_EQ(selector.Type(),
-                  TextFragmentSelector::SelectorType::kInvalid);
-        callback_called = true;
-      });
 
-  TextFragmentSelectorGenerator generator;
-  generator.UpdateSelection(
-      GetDocument().GetFrame(),
-      ToEphemeralRangeInFlatTree(EphemeralRange(selected_start, selected_end)));
-  generator.SetCallbackForTesting(std::move(callback));
-  generator.GenerateSelector();
-
-  EXPECT_TRUE(callback_called);
+  GenerateAndVerifySelector(selected_start, selected_end, "");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_test.cc b/third_party/blink/renderer/modules/cache_storage/cache_test.cc
index ad61fb6..b00892bf 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_test.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache_test.cc
@@ -267,8 +267,7 @@
     cache_ = std::move(cache);
     receiver_ = std::make_unique<
         mojo::AssociatedReceiver<mojom::blink::CacheStorageCache>>(
-        cache_.get(),
-        cache_remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+        cache_.get(), cache_remote.BindNewEndpointAndPassDedicatedReceiver());
     return MakeGarbageCollected<Cache>(
         fetcher, cache_remote.Unbind(),
         blink::scheduler::GetSingleThreadTaskRunnerForTesting());
diff --git a/third_party/blink/renderer/modules/canvas/BUILD.gn b/third_party/blink/renderer/modules/canvas/BUILD.gn
index dcbce9e..098a4e35 100644
--- a/third_party/blink/renderer/modules/canvas/BUILD.gn
+++ b/third_party/blink/renderer/modules/canvas/BUILD.gn
@@ -48,6 +48,9 @@
   seed_corpuses = [ "//third_party/blink/web_tests/fast/canvas" ]
   deps = [
     "../../platform:blink_fuzzer_test_support",
+    "//base/test:test_support",
     "//third_party/blink/renderer/core",
+    "//third_party/blink/renderer/core:testing",
+    "//third_party/blink/renderer/platform:test_support",
   ]
 }
diff --git a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
index 72eb5b7..c8431ee4 100644
--- a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
+++ b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
@@ -10,7 +10,9 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/events/pointer_event.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
@@ -97,14 +99,39 @@
     layout_box = layout_view;
   }
 
-  // TODO(1052145): Move this further into the document lifecycle when layout
-  // is up to date.
-  PhysicalRect physical_rect_area = layout_box->LocalToAbsoluteRect(
+  // Intersect with the visible viewport so that the presentation area can't
+  // extend beyond the edges of the window or over the scrollbars. The frame
+  // visual viewport loop accounts for all iframe viewports, and the page visual
+  // viewport accounts for the full window. Convert everything to root frame
+  // coordinates in order to make sure offsets aren't lost along the way.
+  //
+  // TODO(1052145): Overflow and clip-path clips are ignored here, which results
+  // in delegated ink trails ignoring the clips and appearing incorrectly in
+  // some situations. This could also occur due to transformations, as the
+  // |presenation_area| is currently always a rectilinear bounding box. Ideally
+  // both of these situations are handled correctly, or the trail doesn't appear
+  // if we are unable to accurately render it.
+  PhysicalRect border_box_rect_absolute = layout_box->LocalToAbsoluteRect(
       layout_box->PhysicalBorderBoxRect(), kTraverseDocumentBoundaries);
-  gfx::RectF area(physical_rect_area.X().ToFloat(),
-                  physical_rect_area.Y().ToFloat(),
-                  physical_rect_area.Width().ToFloat(),
-                  physical_rect_area.Height().ToFloat());
+
+  while (layout_view->GetFrame()->OwnerLayoutObject()) {
+    PhysicalRect frame_visual_viewport_absolute =
+        layout_view->LocalToAbsoluteRect(
+            PhysicalRect(
+                layout_view->GetScrollableArea()->VisibleContentRect()),
+            kTraverseDocumentBoundaries);
+    border_box_rect_absolute.Intersect(frame_visual_viewport_absolute);
+
+    layout_view = layout_view->GetFrame()->OwnerLayoutObject()->View();
+  }
+
+  border_box_rect_absolute.Intersect(PhysicalRect(
+      local_frame_->GetPage()->GetVisualViewport().VisibleContentRect()));
+
+  gfx::RectF area(border_box_rect_absolute.X().ToFloat(),
+                  border_box_rect_absolute.Y().ToFloat(),
+                  border_box_rect_absolute.Width().ToFloat(),
+                  border_box_rect_absolute.Height().ToFloat());
 
   const double diameter_in_physical_pixels = style->diameter() * effective_zoom;
   std::unique_ptr<viz::DelegatedInkMetadata> metadata =
diff --git a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter_unittest.cc b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter_unittest.cc
index 23169f624..4b974e7 100644
--- a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter_unittest.cc
+++ b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter_unittest.cc
@@ -30,6 +30,7 @@
       : area_(area) {
     area_.Scale(device_pixel_ratio);
   }
+  TestDelegatedInkMetadata() = default;
 
   void ExpectEqual(TestDelegatedInkMetadata actual) const {
     // LayoutUnits cast floats to ints, causing the actual point and area to be
@@ -64,7 +65,19 @@
 }  // namespace
 
 class DelegatedInkTrailPresenterUnitTest : public SimTest {
- protected:
+ public:
+  void SetWebViewSize(float width, float height) {
+    WebView().MainFrameWidget()->Resize(WebSize(width, height));
+  }
+
+  void SetWebViewSizeGreaterThanCanvas(float width, float height) {
+    // The presentation area is intersected with the visible content rect, so
+    // make sure that the page size is larger than the canvas to ensure it
+    // isn't clipped. Adding 1 to the height and width is enough to ensure that
+    // doesn't happen.
+    SetWebViewSize(width + 1, height + 1);
+  }
+
   PointerEvent* CreatePointerMoveEvent(gfx::PointF pt) {
     PointerEventInit* init = PointerEventInit::Create();
     init->setClientX(pt.x());
@@ -84,9 +97,29 @@
   }
 };
 
+// Test scenarios with the presentation area extending beyond the edges of the
+// window to confirm it gets clipped correctly.
+class DelegatedInkTrailPresenterCanvasBeyondViewport
+    : public DelegatedInkTrailPresenterUnitTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  bool CanvasShouldBePastViewport() { return GetParam(); }
+  float GetViewportWidth() const { return kViewportWidth; }
+  float GetViewportHeight() const { return kViewportHeight; }
+  void SetWebViewSize() {
+    DelegatedInkTrailPresenterUnitTest::SetWebViewSize(kViewportWidth,
+                                                       kViewportHeight);
+  }
+
+ private:
+  const float kViewportWidth = 175.f;
+  const float kViewportHeight = 180.f;
+};
+
 // Confirm that all the information is collected and transformed correctly, if
 // necessary. Numbers and color used were chosen arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, CollectAndPropagateMetadata) {
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport,
+       CollectAndPropagateMetadata) {
   SimRequest main_resource("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
   main_resource.Complete(R"HTML(
@@ -108,8 +141,16 @@
   const float kCanvasWidth = 191.f;
   const float kCanvasHeight = 234.f;
 
-  TestDelegatedInkMetadata expected_metadata(
-      gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight));
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kCanvasWidth, kCanvasHeight);
+    expected_metadata.SetArea(gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight));
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(
+        gfx::RectF(0, 0, GetViewportWidth(), GetViewportHeight()));
+  }
 
   DelegatedInkTrailPresenter* presenter = CreatePresenter(
       GetDocument().getElementById("canvas"), GetDocument().GetFrame());
@@ -130,38 +171,10 @@
   expected_metadata.ExpectEqual(GetActualMetadata());
 }
 
-// Confirm that presentation area defaults to the size of the viewport.
-// Numbers and color used were chosen arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, PresentationAreaNotProvided) {
-  const int kViewportHeight = 555;
-  const int kViewportWidth = 333;
-  WebView().MainFrameWidget()->Resize(WebSize(kViewportWidth, kViewportHeight));
-
-  DelegatedInkTrailPresenter* presenter =
-      CreatePresenter(nullptr, GetDocument().GetFrame());
-  DCHECK(presenter);
-
-  TestDelegatedInkMetadata expected_metadata(
-      gfx::RectF(0, 0, kViewportWidth, kViewportHeight));
-
-  InkTrailStyle style;
-  style.setDiameter(3.6);
-  style.setColor("yellow");
-  expected_metadata.SetDiameter(style.diameter());
-  expected_metadata.SetColor(SK_ColorYELLOW);
-
-  gfx::PointF pt(70, 109);
-  presenter->updateInkTrailStartPoint(
-      ToScriptStateForMainWorld(GetDocument().GetFrame()),
-      CreatePointerMoveEvent(pt), &style);
-  expected_metadata.SetPoint(pt);
-
-  expected_metadata.ExpectEqual(GetActualMetadata());
-}
-
 // Confirm that everything is still calculated correctly when the
 // DevicePixelRatio is not 1. Numbers and color used were chosen arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, NotDefaultDevicePixelRatio) {
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport,
+       NotDefaultDevicePixelRatio) {
   const float kZoom = 1.7;
   SetPageZoomFactor(kZoom);
 
@@ -186,8 +199,18 @@
   const float kCanvasWidth = 281.f;
   const float kCanvasHeight = 190.f;
 
-  TestDelegatedInkMetadata expected_metadata(
-      gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight), kZoom);
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kCanvasWidth * kZoom,
+                                    kCanvasHeight * kZoom);
+    expected_metadata = TestDelegatedInkMetadata(
+        gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight), kZoom);
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(
+        gfx::RectF(0, 0, GetViewportWidth(), GetViewportHeight()));
+  }
 
   DelegatedInkTrailPresenter* presenter = CreatePresenter(
       GetDocument().getElementById("canvas"), GetDocument().GetFrame());
@@ -211,7 +234,7 @@
 
 // Confirm that the offset is correct. Numbers and color used were chosen
 // arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasNotAtOrigin) {
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, CanvasNotAtOrigin) {
   SimRequest main_resource("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
   main_resource.Complete(R"HTML(
@@ -224,8 +247,8 @@
       width: 250px;
       height: 350px;
       position: fixed;
-      top: 375px;
-      left: 166px;
+      top: 59px;
+      left: 16px;
     }
     </style>
     <canvas id='canvas'></canvas>
@@ -235,11 +258,23 @@
 
   const float kCanvasWidth = 250.f;
   const float kCanvasHeight = 350.f;
-  const float kCanvasTopOffset = 375.f;
-  const float kCanvasLeftOffset = 166.f;
+  const float kCanvasTopOffset = 59.f;
+  const float kCanvasLeftOffset = 16.f;
 
-  TestDelegatedInkMetadata expected_metadata(gfx::RectF(
-      kCanvasLeftOffset, kCanvasTopOffset, kCanvasWidth, kCanvasHeight));
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kCanvasWidth + kCanvasLeftOffset,
+                                    kCanvasHeight + kCanvasTopOffset);
+    expected_metadata.SetArea(gfx::RectF(kCanvasLeftOffset, kCanvasTopOffset,
+                                         kCanvasWidth, kCanvasHeight));
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(
+        gfx::RectF(kCanvasLeftOffset, kCanvasTopOffset,
+                   GetViewportWidth() - kCanvasLeftOffset,
+                   GetViewportHeight() - kCanvasTopOffset));
+  }
 
   DelegatedInkTrailPresenter* presenter = CreatePresenter(
       GetDocument().getElementById("canvas"), GetDocument().GetFrame());
@@ -262,7 +297,7 @@
 
 // Confirm that values, specifically offsets, are transformed correctly when
 // the canvas is in an iframe. Numbers and color used were chosen arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasInIFrame) {
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, CanvasInIFrame) {
   SimRequest main_resource("https://example.com/test.html", "text/html");
   SimRequest frame_resource("https://example.com/iframe.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -272,7 +307,7 @@
     body {
       margin: 0;
     }
-    iframe {
+    #iframe {
       width: 500px;
       height: 500px;
       position: fixed;
@@ -309,20 +344,35 @@
   const float kIframeBorder = 2.f;
   const float kIframeLeftOffset = 57.f + kIframeBorder;
   const float kIframeTopOffset = 26.f + kIframeBorder;
+  const float kIframeHeight = 500.f;
+  const float kIframeWidth = 500.f;
   const float kCanvasLeftOffset = 16.f;
   const float kCanvasTopOffset = 33.f;
   const float kCanvasHeight = 250.f;
   const float kCanvasWidth = 250.f;
 
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kIframeWidth + kIframeLeftOffset,
+                                    kIframeHeight + kIframeTopOffset);
+    expected_metadata.SetArea(gfx::RectF(kIframeLeftOffset + kCanvasLeftOffset,
+                                         kIframeTopOffset + kCanvasTopOffset,
+                                         kCanvasWidth, kCanvasHeight));
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(gfx::RectF(
+        kIframeLeftOffset + kCanvasLeftOffset,
+        kIframeTopOffset + kCanvasTopOffset,
+        GetViewportWidth() - (kIframeLeftOffset + kCanvasLeftOffset),
+        GetViewportHeight() - (kIframeTopOffset + kCanvasTopOffset)));
+  }
+
   auto* iframe_element =
       To<HTMLIFrameElement>(GetDocument().getElementById("iframe"));
   auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame());
   Document* iframe_document = iframe_element->contentDocument();
 
-  TestDelegatedInkMetadata expected_metadata(gfx::RectF(
-      kIframeLeftOffset + kCanvasLeftOffset,
-      kIframeTopOffset + kCanvasTopOffset, kCanvasWidth, kCanvasHeight));
-
   DelegatedInkTrailPresenter* presenter = CreatePresenter(
       iframe_localframe->GetDocument()->getElementById("canvas"),
       iframe_document->GetFrame());
@@ -344,9 +394,136 @@
   expected_metadata.ExpectEqual(GetActualMetadata());
 }
 
+// Confirm that values, specifically offsets, are transformed correctly when
+// the canvas is in a nested iframe. Numbers and color used were chosen
+// arbitrarily.
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, NestedIframe) {
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+  SimRequest frame2_resource("https://example.com/iframe2.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #OuterIframe {
+      width: 500px;
+      height: 500px;
+      position: fixed;
+      top: 26px;
+      left: 57px;
+    }
+    </style>
+    <iframe id='OuterIframe' src='https://example.com/iframe.html'>
+    </iframe>
+  )HTML");
+
+  frame_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #InnerIframe {
+      width: 400px;
+      height: 400px;
+      position: fixed;
+      top: 11px;
+      left: 18px;
+    }
+    </style>
+    <iframe id='InnerIframe' src='https://example.com/iframe2.html'>
+    </iframe>
+    )HTML");
+
+  frame2_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    canvas {
+      width: 250px;
+      height: 250px;
+      position: fixed;
+      top: 28px;
+      left: 6px;
+    }
+    </style>
+    <canvas id='canvas'></canvas>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  // When creating the expected metadata, we have to take into account the
+  // offsets that are applied to the iframe that the canvas is in, and the 2px
+  // border around the iframe.
+  const float kIframeBorder = 2.f;
+  const float kOuterIframeLeftOffset = 57.f + kIframeBorder;
+  const float kOuterIframeTopOffset = 26.f + kIframeBorder;
+  const float kOuterIframeHeight = 500.f;
+  const float kOuterIframeWidth = 500.f;
+  const float kInnerIframeLeftOffset =
+      kOuterIframeLeftOffset + 18.f + kIframeBorder;
+  const float kInnerIframeTopOffset =
+      kOuterIframeTopOffset + 11.f + kIframeBorder;
+  const float kCanvasLeftOffset = 6.f;
+  const float kCanvasTopOffset = 28.f;
+  const float kCanvasHeight = 250.f;
+  const float kCanvasWidth = 250.f;
+
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kOuterIframeWidth + kOuterIframeLeftOffset,
+                                    kOuterIframeHeight + kOuterIframeTopOffset);
+    expected_metadata.SetArea(gfx::RectF(
+        kInnerIframeLeftOffset + kCanvasLeftOffset,
+        kInnerIframeTopOffset + kCanvasTopOffset, kCanvasWidth, kCanvasHeight));
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(gfx::RectF(
+        kInnerIframeLeftOffset + kCanvasLeftOffset,
+        kInnerIframeTopOffset + kCanvasTopOffset,
+        GetViewportWidth() - (kInnerIframeLeftOffset + kCanvasLeftOffset),
+        GetViewportHeight() - (kInnerIframeTopOffset + kCanvasTopOffset)));
+  }
+
+  auto* outer_iframe_element =
+      To<HTMLIFrameElement>(GetDocument().getElementById("OuterIframe"));
+  auto* inner_iframe_element = To<HTMLIFrameElement>(
+      outer_iframe_element->contentDocument()->getElementById("InnerIframe"));
+  auto* iframe_localframe =
+      To<LocalFrame>(inner_iframe_element->ContentFrame());
+  Document* iframe_document = inner_iframe_element->contentDocument();
+
+  DelegatedInkTrailPresenter* presenter = CreatePresenter(
+      iframe_localframe->GetDocument()->getElementById("canvas"),
+      iframe_document->GetFrame());
+  DCHECK(presenter);
+
+  InkTrailStyle style;
+  style.setDiameter(100000.3);
+  style.setColor("yellow");
+  expected_metadata.SetDiameter(style.diameter());
+  expected_metadata.SetColor(SK_ColorYELLOW);
+
+  gfx::PointF pt(350, 375);
+  presenter->updateInkTrailStartPoint(
+      ToScriptStateForMainWorld(iframe_document->GetFrame()),
+      CreatePointerMoveEvent(pt), &style);
+  expected_metadata.SetPoint(gfx::PointF(pt.x() + kInnerIframeLeftOffset,
+                                         pt.y() + kInnerIframeTopOffset));
+
+  expected_metadata.ExpectEqual(GetActualMetadata());
+}
+
 // Confirm that values are correct when an iframe is used and presentation area
 // isn't provided. Numbers and color used were chosen arbitrarily.
-TEST_F(DelegatedInkTrailPresenterUnitTest, IFrameNoPresentationArea) {
+TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport,
+       IFrameNoPresentationArea) {
   SimRequest main_resource("https://example.com/test.html", "text/html");
   SimRequest frame_resource("https://example.com/iframe.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -356,7 +533,7 @@
     body {
       margin: 0;
     }
-    iframe {
+    #iframe {
       width: 500px;
       height: 500px;
       position: fixed;
@@ -387,13 +564,25 @@
   const float kIframeHeight = 500.f;
   const float kIframeWidth = 500.f;
 
+  TestDelegatedInkMetadata expected_metadata;
+
+  if (!CanvasShouldBePastViewport()) {
+    SetWebViewSizeGreaterThanCanvas(kIframeWidth + kIframeLeftOffset,
+                                    kIframeHeight + kIframeTopOffset);
+    expected_metadata.SetArea(gfx::RectF(kIframeLeftOffset, kIframeTopOffset,
+                                         kIframeWidth, kIframeHeight));
+  } else {
+    SetWebViewSize();
+    expected_metadata.SetArea(
+        gfx::RectF(kIframeLeftOffset, kIframeTopOffset,
+                   GetViewportWidth() - kIframeLeftOffset,
+                   GetViewportHeight() - kIframeTopOffset));
+  }
+
   Document* iframe_document =
       To<HTMLIFrameElement>(GetDocument().getElementById("iframe"))
           ->contentDocument();
 
-  TestDelegatedInkMetadata expected_metadata(gfx::RectF(
-      kIframeLeftOffset, kIframeTopOffset, kIframeWidth, kIframeHeight));
-
   DelegatedInkTrailPresenter* presenter =
       CreatePresenter(nullptr, iframe_document->GetFrame());
   DCHECK(presenter);
@@ -414,4 +603,333 @@
   expected_metadata.ExpectEqual(GetActualMetadata());
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         DelegatedInkTrailPresenterCanvasBeyondViewport,
+                         testing::Bool());
+
+// Confirm that presentation area defaults to the size of the viewport.
+// Numbers and color used were chosen arbitrarily.
+TEST_F(DelegatedInkTrailPresenterUnitTest, PresentationAreaNotProvided) {
+  const int kViewportHeight = 555;
+  const int kViewportWidth = 333;
+  SetWebViewSize(kViewportWidth, kViewportHeight);
+
+  DelegatedInkTrailPresenter* presenter =
+      CreatePresenter(nullptr, GetDocument().GetFrame());
+  DCHECK(presenter);
+
+  TestDelegatedInkMetadata expected_metadata(
+      gfx::RectF(0, 0, kViewportWidth, kViewportHeight));
+
+  InkTrailStyle style;
+  style.setDiameter(3.6);
+  style.setColor("yellow");
+  expected_metadata.SetDiameter(style.diameter());
+  expected_metadata.SetColor(SK_ColorYELLOW);
+
+  gfx::PointF pt(70, 109);
+  presenter->updateInkTrailStartPoint(
+      ToScriptStateForMainWorld(GetDocument().GetFrame()),
+      CreatePointerMoveEvent(pt), &style);
+  expected_metadata.SetPoint(pt);
+
+  expected_metadata.ExpectEqual(GetActualMetadata());
+}
+
+// Test that the presentation area is clipped correctly by the dimensions of
+// the iframe, even when the iframe and canvas each fit entirely within the
+// visual viewport.
+TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasExtendsOutsideOfIframe) {
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #iframe {
+      width: 150px;
+      height: 150px;
+      position: fixed;
+      top: 13px;
+      left: 19px;
+    }
+    </style>
+    <iframe id='iframe' src='https://example.com/iframe.html'>
+    </iframe>
+  )HTML");
+
+  frame_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    canvas {
+      width: 199px;
+      height: 202px;
+    }
+    </style>
+    <canvas id='canvas'></canvas>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  // When creating the expected metadata, we have to take into account the
+  // offsets that are applied to the iframe that the canvas is in, and the 2px
+  // border around the iframe.
+  const float kIframeBorder = 2.f;
+  const float kIframeLeftOffset = 19.f + kIframeBorder;
+  const float kIframeTopOffset = 13.f + kIframeBorder;
+  const float kIframeHeight = 150.f;
+  const float kIframeWidth = 150.f;
+  const float kCanvasHeight = 202.f;
+  const float kCanvasWidth = 199.f;
+
+  // Ensure that the webpage is larger than the iframe and canvas.
+  SetWebViewSize(kCanvasWidth + kIframeLeftOffset + 1,
+                 kCanvasHeight + kIframeTopOffset + 1);
+
+  TestDelegatedInkMetadata expected_metadata(gfx::RectF(
+      kIframeLeftOffset, kIframeTopOffset, kIframeWidth, kIframeHeight));
+
+  auto* iframe_element =
+      To<HTMLIFrameElement>(GetDocument().getElementById("iframe"));
+  auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame());
+  Document* iframe_document = iframe_element->contentDocument();
+
+  DelegatedInkTrailPresenter* presenter = CreatePresenter(
+      iframe_localframe->GetDocument()->getElementById("canvas"),
+      iframe_document->GetFrame());
+  DCHECK(presenter);
+
+  InkTrailStyle style;
+  style.setDiameter(99.999);
+  style.setColor("lime");
+  expected_metadata.SetDiameter(style.diameter());
+  expected_metadata.SetColor(SK_ColorGREEN);
+
+  gfx::PointF pt(102, 67);
+  presenter->updateInkTrailStartPoint(
+      ToScriptStateForMainWorld(iframe_document->GetFrame()),
+      CreatePointerMoveEvent(pt), &style);
+  expected_metadata.SetPoint(
+      gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset));
+
+  expected_metadata.ExpectEqual(GetActualMetadata());
+}
+
+// Test that the presentation area is clipped correctly when it is offset left
+// and above the iframe boundaries.
+TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasLeftAndAboveIframeBoundaries) {
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #iframe {
+      width: 300px;
+      height: 301px;
+      position: fixed;
+      top: 13px;
+      left: 19px;
+    }
+    </style>
+    <iframe id='iframe' src='https://example.com/iframe.html'>
+    </iframe>
+  )HTML");
+
+  frame_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    canvas {
+      width: 189px;
+      height: 145px;
+      position: fixed;
+      top: -70px;
+      left: -99px;
+    }
+    </style>
+    <canvas id='canvas'></canvas>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  // When creating the expected metadata, we have to take into account the
+  // offsets that are applied to the iframe that the canvas is in, and the 2px
+  // border around the iframe.
+  const float kIframeBorder = 2.f;
+  const float kIframeLeftOffset = 19.f + kIframeBorder;
+  const float kIframeTopOffset = 13.f + kIframeBorder;
+  const float kIframeHeight = 301.f;
+  const float kIframeWidth = 300.f;
+  const float kCanvasHeight = 145.f;
+  const float kCanvasWidth = 189.f;
+  const float kCanvasLeftOffset = -99.f;
+  const float kCanvasTopOffset = -70.f;
+
+  // Ensure that the webpage is larger than the iframe.
+  SetWebViewSize(kIframeHeight + kIframeLeftOffset + 1,
+                 kIframeWidth + kIframeTopOffset + 1);
+
+  TestDelegatedInkMetadata expected_metadata(gfx::RectF(
+      kIframeLeftOffset, kIframeTopOffset, kCanvasWidth + kCanvasLeftOffset,
+      kCanvasHeight + kCanvasTopOffset));
+
+  auto* iframe_element =
+      To<HTMLIFrameElement>(GetDocument().getElementById("iframe"));
+  auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame());
+  Document* iframe_document = iframe_element->contentDocument();
+
+  DelegatedInkTrailPresenter* presenter = CreatePresenter(
+      iframe_localframe->GetDocument()->getElementById("canvas"),
+      iframe_document->GetFrame());
+  DCHECK(presenter);
+
+  InkTrailStyle style;
+  style.setDiameter(99.999);
+  style.setColor("lime");
+  expected_metadata.SetDiameter(style.diameter());
+  expected_metadata.SetColor(SK_ColorGREEN);
+
+  gfx::PointF pt(102, 67);
+  presenter->updateInkTrailStartPoint(
+      ToScriptStateForMainWorld(iframe_document->GetFrame()),
+      CreatePointerMoveEvent(pt), &style);
+  expected_metadata.SetPoint(
+      gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset));
+
+  expected_metadata.ExpectEqual(GetActualMetadata());
+}
+
+// Confirm that values, specifically presentation area, are transformed
+// correctly when the iframe that the canvas is in is clipped by its parent
+// iframe. Numbers and color used were chosen arbitrarily.
+TEST_F(DelegatedInkTrailPresenterUnitTest, OuterIframeClipsInnerIframe) {
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+  SimRequest frame2_resource("https://example.com/iframe2.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #OuterIframe {
+      width: 500px;
+      height: 500px;
+      position: fixed;
+      top: 26px;
+      left: 57px;
+    }
+    </style>
+    <iframe id='OuterIframe' src='https://example.com/iframe.html'>
+    </iframe>
+  )HTML");
+
+  frame_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    #InnerIframe {
+      width: 400px;
+      height: 400px;
+      position: fixed;
+      top: 311px;
+      left: 334px;
+    }
+    </style>
+    <iframe id='InnerIframe' src='https://example.com/iframe2.html'>
+    </iframe>
+    )HTML");
+
+  frame2_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    body {
+      margin: 0;
+    }
+    canvas {
+      width: 250px;
+      height: 250px;
+      position: fixed;
+      top: 1px;
+      left: 2px;
+    }
+    </style>
+    <canvas id='canvas'></canvas>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  // When creating the expected metadata, we have to take into account the
+  // offsets that are applied to the iframe that the canvas is in, and the 2px
+  // border around the iframe.
+  const float kIframeBorder = 2.f;
+  const float kOuterIframeLeftOffset = 57.f + kIframeBorder;
+  const float kOuterIframeTopOffset = 26.f + kIframeBorder;
+  const float kOuterIframeHeight = 500.f;
+  const float kOuterIframeWidth = 500.f;
+  const float kInnerIframeLeftOffset =
+      kOuterIframeLeftOffset + 334.f + kIframeBorder;
+  const float kInnerIframeTopOffset =
+      kOuterIframeTopOffset + 311.f + kIframeBorder;
+  const float kCanvasLeftOffset = 2.f;
+  const float kCanvasTopOffset = 1.f;
+
+  // Ensure that the webpage is larger than the iframe and canvas.
+  const float kViewportWidth = kOuterIframeWidth + kOuterIframeLeftOffset + 1.f;
+  const float kViewportHeight =
+      kOuterIframeHeight + kOuterIframeTopOffset + 1.f;
+  SetWebViewSize(kViewportWidth, kViewportHeight);
+
+  TestDelegatedInkMetadata expected_metadata(
+      gfx::RectF(kInnerIframeLeftOffset + kCanvasLeftOffset,
+                 kInnerIframeTopOffset + kCanvasTopOffset,
+                 kOuterIframeWidth + kOuterIframeLeftOffset -
+                     kInnerIframeLeftOffset - kCanvasLeftOffset,
+                 kOuterIframeHeight + kOuterIframeTopOffset -
+                     kInnerIframeTopOffset - kCanvasTopOffset));
+
+  auto* outer_iframe_element =
+      To<HTMLIFrameElement>(GetDocument().getElementById("OuterIframe"));
+  auto* inner_iframe_element = To<HTMLIFrameElement>(
+      outer_iframe_element->contentDocument()->getElementById("InnerIframe"));
+  auto* iframe_localframe =
+      To<LocalFrame>(inner_iframe_element->ContentFrame());
+  Document* iframe_document = inner_iframe_element->contentDocument();
+
+  DelegatedInkTrailPresenter* presenter = CreatePresenter(
+      iframe_localframe->GetDocument()->getElementById("canvas"),
+      iframe_document->GetFrame());
+  DCHECK(presenter);
+
+  InkTrailStyle style;
+  style.setDiameter(19);
+  style.setColor("red");
+  expected_metadata.SetDiameter(style.diameter());
+  expected_metadata.SetColor(SK_ColorRED);
+
+  gfx::PointF pt(357, 401);
+  presenter->updateInkTrailStartPoint(
+      ToScriptStateForMainWorld(iframe_document->GetFrame()),
+      CreatePointerMoveEvent(pt), &style);
+  expected_metadata.SetPoint(gfx::PointF(pt.x() + kInnerIframeLeftOffset,
+                                         pt.y() + kInnerIframeTopOffset));
+
+  expected_metadata.ExpectEqual(GetActualMetadata());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index 212bd13..99d9c2f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -412,7 +412,7 @@
     mojo::AssociatedRemote<mojom::blink::IDBDatabase> remote;
     std::unique_ptr<BackendDatabaseWithMockedClose> mock_database =
         std::make_unique<BackendDatabaseWithMockedClose>(
-            remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+            remote.BindNewEndpointAndPassDedicatedReceiver());
     EXPECT_CALL(*mock_database, Close()).Times(1);
 
     auto transaction_backend = std::make_unique<MockWebIDBTransaction>(
@@ -435,7 +435,7 @@
     mojo::AssociatedRemote<mojom::blink::IDBDatabase> remote;
     std::unique_ptr<BackendDatabaseWithMockedClose> mock_database =
         std::make_unique<BackendDatabaseWithMockedClose>(
-            remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+            remote.BindNewEndpointAndPassDedicatedReceiver());
     EXPECT_CALL(*mock_database, Close()).Times(1);
 
     auto transaction_backend = std::make_unique<MockWebIDBTransaction>(
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
index a56104c7..26480f8f 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
@@ -115,7 +115,7 @@
   WebIDBCursorImplTest() : null_key_(IDBKey::CreateNone()) {
     mojo::AssociatedRemote<mojom::blink::IDBCursor> remote;
     mock_cursor_ = std::make_unique<MockCursorImpl>(
-        remote.BindNewEndpointAndPassDedicatedReceiverForTesting());
+        remote.BindNewEndpointAndPassDedicatedReceiver());
     cursor_ = std::make_unique<WebIDBCursorImpl>(
         remote.Unbind(), 1,
         blink::scheduler::GetSingleThreadTaskRunnerForTesting());
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
index 9ef4ae09..cd37ccb 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
@@ -113,7 +113,7 @@
     HeapMojoAssociatedRemote<device::mojom::blink::ScreenOrientation>
         screen_orientation(frame.DomWindow());
     ScreenOrientationClient().BindPendingReceiver(
-        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
+        screen_orientation.BindNewEndpointAndPassDedicatedReceiver());
     ScreenOrientationController::From(*frame.DomWindow())
         ->SetScreenOrientationAssociatedRemoteForTests(
             std::move(screen_orientation));
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
index 560e7e0..3f3b2aa 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
@@ -56,8 +56,7 @@
     EmptyChromeClient::InstallSupplements(frame);
     HeapMojoAssociatedRemote<device::mojom::blink::ScreenOrientation>
         screen_orientation(frame.DomWindow());
-    ignore_result(
-        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
+    ignore_result(screen_orientation.BindNewEndpointAndPassDedicatedReceiver());
     ScreenOrientationController::From(*frame.DomWindow())
         ->SetScreenOrientationAssociatedRemoteForTests(
             std::move(screen_orientation));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
index 0a9cf5b12..fc46e57 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
@@ -212,6 +212,7 @@
     return;
   }
   stopped_ = true;
+  UpdateMembers();
 }
 
 void RTCRtpTransceiver::setCodecPreferences(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
index 900be89..f88d1933 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
@@ -256,7 +256,12 @@
     return error;
   }
 
-  webrtc::RTCError Stop() { return webrtc_transceiver_->StopStandard(); }
+  webrtc::RTCError Stop() {
+    auto error = webrtc_transceiver_->StopStandard();
+    // After stop(), direction always returns 'stopped'.
+    state_.set_direction(webrtc::RtpTransceiverDirection::kStopped);
+    return error;
+  }
 
   webrtc::RTCError setCodecPreferences(
       std::vector<webrtc::RtpCodecCapability> codec_preferences) {
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_test.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_test.cc
index ce13ab8..5da6f89a 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_test.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_test.cc
@@ -63,8 +63,7 @@
     PageTestBase::SetUp(IntSize());
     HeapMojoAssociatedRemote<device::mojom::blink::ScreenOrientation>
         screen_orientation(GetFrame().DomWindow());
-    ignore_result(
-        screen_orientation.BindNewEndpointAndPassDedicatedReceiverForTesting());
+    ignore_result(screen_orientation.BindNewEndpointAndPassDedicatedReceiver());
     Controller()->SetScreenOrientationAssociatedRemoteForTests(
         std::move(screen_orientation));
   }
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 43823db0..03f3c33 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1547,7 +1547,6 @@
     sources += [
       "widget/input/synchronous_compositor_proxy.cc",
       "widget/input/synchronous_compositor_proxy.h",
-      "widget/input/synchronous_compositor_registry.h",
     ]
   }
 
@@ -1597,6 +1596,8 @@
     "//crypto",
     "//device/vr/public/mojom:mojom_blink",
     "//gin",
+    "//gpu:gpu",
+    "//gpu/command_buffer/client:webgpu_interface",
     "//jingle:webrtc_glue",
     "//media",
     "//media/capture:capture_switches",
@@ -1818,6 +1819,7 @@
     "//cc",
     "//cc:test_support",
     "//components/viz/test:test_support",
+    "//gpu:raster",
     "//testing/gmock",
     "//testing/gtest:gtest",
     "//testing/perf",
@@ -2271,9 +2273,9 @@
     "testing/blink_fuzzer_test_support.cc",
     "testing/blink_fuzzer_test_support.h",
   ]
+  public_deps = [ ":test_support" ]
   deps = [
     ":platform",
-    ":test_support",
     "//base/test:test_config",
     "//content/test:test_support",
   ]
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
index 3dc9d88..d9bb1100 100644
--- a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
+++ b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
@@ -131,7 +131,7 @@
   auto& associated_receiver_set = owner()->associated_receiver_set();
   mojo::AssociatedRemote<sample::blink::Service> associated_remote;
   auto associated_receiver =
-      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      associated_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::ReceiverId rid = associated_receiver_set.Add(
       std::move(associated_receiver), task_runner());
@@ -148,7 +148,7 @@
   auto& associated_receiver_set = owner()->associated_receiver_set();
   mojo::AssociatedRemote<sample::blink::Service> associated_remote;
   auto associated_receiver =
-      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      associated_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::ReceiverId rid = associated_receiver_set.Add(
       std::move(associated_receiver), task_runner());
@@ -167,7 +167,7 @@
 
   mojo::AssociatedRemote<sample::blink::Service> associated_remote;
   auto associated_receiver =
-      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      associated_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::ReceiverId rid = owner()->associated_receiver_set().Add(
       std::move(associated_receiver), task_runner());
@@ -188,7 +188,7 @@
   auto& associated_receiver_set = owner()->associated_receiver_set();
   mojo::AssociatedRemote<sample::blink::Service> associated_remote;
   auto associated_receiver =
-      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      associated_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::ReceiverId rid = associated_receiver_set.Add(
       std::move(associated_receiver), task_runner());
@@ -205,7 +205,7 @@
   auto& associated_receiver_set = owner()->associated_receiver_set();
   mojo::AssociatedRemote<sample::blink::Service> associated_remote;
   auto associated_receiver =
-      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+      associated_remote.BindNewEndpointAndPassDedicatedReceiver();
 
   mojo::ReceiverId rid = associated_receiver_set.Add(
       std::move(associated_receiver), task_runner());
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
index 0db58374..4a3760e 100644
--- a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
+++ b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
@@ -64,9 +64,9 @@
         std::move(task_runner));
   }
   mojo::PendingAssociatedReceiver<Interface>
-  BindNewEndpointAndPassDedicatedReceiverForTesting() WARN_UNUSED_RESULT {
+  BindNewEndpointAndPassDedicatedReceiver() WARN_UNUSED_RESULT {
     return wrapper_->associated_remote()
-        .BindNewEndpointAndPassDedicatedReceiverForTesting();
+        .BindNewEndpointAndPassDedicatedReceiver();
   }
   void Bind(mojo::PendingAssociatedRemote<Interface> pending_associated_remote,
             scoped_refptr<base::SequencedTaskRunner> task_runner) {
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index 5e1772e..94bcb81 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -184,3 +184,5 @@
 crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[2] [ Failure Pass ]
 crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[3] [ Failure Pass ]
 crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[4] [ Failure Pass ]
+crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress [ Failure Pass ]
+crbug.com/1128104 [ Linux ] external/wpt/webdriver/tests/release_actions/sequence.py>>test_no_release_mouse_sequence_keeps_dblclick_state [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/mathml/crashtests/display-and-column-properties.html b/third_party/blink/web_tests/external/wpt/mathml/crashtests/display-and-column-properties.html
new file mode 100644
index 0000000..c40a2a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/crashtests/display-and-column-properties.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="test-wait">
+  <head>
+    <title>MathML elements with display and column properties</title>
+    <meta charset="utf-8"/>
+    <style>
+      math {
+          column-width: 100px;
+      }
+    </style>
+  </head>
+  <body>
+    <math></math>
+    <math style="display: list-item"></math>
+    <script>
+      window.addEventListener("load", function() {
+          // Force initial layout.
+          document.documentElement.getBoundingClientRect();
+
+          // Change display and reforce layout.
+          let maths = document.getElementsByTagName("math");
+          maths[0].setAttribute("style", "display: list-item");
+          maths[1].removeAttribute("style");
+          document.documentElement.getBoundingClientRect();
+
+          document.documentElement.classList.remove("test-wait");
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-2.html
new file mode 100644
index 0000000..e0d41e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-2.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that one can override the layout of MathML elements with the CSS display property</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#layout-algorithms">
+<meta name="assert" content="Verify that one can override the display of a MathML element.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/layout-comparison.js"></script>
+<script src="/mathml/support/mathml-fragments.js"></script>
+<style>
+  mfrac {
+      padding: 0;
+  }
+</style>
+<script>
+  const Xsize = 25;
+  const templates = {
+      "block display": `<math style="display: block;">XXX</math>`,
+      "block display with contrained width": `<math style="display: block; width: ${2*Xsize}px;">XXX</math>`,
+      "list display inside display block": `<math style="display: block">\
+  <mmultiscripts style="display: list-item;">X</mmultiscripts>\
+  <maction style="display: list-item;">X</maction>\
+  <mpadded style="display: list-item;">X</mpadded>\
+</math>`,
+      "inline display": `<math style="display: inline;">XXX</math>`,
+      "inline-block display": `<math style="display: inline-block">XXX</math>`,
+      "table display (math)": `<math style="display: table">\
+  <mfrac style='display: table-row'>\
+    <msub style='display: table-cell'>X</msub>\
+    <msup style='display: table-cell'>X</msup>\
+    <msubsup style='display: table-cell'>X</msubsup>\
+  </mfrac>\
+  <mtable style='display: table-row'>\
+    <munder style='display: table-cell'>X</munder>\
+    <mover style='display: table-cell'>X</mover>\
+    <munderover style='display: table-cell'>X</munderover>\
+  </mtable>\
+</math>`,
+      "table display (mrow)": `<math display="block">\
+<mrow style="display: table">\
+  <mfrac style='display: table-row'>\
+    <msub style='display: table-cell'>X</msub>\
+    <msup style='display: table-cell'>X</msup>\
+    <msubsup style='display: table-cell'>X</msubsup>\
+  </mfrac>\
+  <mtable style='display: table-row'>\
+    <munder style='display: table-cell'>X</munder>\
+    <mover style='display: table-cell'>X</mover>\
+    <munderover style='display: table-cell'>X</munderover>\
+  </mtable>\
+</mrow></math>`,
+      "inline-table display (math)": `<math style="display: inline-table">\
+  <mfrac style='display: table-row'>\
+    <msub style='display: table-cell'>X</msub>\
+    <msup style='display: table-cell'>X</msup>\
+    <msubsup style='display: table-cell'>X</msubsup>\
+  </mfrac>\
+  <mtable style='display: table-row'>\
+    <munder style='display: table-cell'>X</munder>\
+    <mover style='display: table-cell'>X</mover>\
+    <munderover style='display: table-cell'>X</munderover>\
+  </mtable>\
+</math>`,
+      "inline-table display (mrow)": `<math display="block">\
+<mrow style="display: inline-table">\
+  <mfrac style='display: table-row'>\
+    <msub style='display: table-cell'>X</msub>\
+    <msup style='display: table-cell'>X</msup>\
+    <msubsup style='display: table-cell'>X</msubsup>\
+  </mfrac>\
+  <mtable style='display: table-row'>\
+    <munder style='display: table-cell'>X</munder>\
+    <mover style='display: table-cell'>X</mover>\
+    <munderover style='display: table-cell'>X</munderover>\
+  </mtable>\
+</mrow></math>`,
+      "flexbox display (math)": `<math style="display: flex; flex-direction: column;">XXX</math>`,
+      "flexbox display (mrow)": `<math display="block"><mrow style="display: flex; flex-direction: column;">XXX</mrow></math>`,
+      "grid display (math)": `<math style="display: grid; grid-gap: 2px; grid-template-columns: ${Xsize}px ${Xsize}px ${Xsize}px;>">XXXXXXXXX</math>`,
+      "grid display (mrow)": `<math display="block"><mrow style="display: grid; grid-gap: 2px; grid-template-columns: ${Xsize}px ${Xsize}px ${Xsize}px;>">XXXXXXXXX</mrow></math>`,
+      "ruby display (math)": `<math style="display: ruby;">\
+<mrow style="display: ruby-base;">X</mrow>\
+<mrow style="display: ruby-text">XX</mrow>\
+</math>`,
+      "ruby display (mrow)": `<math display="block"><mrow style="display: ruby;">\
+<mrow style="display: ruby-base;">X</mrow>\
+<mrow style="display: ruby-text">XX</mrow>\
+</mrow></math>`,
+      "block display with column width (math)": `<math style="display: block; column-width: ${2*Xsize}px">\
+    <mrow>XXXX</mrow><mrow>XXXX</mrow><mrow>XXXX</mrow>\
+</math>`,
+      "block display with column width (mrow)": `<math style="display: block"><mrow style="display: block; column-width: ${2*Xsize}px">\
+    <mrow>XXXX</mrow><mrow>XXXX</mrow><mrow>XXXX</mrow>\
+</mrow></math>`,
+  };
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+
+      for (let key in templates) {
+          if (!templates.hasOwnProperty(key))
+              continue;
+          let mathtest = templates[key].
+              replace(/X/g, `<mspace style="display: inline-block; width: ${Xsize}px; height: ${Xsize}px; background: black"></mspace>`);
+          let reference = mathtest.
+              replace(/maction|math|mfrac|mmultiscripts|mover|mover|mpadded|mrow|mspace|msubsup|msub|msup|mtable|munderover|munder/g, "div");
+          document.body.insertAdjacentHTML("beforeend", `<div style="position: absolute;">\
+<div><span>${key}:</span>${mathtest}</div>\
+<div><span>${key}:</span>${reference}</div>\
+</div>`);
+          let div = document.body.lastElementChild;
+          let elementDiv = div.firstElementChild;
+          let referenceDiv = div.lastElementChild;
+
+          test(function() {
+              const epsilon = 1;
+              compareLayout(elementDiv, referenceDiv, epsilon);
+          }, `${key}`);
+
+          div.style = "display: none;"; // Hide the div after measurement.
+      }
+
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html
index 0f01cc4..8469a6c 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/element-internals-shadowroot.tentative.html
@@ -41,4 +41,73 @@
   assert_true(constructed);
 }, 'ElementInternals.shadowRoot allows access to closed shadow root');
 
+test(() => {
+  let constructed = false;
+  const element = document.createElement('x-1');
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(),'attachInternals cannot be called before definition exists');
+  customElements.define('x-1', class extends HTMLElement {
+    constructor() {
+      super();
+      assert_true(!!this.attachInternals());
+      constructed = true;
+    }
+  });
+  assert_false(constructed);
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(),'attachInternals cannot be called before constructor');
+  customElements.upgrade(element);
+  assert_true(constructed);
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(),'attachInternals already called');
+}, 'ElementInternals cannot be called before constructor, upgrade case');
+
+test(() => {
+  let constructed = false;
+  const element = document.createElement('x-2');
+  customElements.define('x-2', class extends HTMLElement {
+    constructor() {
+      super();
+      // Don't attachInternals() here
+      constructed = true;
+    }
+  });
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(),'attachInternals cannot be called before constructor');
+  assert_false(constructed);
+  customElements.upgrade(element);
+  assert_true(constructed);
+  assert_true(!!element.attachInternals(),'After the constructor, ok to call from outside');
+}, 'ElementInternals *can* be called after constructor, upgrade case');
+
+test(() => {
+  let constructed = false;
+  customElements.define('x-3', class extends HTMLElement {
+    constructor() {
+      super();
+      assert_true(!!this.attachInternals());
+      constructed = true;
+    }
+  });
+  const element = document.createElement('x-3');
+  assert_true(constructed);
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(), 'attachInternals already called');
+}, 'ElementInternals cannot be called after constructor calls it, create case');
+
+test(() => {
+  let constructed = false;
+  const element = document.createElement('x-5');
+  customElements.define('x-5', class extends HTMLElement {
+    static disabledFeatures = [ 'internals' ];
+    constructor() {
+      super();
+      assert_throws_dom('NotSupportedError', () => this.attachInternals(), 'attachInternals forbidden by disabledFeatures, constructor');
+      constructed = true;
+    }
+  });
+  assert_false(constructed);
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(), 'attachInternals forbidden by disabledFeatures, pre-upgrade');
+  customElements.upgrade(element);
+  assert_true(constructed);
+  assert_throws_dom('NotSupportedError', () => element.attachInternals(), 'attachInternals forbidden by disabledFeatures, post-upgrade');
+}, 'ElementInternals disabled by disabledFeatures');
+
+
+
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
index 71f96185..cdefe3f 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
@@ -24,16 +24,16 @@
 FAIL checkRemoveTrackNegotiation assert_equals: pc2.setRemoteDescription(offer) should've added 2 tracks to receive stream expected 2 but got 0
 FAIL checkMute assert_true: expected true got false
 FAIL checkStop assert_equals: getReceivers does not expose a receiver of a stopped transceiver expected 0 but got 1
-FAIL checkStopAfterCreateOffer assert_equals: expected "[{stopped:true}]" but got "[{stopped:false}]"
-FAIL checkStopAfterSetLocalOffer assert_equals: expected "[{stopped:true}]" but got "[{stopped:false}]"
-FAIL checkStopAfterSetRemoteOffer assert_equals: expected "[{mid:null,stopped:true}]" but got "[{mid:\"0\",stopped:false}]"
-FAIL checkStopAfterCreateAnswer assert_equals: expected "[{mid:null,stopped:true}]" but got "[{mid:\"0\",stopped:false}]"
-FAIL checkStopAfterSetLocalAnswer assert_equals: expected "[{mid:null,stopped:true}]" but got "[{mid:\"0\",stopped:false}]"
+PASS checkStopAfterCreateOffer
+PASS checkStopAfterSetLocalOffer
+PASS checkStopAfterSetRemoteOffer
+PASS checkStopAfterCreateAnswer
+PASS checkStopAfterSetLocalAnswer
 PASS checkStopAfterClose
-FAIL checkLocalRollback assert_equals: expected "[{stopped:true}]" but got "[{stopped:false}]"
+PASS checkLocalRollback
 PASS checkRollbackAndSetRemoteOfferWithDifferentType
-FAIL checkRemoteRollback assert_equals: expected "{currentDirection:null,mid:null,stopped:true}" but got "{currentDirection:null,mid:null,stopped:false}"
-FAIL checkMsectionReuse assert_equals: expected "[{currentDirection:null,mid:null,stopped:true}]" but got "[{currentDirection:\"inactive\",mid:\"0\",stopped:false}]"
+FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'addTrack' on 'RTCPeerConnection': A sender already exists for the track."
+FAIL checkMsectionReuse assert_equals: expected "[{currentDirection:null,mid:null}]" but got "[{currentDirection:\"inactive\",mid:\"0\"}]"
 PASS checkStopAfterCreateOfferWithReusedMsection
 PASS checkAddIceCandidateToStoppedTransceiver
 FAIL checkBundleTagRejected promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: The m= section with mid='1' should be rejected."
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https.html
index 412736b..2e1dcbb 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https.html
@@ -199,7 +199,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         },
         {
           receiver: {track: {kind: "video", readyState: "live", muted: true}},
@@ -207,7 +206,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -232,7 +230,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         },
         {
           receiver: {track: {kind: "video"}},
@@ -240,7 +237,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -265,7 +261,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         },
         {
           receiver: {track: {kind: "video"}},
@@ -273,7 +268,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -293,7 +287,6 @@
           direction: "recvonly",
           mid: null,
           currentDirection: null,
-          stopped: false
         },
         {
           receiver: {track: {kind: "video"}},
@@ -301,7 +294,6 @@
           direction: "recvonly",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -336,7 +328,6 @@
           sender: {track: null},
           direction: "recvonly",
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -365,7 +356,6 @@
           // rtcweb-jsep says this is recvonly, w3c-webrtc does not...
           direction: "recvonly",
           currentDirection: null,
-          stopped: false
         }
       ]);
   };
@@ -435,7 +425,6 @@
           sender: {track: null},
           direction: "recvonly",
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -466,7 +455,6 @@
           sender: {track: {kind: "audio"}},
           direction: "sendrecv",
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -486,7 +474,6 @@
           sender: {track: {kind: "audio"}},
           direction: "sendrecv",
           currentDirection: "sendonly",
-          stopped: false,
           mid: lastMid
         }
       ]);
@@ -500,7 +487,6 @@
           sender: {track: {kind: "audio"}},
           direction: "sendrecv",
           currentDirection: "sendonly",
-          stopped: false,
           mid: lastMid
         }
       ]);
@@ -1253,15 +1239,14 @@
     assert_equals(pc1.getSenders().length, 0, 'getSenders does not expose a sender of a stopped transceiver');
 
     await onended;
-
+    // The transceiver has [[stopping]] = true, [[stopped]] = false
     hasPropsAndUniqueMids(pc1.getTransceivers(),
       [
         {
           sender: {track: {kind: "audio"}},
           receiver: {track: {kind: "audio", readyState: "ended"}},
-          stopped: true,
-          currentDirection: null,
-          direction: "sendrecv"
+          currentDirection: "sendrecv",
+          direction: "stopped"
         }
       ]);
 
@@ -1297,15 +1282,16 @@
 
     await onended;
 
+    // pc2's transceiver was stopped remotely, so has
+    // [[stopping]] = true, [[stopped]] = true.
     hasProps(pc2.getTransceivers(),
       [
         {
           sender: {track: {kind: "audio"}},
           receiver: {track: {kind: "audio", readyState: "ended"}},
-          stopped: true,
           mid: null,
-          currentDirection: null,
-          direction: "sendrecv"
+          currentDirection: "stopped",
+          direction: "stopped"
         }
       ]);
 
@@ -1335,13 +1321,14 @@
 
     let offer = await pc1.createOffer();
 
-    pc1.getTransceivers()[0].stop();
-
+    const transceiverThatWasStopped = pc1.getTransceivers()[0];
+    transceiverThatWasStopped.stop();
     await pc2.setRemoteDescription(offer)
     trickle(t, pc1, pc2);
     await pc1.setLocalDescription(offer);
 
     let answer = await pc2.createAnswer();
+    const negotiationNeededAwaiter = negotiationNeeded(pc1);
     const trackEvents = await setRemoteDescriptionReturnTrackEvents(pc1, answer);
     // Spec language doesn't say anything about checking whether the transceiver
     // is stopped here.
@@ -1353,16 +1340,20 @@
         }
       ]);
 
+    assert_equals(transceiverThatWasStopped, pc1.getTransceivers()[0]);
+    // The transceiver should still be [[stopping]]=true, [[stopped]]=false.
     hasPropsAndUniqueMids(pc1.getTransceivers(),
       [
         {
-          stopped: true,
+          currentDirection: "sendrecv",
+          direction: "stopped"
         }
       ]);
 
-    await negotiationNeeded(pc1);
+    await negotiationNeededAwaiter;
 
     trickle(t, pc2, pc1);
+
     await pc2.setLocalDescription(answer);
 
     await iceConnected(pc1);
@@ -1374,22 +1365,8 @@
     answer = await pc2.createAnswer();
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(answer);
-
-    hasProps(pc1.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
-
-    hasProps(pc2.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
+    assert_equals(pc1.getTransceivers().length, 0);
+    assert_equals(pc2.getTransceivers().length, 0);
   };
 
   const checkStopAfterSetLocalOffer = async t => {
@@ -1413,6 +1390,7 @@
     pc1.getTransceivers()[0].stop();
 
     let answer = await pc2.createAnswer();
+    const negotiationNeededAwaiter = negotiationNeeded(pc1);
     const trackEvents = await setRemoteDescriptionReturnTrackEvents(pc1, answer);
     // Spec language doesn't say anything about checking whether the transceiver
     // is stopped here.
@@ -1427,10 +1405,11 @@
     hasPropsAndUniqueMids(pc1.getTransceivers(),
       [
         {
-          stopped: true,
+          direction: "stopped",
+          currentDirection: "sendrecv"
         }
       ]);
-    await negotiationNeeded(pc1);
+    await negotiationNeededAwaiter;
 
     trickle(t, pc2, pc1);
     await pc2.setLocalDescription(answer);
@@ -1445,21 +1424,8 @@
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(answer);
 
-    hasProps(pc1.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
-
-    hasProps(pc2.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
+    assert_equals(pc1.getTransceivers().length, 0);
+    assert_equals(pc2.getTransceivers().length, 0);
   };
 
   const checkStopAfterSetRemoteOffer = async t => {
@@ -1479,22 +1445,35 @@
     await pc2.setRemoteDescription(offer)
     await pc1.setLocalDescription(offer);
 
-    // Stop on _answerer_side now. Should take effect in answer.
+    // Stop on _answerer_ side now. Should not stop transceiver in answer,
+    // but cause firing of negotiationNeeded at pc2, and disabling
+    // of the transceiver with direction = inactive in answer.
     pc2.getTransceivers()[0].stop();
+    assert_equals(pc2.getTransceivers()[0].direction, 'stopped');
 
     const answer = await pc2.createAnswer();
     const trackEvents = await setRemoteDescriptionReturnTrackEvents(pc1, answer);
     hasProps(trackEvents, []);
 
-    hasProps(pc1.getTransceivers(),
+    hasProps(pc2.getTransceivers(),
       [
         {
-          stopped: true,
-          mid: null
+          direction: "stopped",
+          currentDirection: null,
         }
       ]);
 
+    const negotiationNeededAwaiter = negotiationNeeded(pc2);
     await pc2.setLocalDescription(answer);
+    hasProps(pc2.getTransceivers(),
+      [
+        {
+          direction: "stopped",
+          currentDirection: "inactive",
+        }
+      ]);
+
+    await negotiationNeededAwaiter;
   };
 
   const checkStopAfterCreateAnswer = async t => {
@@ -1532,16 +1511,25 @@
     hasPropsAndUniqueMids(pc2.getTransceivers(),
       [
         {
-          stopped: true,
+          direction: "stopped",
+          currentDirection: null,
         }
       ]);
 
     trickle(t, pc2, pc1);
-    // The negotiaionneeded event is fired during processing of
+    // The negotiationneeded event is fired during processing of
     // setLocalDescription()
-    const negotiationNeededWaiter = negotiationNeeded(pc2);
+    const negotiationNeededAwaiter = negotiationNeeded(pc2);
     await pc2.setLocalDescription(answer);
-    await negotiationNeededWaiter;
+    hasPropsAndUniqueMids(pc2.getTransceivers(),
+      [
+        {
+          direction: "stopped",
+          currentDirection: "sendrecv",
+        }
+      ]);
+
+    await negotiationNeededAwaiter;
     await iceConnected(pc1);
     await iceConnected(pc2);
 
@@ -1552,19 +1540,22 @@
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(answer);
 
+    // Since this offer/answer exchange was initiated from pc1,
+    // pc2 still doesn't get to say that it has a stopped transceiver,
+    // but does get to set it to inactive.
     hasProps(pc1.getTransceivers(),
       [
         {
-          stopped: true,
-          mid: null
+          direction: "sendrecv",
+          currentDirection: "inactive",
         }
       ]);
 
     hasProps(pc2.getTransceivers(),
       [
         {
-          stopped: true,
-          mid: null
+          direction: "stopped",
+          currentDirection: "inactive",
         }
       ]);
   };
@@ -1607,7 +1598,8 @@
     hasPropsAndUniqueMids(pc2.getTransceivers(),
       [
         {
-          stopped: true,
+          direction: "stopped",
+          currentDirection: "sendrecv",
         }
       ]);
 
@@ -1615,28 +1607,17 @@
     await iceConnected(pc1);
     await iceConnected(pc2);
 
-    offer = await pc1.createOffer();
-    await pc1.setLocalDescription(offer);
-    await pc2.setRemoteDescription(offer);
-    answer = await pc2.createAnswer();
-    await pc2.setLocalDescription(answer);
-    await pc1.setRemoteDescription(answer);
+    // Initiate an offer/answer exchange from pc2 in order
+    // to negotiate the stopped transceiver.
+    offer = await pc2.createOffer();
+    await pc2.setLocalDescription(offer);
+    await pc1.setRemoteDescription(offer);
+    answer = await pc1.createAnswer();
+    await pc1.setLocalDescription(answer);
+    await pc2.setRemoteDescription(answer);
 
-    hasProps(pc1.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
-
-    hasProps(pc2.getTransceivers(),
-      [
-        {
-          stopped: true,
-          mid: null
-        }
-      ]);
+    assert_equals(pc1.getTransceivers().length, 0);
+    assert_equals(pc2.getTransceivers().length, 0);
   };
 
   const checkStopAfterClose = async t => {
@@ -1683,7 +1664,6 @@
           sender: {track},
           direction: "sendrecv",
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1703,7 +1683,6 @@
           direction: "sendonly",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1713,7 +1692,11 @@
     pc.getTransceivers()[0].stop();
     await pc.setLocalDescription({type: "rollback"});
 
-    hasProps(pc.getTransceivers(), [{ stopped: true }]);
+    hasProps(pc.getTransceivers(), [
+      {
+        direction: "stopped",
+      }
+    ]);
   };
 
   const checkRollbackAndSetRemoteOfferWithDifferentType = async t => {
@@ -1744,7 +1727,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1756,7 +1738,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1770,14 +1751,12 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         },
         {
           receiver: {track: {kind: "video"}},
           sender: {track: null},
           direction: "recvonly",
           currentDirection: "recvonly",
-          stopped: false
         }
       ]);
 
@@ -1788,7 +1767,6 @@
           sender: {track: videoTrack},
           direction: "sendrecv",
           currentDirection: "sendonly",
-          stopped: false
         }
       ]);
 
@@ -1823,7 +1801,6 @@
 
     hasProps(removedTransceiver,
       {
-        stopped: true,
         mid: null,
         currentDirection: null
       }
@@ -1834,7 +1811,6 @@
     hasProps(removedTransceiver,
       {
         receiver: {track: {readyState: "ended"}},
-        stopped: true,
         mid: null,
         currentDirection: null
       }
@@ -1849,7 +1825,6 @@
           sender: {track: null},
           direction: "recvonly",
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1869,7 +1844,6 @@
           direction: "sendrecv",
           mid: mid0,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1889,7 +1863,6 @@
           direction: "recvonly",
           mid: mid0,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1903,7 +1876,6 @@
           direction: "sendrecv",
           mid: mid0,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1917,7 +1889,6 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: false
         }
       ]);
 
@@ -1954,7 +1925,6 @@
           direction: "sendrecv",
           mid: mid0,
           currentDirection: "sendrecv",
-          stopped: false
         }
       ]);
 
@@ -1990,21 +1960,20 @@
           direction: "sendrecv",
           mid: null,
           currentDirection: null,
-          stopped: true
         }
       ]);
 
     // stop() cannot be rolled back!
+    // Transceiver should have [[stopping]]=true, [[stopped]]=false
     await pc2.setRemoteDescription({type: "rollback"});
     hasProps(pc2.getTransceivers(),
       [
         {
           receiver: {track: {kind: "audio"}},
           sender: {track: {kind: "audio"}},
-          direction: "sendrecv",
+          direction: "stopped",
           mid: null,
-          currentDirection: null,
-          stopped: true
+          currentDirection: "sendrecv",
         }
       ]);
   };
@@ -2062,7 +2031,6 @@
         {
           mid: null,
           currentDirection: null,
-          stopped: true
         }
       ]);
 
@@ -2071,7 +2039,6 @@
         {
           mid: null,
           currentDirection: null,
-          stopped: true
         }
       ]);
 
@@ -2087,7 +2054,6 @@
     hasProps(pc1.getTransceivers(),
       [
         {
-          stopped: true
         },
         {
           sender: {track: track2}
@@ -2103,7 +2069,6 @@
     hasProps(pc2.getTransceivers(),
       [
         {
-          stopped: true
         },
         {
           sender: {track}
@@ -2181,7 +2146,6 @@
         },
         {
           mid: null,
-          stopped: true
         },
         {
           sender: {track: null},
@@ -2206,7 +2170,6 @@
       [
         {},
         {
-          stopped: true
         },
         {
           mid: mid1,
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/mathml/relations/css-styling/display-2-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/mathml/relations/css-styling/display-2-expected.txt
new file mode 100644
index 0000000..57a3c69
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/mathml/relations/css-styling/display-2-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS block display
+PASS block display with contrained width
+PASS list display inside display block
+PASS inline display
+PASS inline-block display
+FAIL table display (math) assert_approx_equals: block size expected 80 +/- 1 but got 78
+PASS table display (mrow)
+FAIL inline-table display (math) assert_approx_equals: block size expected 60 +/- 1 but got 58
+PASS inline-table display (mrow)
+PASS flexbox display (math)
+PASS flexbox display (mrow)
+PASS grid display (math)
+PASS grid display (mrow)
+FAIL ruby display (math) assert_approx_equals: block size expected 80 +/- 1 but got 30
+FAIL ruby display (mrow) assert_approx_equals: block size expected 80 +/- 1 but got 45
+FAIL block display with column width (math) assert_approx_equals: block size expected 80 +/- 1 but got 45
+FAIL block display with column width (mrow) assert_approx_equals: block size expected 80 +/- 1 but got 45
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/mathml/relations/css-styling/display-2-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/mathml/relations/css-styling/display-2-expected.txt
new file mode 100644
index 0000000..2ec5d3b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/mathml/relations/css-styling/display-2-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS block display
+PASS block display with contrained width
+PASS list display inside display block
+PASS inline display
+PASS inline-block display
+PASS table display (math)
+PASS table display (mrow)
+PASS inline-table display (math)
+PASS inline-table display (mrow)
+PASS flexbox display (math)
+PASS flexbox display (mrow)
+PASS grid display (math)
+PASS grid display (mrow)
+FAIL ruby display (math) assert_approx_equals: block size expected 76 +/- 1 but got 29
+FAIL ruby display (mrow) assert_approx_equals: block size expected 76 +/- 1 but got 43
+FAIL block display with column width (math) assert_approx_equals: block size expected 76 +/- 1 but got 43
+FAIL block display with column width (mrow) assert_approx_equals: block size expected 76 +/- 1 but got 43
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/mathml/relations/css-styling/display-2-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/mathml/relations/css-styling/display-2-expected.txt
new file mode 100644
index 0000000..57a3c69
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/mathml/relations/css-styling/display-2-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS block display
+PASS block display with contrained width
+PASS list display inside display block
+PASS inline display
+PASS inline-block display
+FAIL table display (math) assert_approx_equals: block size expected 80 +/- 1 but got 78
+PASS table display (mrow)
+FAIL inline-table display (math) assert_approx_equals: block size expected 60 +/- 1 but got 58
+PASS inline-table display (mrow)
+PASS flexbox display (math)
+PASS flexbox display (mrow)
+PASS grid display (math)
+PASS grid display (mrow)
+FAIL ruby display (math) assert_approx_equals: block size expected 80 +/- 1 but got 30
+FAIL ruby display (mrow) assert_approx_equals: block size expected 80 +/- 1 but got 45
+FAIL block display with column width (math) assert_approx_equals: block size expected 80 +/- 1 but got 45
+FAIL block display with column width (mrow) assert_approx_equals: block size expected 80 +/- 1 but got 45
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
index 511ad56..66385f4 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
@@ -2,14 +2,14 @@
 Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Cannot read property 'receiver' of undefined
 FAIL checkAddTransceiverNoTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL checkAddTransceiverWithTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithAddTrack assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:{}},stopped:false},{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:{}},stopped:false}]" but got "[]"
+FAIL checkAddTransceiverWithAddTrack assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:{}}},{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:{}}}]" but got "[]"
 FAIL checkAddTransceiverWithDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 PASS checkMsidNoTrackId
 FAIL checkAddTransceiverWithSetRemoteOfferSending promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL checkAddTransceiverWithSetRemoteOfferNoSend promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL checkAddTransceiverBadKind assert_true: addTransceiver("foo") throws a TypeError expected true got false
-FAIL checkNoMidOffer assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
-FAIL checkNoMidAnswer assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{kind:\"audio\"}},stopped:false}]" but got "[]"
+FAIL checkNoMidOffer assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",receiver:{track:{kind:\"audio\"}},sender:{track:null}}]" but got "[]"
+FAIL checkNoMidAnswer assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{kind:\"audio\"}}}]" but got "[]"
 FAIL checkSetDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL checkCurrentDirection assert_equals: expected "[{currentDirection:null}]" but got "[]"
 FAIL checkSendrecvWithNoSendTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
@@ -31,7 +31,7 @@
 FAIL checkStopAfterCreateAnswer promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'stop' of undefined"
 FAIL checkStopAfterSetLocalAnswer promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'receiver' of undefined"
 FAIL checkStopAfterClose assert_equals: Stopping a transceiver on a closed PC should throw. throws InvalidStateError expected "InvalidStateError" but got "TypeError"
-FAIL checkLocalRollback assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{}},stopped:false}]" but got "[]"
+FAIL checkLocalRollback assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{}}}]" but got "[]"
 FAIL checkRollbackAndSetRemoteOfferWithDifferentType promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
 FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
 FAIL checkMsectionReuse promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'mid' of undefined"
diff --git a/third_party/usrsctp/BUILD.gn b/third_party/usrsctp/BUILD.gn
index 00ed6acb..b869852 100644
--- a/third_party/usrsctp/BUILD.gn
+++ b/third_party/usrsctp/BUILD.gn
@@ -9,6 +9,12 @@
     "usrsctplib/usrsctplib",
     "usrsctplib/usrsctplib/netinet",
   ]
+
+  # This is part of the public config because it's used both by usrsctp and
+  # the client (WebRTC), to determine if the length field needs populating.
+  if (is_mac || is_ios || target_os == "freebsd") {
+    defines = [ "HAVE_SCONN_LEN" ]
+  }
 }
 
 config("usrsctp_warnings") {
@@ -129,11 +135,9 @@
   } else if (is_apple) {
     defines += [
       "HAVE_SA_LEN",
-      "HAVE_SCONN_LEN",
       "__APPLE_USE_RFC_2292",
       "__Userspace_os_Darwin",
     ]
-    cflags += [ "-U__APPLE__" ]
   }
 
   if (is_win) {
diff --git a/third_party/usrsctp/README.chromium b/third_party/usrsctp/README.chromium
index 18ebfe0d..33139cb7 100644
--- a/third_party/usrsctp/README.chromium
+++ b/third_party/usrsctp/README.chromium
@@ -1,8 +1,8 @@
 Name: usrsctp
 URL: http://github.com/sctplab/usrsctp
 Version: 0
-Date: Feb 2, 2020
-Revision: bee946a606752a443bd70bca1cb296527fed706d
+Date: Aug 28, 2020
+Revision: 995c0b84414466d77d52011e5b572cbe213b770a
 License: New BSD
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/widevine/cdm/widevine.gni b/third_party/widevine/cdm/widevine.gni
index 769489b..ddf5c94 100644
--- a/third_party/widevine/cdm/widevine.gni
+++ b/third_party/widevine/cdm/widevine.gni
@@ -47,8 +47,17 @@
   bundle_widevine_cdm = false
 }
 
+declare_args() {
+# Enable Widevine CDM host verification, which will sign additional binaries
+# with Widevine certs and copy signature files as needed. Only enabled by
+# default on official bots. For developers building with "is_official_build"
+# locally without Widevine certs, this will cause a build failure, which can
+# be suppressed by manually setting `enable_widevine_cdm_host_verification` to
+# false.
 enable_widevine_cdm_host_verification =
     enable_library_widevine_cdm && enable_cdm_host_verification
+    && is_official_build
+}
 
 template("widevine_sign_file") {
   # For official builds, generate a signature file for |file| which will
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 902db6a2..32c6f27 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -328,6 +328,9 @@
   "chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd": {
     "includes": [2517],
   },
+  "chromeos/components/file_manager/resources/file_manager_resources.grd": {
+    "includes": [2518],
+  },
   "chromeos/components/help_app_ui/resources/help_app_resources.grd": {
     "includes": [2520],
   },
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index fd1a77dd..72057fc4 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -161,8 +161,8 @@
 
       if type_.origin.from_manifest_keys:
         c.Cblock(
-          self._GenerateParseFromDictionary(classname, classname_in_namespace,
-                                            type_))
+          self._GenerateParseFromDictionary(
+            classname, classname_in_namespace, type_))
     elif type_.property_type == PropertyType.ENUM:
       (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_))
         .Cblock(self._GenerateEnumFromString(cpp_namespace, type_))
@@ -490,8 +490,8 @@
     assert self._namespace.manifest_keys.property_type == PropertyType.OBJECT
     return self._GenerateType(None, self._namespace.manifest_keys)
 
-  def _GenerateParseFromDictionary(self, classname, classname_in_namespace,
-                                   type_):
+  def _GenerateParseFromDictionary(
+    self, classname, classname_in_namespace, type_):
     # type: (str, str, Type) -> Code
     """Generates a function that deserializes the type from the passed
     dictionary. E.g. for type "Foo", generates Foo::ParseFromDictionary().
@@ -613,6 +613,7 @@
       PropertyType.INTEGER,
       PropertyType.OBJECT,
       PropertyType.STRING,
+      PropertyType.ENUM
     }
 
     c = Code()
@@ -621,14 +622,47 @@
     assert (underlying_property_type in supported_property_types), (
       'Property type not implemented for %s, type: %s, namespace: %s' %
       (underlying_property_type, underlying_type.name,
-        underlying_type.namespace.name))
+      underlying_type.namespace.name))
+
+    assert (underlying_property_type != PropertyType.ARRAY or
+      underlying_type.item_type.property_type != PropertyType.ENUM), (
+      'Enum types in arrays are not currently supported. Type: %s.' %
+      underlying_type.name)
 
     property_constant = cpp_util.UnixNameToConstantName(property.unix_name)
     out_expression = '&out->%s' % property.unix_name
+
+    if underlying_property_type == PropertyType.ENUM:
+      enum_name = cpp_util.Classname(
+        schema_util.StripNamespace(underlying_type.name))
+      cpp_type_namespace = '' if underlying_type.namespace == self._namespace \
+          else '%s::' % underlying_type.namespace.unix_name
+
+      params = [
+        'dict',
+        '%s' % property_constant,
+        '&%sParse%s' % (cpp_type_namespace, enum_name),
+        'true' if property.optional else 'false',
+        '%s%s' % (cpp_type_namespace,
+                  self._type_helper.GetEnumNoneValue(underlying_type)),
+        '%s' % out_expression,
+        'error',
+        'error_path_reversed'
+      ]
+      func_name = 'ParseEnumFromDictionary'
+    else:
+      params = [
+        'dict',
+        '%s' % property_constant,
+        '%s' % out_expression,
+        'error',
+        'error_path_reversed'
+      ]
+      func_name = 'ParseFromDictionary'
+
     c.Sblock(
-      'if (!::json_schema_compiler::manifest_parse_util::ParseFromDictionary'
-      '(dict, %s, %s, error, error_path_reversed)) {' %
-      (property_constant, out_expression)
+      'if (!::json_schema_compiler::manifest_parse_util::%s(%s)) {'
+      % (func_name, self._GenerateParams(params, generate_error_messages=False))
     )
     if is_root_manifest_type:
       c.Append('::json_schema_compiler::manifest_parse_util::'
diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py
index e5159f1..d83f4f4 100644
--- a/tools/json_schema_compiler/cpp_util.py
+++ b/tools/json_schema_compiler/cpp_util.py
@@ -138,8 +138,7 @@
   # type (str) -> str
   """Converts unix_name to kUnixName.
   """
-  return ('k' + ''.join(word[0].upper() + word[1:]
-      for word in unix_name.split('_')))
+  return ('k' + ''.join(word.capitalize() for word in unix_name.split('_')))
 
 def IsUnixName(s):
   # type (str) -> bool
diff --git a/tools/json_schema_compiler/manifest_parse_util.cc b/tools/json_schema_compiler/manifest_parse_util.cc
index ae41bbc..98728c7 100644
--- a/tools/json_schema_compiler/manifest_parse_util.cc
+++ b/tools/json_schema_compiler/manifest_parse_util.cc
@@ -54,6 +54,20 @@
       "Parsing array failed: %s.", base::UTF16ToASCII(*error).c_str()));
 }
 
+void PopulateInvalidEnumValueError(
+    base::StringPiece key,
+    const std::string& value,
+    base::string16* error,
+    std::vector<base::StringPiece>* error_path_reversed) {
+  DCHECK(error);
+  DCHECK(error_path_reversed);
+  DCHECK(error_path_reversed->empty());
+
+  error_path_reversed->push_back(key);
+  *error = base::ASCIIToUTF16(
+      base::StringPrintf("Specified value '%s' is invalid.", value.c_str()));
+}
+
 void PopulateFinalError(base::string16* error,
                         std::vector<base::StringPiece>* error_path_reversed) {
   DCHECK(error);
@@ -77,7 +91,6 @@
     std::vector<base::StringPiece>* error_path_reversed) {
   DCHECK(error);
   DCHECK(error_path_reversed);
-  DCHECK(error->empty());
   DCHECK(error_path_reversed->empty());
 
   const base::Value* value = dict.FindKey(key);
diff --git a/tools/json_schema_compiler/manifest_parse_util.h b/tools/json_schema_compiler/manifest_parse_util.h
index 31923cc..d6c108d 100644
--- a/tools/json_schema_compiler/manifest_parse_util.h
+++ b/tools/json_schema_compiler/manifest_parse_util.h
@@ -25,6 +25,14 @@
     base::string16* error,
     std::vector<base::StringPiece>* error_path_reversed);
 
+// Populates |error| and |error_path_reversed| denoting the given invalid enum
+// |value| at the given |key|.
+void PopulateInvalidEnumValueError(
+    base::StringPiece key,
+    const std::string& value,
+    base::string16* error,
+    std::vector<base::StringPiece>* error_path_reversed);
+
 // Populates manifest parse |error| for the given path in |error_path_reversed|.
 void PopulateFinalError(base::string16* error,
                         std::vector<base::StringPiece>* error_path_reversed);
@@ -129,6 +137,47 @@
   return true;
 }
 
+// Alias for pointer to a function which converts a string to an enum of type T.
+template <typename T>
+using StringToEnumConverter = T (*)(const std::string&);
+
+// Parses enum |out| from |dict| at the given |key|. On failure, returns false
+// and populates |error| and |error_path_reversed|.
+template <typename T>
+bool ParseEnumFromDictionary(
+    const base::DictionaryValue& dict,
+    base::StringPiece key,
+    StringToEnumConverter<T> converter,
+    bool is_optional_property,
+    T none_value,
+    T* out,
+    base::string16* error,
+    std::vector<base::StringPiece>* error_path_reversed) {
+  DCHECK(out);
+  DCHECK_EQ(none_value, *out);
+
+  // Ignore optional keys if they are not present without raising an error.
+  if (is_optional_property && !dict.FindKey(key))
+    return true;
+
+  // Parse errors for optional keys which are specified should still cause a
+  // failure.
+  const base::Value* value = FindKeyOfType(dict, key, base::Value::Type::STRING,
+                                           error, error_path_reversed);
+  if (!value)
+    return false;
+
+  const std::string str = value->GetString();
+  T enum_value = converter(str);
+  if (enum_value == none_value) {
+    PopulateInvalidEnumValueError(key, str, error, error_path_reversed);
+    return false;
+  }
+
+  *out = enum_value;
+  return true;
+}
+
 }  // namespace manifest_parse_util
 }  // namespace json_schema_compiler
 
diff --git a/tools/json_schema_compiler/test/simple_api.json b/tools/json_schema_compiler/test/simple_api.json
index 9ad8175..de2c8b3 100644
--- a/tools/json_schema_compiler/test/simple_api.json
+++ b/tools/json_schema_compiler/test/simple_api.json
@@ -4,6 +4,11 @@
     "description": "This is a simple API.",
     "types": [
       {
+        "id": "TestEnum",
+        "type": "string",
+        "enum": ["one", "two", "three"]
+      },
+      {
         "id": "TestType",
         "type": "object",
         "properties": {
@@ -38,13 +43,12 @@
             "items": {
               "type": "string"
             }
+          },
+          "opt_external_enum": {
+            "optional": true,
+            "$ref": "enums.Enumeration"
           }
         }
-      },
-      {
-        "id": "TestEnum",
-        "type": "string",
-        "enum": ["one", "two", "three"]
       }
     ],
     "manifest_keys": {
@@ -67,6 +71,9 @@
             "type": "boolean"
           }
         }
+      },
+      "key_enum": {
+        "$ref": "TestEnum"
       }
     },
     "functions": [
diff --git a/tools/json_schema_compiler/test/simple_api_unittest.cc b/tools/json_schema_compiler/test/simple_api_unittest.cc
index 74613b3..d3adb399 100644
--- a/tools/json_schema_compiler/test/simple_api_unittest.cc
+++ b/tools/json_schema_compiler/test/simple_api_unittest.cc
@@ -12,8 +12,10 @@
 #include "base/values.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "tools/json_schema_compiler/test/enums.h"
 
 namespace simple_api = test::api::simple_api;
+namespace enums = test::api::enums;
 
 namespace {
 
@@ -49,7 +51,7 @@
   bool result = simple_api::ManifestKeys::ParseFromDictionary(
       base::Value::AsDictionaryValue(*manifest), manifest_keys, &error_16);
 
-  ASSERT_TRUE(result);
+  ASSERT_TRUE(result) << error_16;
   ASSERT_TRUE(error_16.empty()) << error_16;
 }
 
@@ -245,7 +247,8 @@
       "integer": 32,
       "object": {
         "foo": "bar"
-      }
+      },
+      "key_enum": "one"
     }
   })";
 
@@ -266,7 +269,8 @@
       "number": 25.4,
       "integer": 32,
       "array": ["one", "two", 3]
-    }
+    },
+    "key_enum": "one"
   })";
 
   std::string error;
@@ -277,6 +281,27 @@
       error);
 }
 
+TEST(JsonSchemaCompilerSimpleTest, ManifestKeyParsing_InvalidEnumValue) {
+  const char kPartialManifestJson[] = R"({
+    "key_string": "abc",
+    "key_ref": {
+      "string": "ref_string",
+      "boolean": true,
+      "number": 25.4,
+      "integer": 32,
+      "opt_external_enum": "four"
+    },
+    "key_enum": "one"
+  })";
+
+  std::string error;
+  ASSERT_NO_FATAL_FAILURE(GetManifestParseError(kPartialManifestJson, &error));
+  EXPECT_EQ(
+      "Error at key 'key_ref.opt_external_enum'. Specified value 'four' is "
+      "invalid.",
+      error);
+}
+
 TEST(JsonSchemaCompilerSimpleTest, ManifestKeyParsing_Success_AllKeys) {
   const char kPartialManifestJson[] = R"({
     "key_string": "abc",
@@ -288,12 +313,14 @@
       "object": {
         "foo": 42
       },
-      "array": ["one", "two"]
+      "array": ["one", "two"],
+      "opt_external_enum": "two"
     },
     "key_obj": {
       "obj_string": "foo",
       "obj_bool": true
-    }
+    },
+    "key_enum": "one"
   })";
 
   simple_api::ManifestKeys manifest_keys;
@@ -306,6 +333,8 @@
   EXPECT_EQ("foo", manifest_keys.key_obj->obj_string);
   EXPECT_TRUE(manifest_keys.key_obj->obj_bool);
 
+  EXPECT_EQ(simple_api::TEST_ENUM_ONE, manifest_keys.key_enum);
+
   EXPECT_EQ("ref_string", manifest_keys.key_ref.string);
   EXPECT_EQ(true, manifest_keys.key_ref.boolean);
   EXPECT_DOUBLE_EQ(25.4, manifest_keys.key_ref.number);
@@ -315,6 +344,7 @@
   ASSERT_TRUE(manifest_keys.key_ref.array);
   EXPECT_THAT(*manifest_keys.key_ref.array,
               ::testing::ElementsAre("one", "two"));
+  EXPECT_EQ(enums::ENUMERATION_TWO, manifest_keys.key_ref.opt_external_enum);
 }
 
 // Ensure leaving out optional keys is not a manifest parse error.
@@ -327,7 +357,8 @@
       "boolean": true,
       "number": 25.4,
       "integer": 32
-    }
+    },
+    "key_enum": "two"
   })";
 
   simple_api::ManifestKeys manifest_keys;
@@ -336,10 +367,12 @@
 
   EXPECT_EQ("abc", manifest_keys.key_string);
   EXPECT_FALSE(manifest_keys.key_obj);
+  EXPECT_EQ(simple_api::TEST_ENUM_TWO, manifest_keys.key_enum);
 
   EXPECT_EQ("ref_string", manifest_keys.key_ref.string);
   EXPECT_EQ(true, manifest_keys.key_ref.boolean);
   EXPECT_DOUBLE_EQ(25.4, manifest_keys.key_ref.number);
   EXPECT_EQ(32, manifest_keys.key_ref.integer);
   EXPECT_FALSE(manifest_keys.key_ref.array);
+  EXPECT_EQ(enums::ENUMERATION_NONE, manifest_keys.key_ref.opt_external_enum);
 }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 947830e..d6ab6830 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -27,6 +27,7 @@
       'linux-chrome-beta': 'official_goma',
       'linux-chrome-stable': 'official_goma',
       'linux-chromeos-chrome': 'official_goma_chromeos',
+      'linux-chromeos-chrome-easwa': 'official_goma_chromeos',
       'mac-chrome': 'official_goma',
       'mac-chrome-beta': 'official_goma',
       'mac-chrome-stable': 'official_goma',
@@ -255,6 +256,7 @@
       'Linux remote_run Tester': 'release_bot',
       'Linux Viz': 'release_trybot',
       'linux-ash-chromium-builder-fyi-rel': 'chromeos_with_codecs_release_bot',
+      'linux-inverse-fieldtrials-fyi-rel': 'gpu_tests_release_bot_minimal_symbols_invert_fieldtrials',
       'linux-lacros-builder-fyi-rel': 'lacros_on_linux_release_bot',
       'linux-lacros-tester-fyi-rel': 'lacros_on_linux_release_bot',
       'Mac Builder Next': 'gpu_tests_release_bot_minimal_symbols',
@@ -814,6 +816,7 @@
       'linux-chrome-beta': 'official_goma',
       'linux-chrome-stable': 'official_goma',
       'linux-chromeos-chrome': 'official_goma_chromeos_include_unwind_tables',
+      'linux-chromeos-chrome-easwa': 'official_goma_chromeos_include_unwind_tables',
       'mac-chrome': 'official_goma',
       'mac-chrome-beta': 'official_goma',
       'mac-chrome-stable': 'official_goma',
@@ -902,6 +905,7 @@
       'linux-clang-tidy-dbg': 'debug_bot',
       'linux-clang-tidy-rel': 'release_trybot',
       'linux-dcheck-off-rel': 'release_trybot_dcheck_off',
+      'linux-inverse-fieldtrials-fyi-rel': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_invert_fieldtrials',
       'linux-gcc-rel': 'release_bot_x86_minimal_symbols_no_clang_cxx11',
       'linux-lacros-fyi-rel': 'lacros_on_linux_release_trybot',
       'linux-libfuzzer-asan-rel': 'libfuzzer_asan_release_trybot',
@@ -1899,6 +1903,10 @@
       'gpu_tests', 'debug_bot', 'x86', 'no_symbols'
     ],
 
+    'gpu_tests_release_bot': [
+      'gpu_tests', 'release_bot',
+    ],
+
     'gpu_tests_release_bot_minimal_symbols': [
       'gpu_tests', 'release_bot', 'minimal_symbols',
     ],
@@ -1908,11 +1916,12 @@
       'partial_code_coverage_instrumentation',
     ],
 
+    'gpu_tests_release_bot_minimal_symbols_invert_fieldtrials': [
+      'gpu_tests', 'release_bot', 'minimal_symbols', 'invert_fieldtrials',
+    ],
+
     'gpu_tests_release_bot_minimal_symbols_no_nacl': [
-      'gpu_tests',
-      'release_bot',
-      'minimal_symbols',
-      'disable_nacl',
+      'gpu_tests', 'release_bot', 'minimal_symbols', 'disable_nacl',
     ],
 
     'gpu_tests_release_bot_x86_minimal_symbols': [
@@ -1961,8 +1970,9 @@
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
     ],
 
-    'gpu_tests_release_trybot': [
-      'gpu_tests', 'release_trybot',
+    'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_invert_fieldtrials': [
+      'gpu_tests', 'release_trybot', 'no_symbols', 'use_dummy_lastchange',
+      'invert_fieldtrials',
     ],
 
     'gpu_tests_release_trybot_ptr_comp': [
@@ -1973,10 +1983,6 @@
       'gpu_tests', 'release_trybot', 'x86', 'resource_whitelisting',
     ],
 
-    'gpu_tests_release_bot': [
-      'gpu_tests', 'release_bot',
-    ],
-
     'gpu_tests_sk_dawn_release_trybot': [
       'gpu_tests', 'sk_dawn', 'release_trybot',
     ],
@@ -2765,6 +2771,10 @@
       'gn_args': 'internal_gles2_conform_tests=true build_angle_gles1_conform_tests=true',
     },
 
+    'invert_fieldtrials': {
+      'gn_args': 'invert_fieldtrials=true',
+    },
+
     'ios': {
       'gn_args': 'target_os="ios"',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 796889b82..0cd38739 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -29157,6 +29157,7 @@
   <int value="3467" label="CSSContainAllWithoutContentVisibility"/>
   <int value="3468" label="TimerInstallFromBeforeUnload"/>
   <int value="3469" label="TimerInstallFromUnload"/>
+  <int value="3470" label="ElementAttachInternalsBeforeConstructor"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -40965,6 +40966,7 @@
   <int value="-1645071473" label="ChromeColors:disabled"/>
   <int value="-1644721118"
       label="disable-experimental-accessibility-chromevox-search-menus"/>
+  <int value="-1643933608" label="SyncAutofillWalletOfferData:enabled"/>
   <int value="-1641832607" label="DragToPinTabs:enabled"/>
   <int value="-1638815914" label="enable-experimental-productivity-features"/>
   <int value="-1634878515" label="ChromeHomeModernLayout:enabled"/>
@@ -41271,6 +41273,7 @@
   <int value="-1357655121" label="enable-iframe-based-signin"/>
   <int value="-1357008397" label="ContextualSuggestionsCarousel:disabled"/>
   <int value="-1356379431" label="GlobalMediaControlsOverlayControls:enabled"/>
+  <int value="-1353865639" label="InteractiveWindowCycleList:disabled"/>
   <int value="-1353714232" label="FilesTransferDetails:enabled"/>
   <int value="-1353195918" label="UseSkiaRenderer:enabled"/>
   <int value="-1351968795" label="SingleProcessMash:enabled"/>
@@ -41985,6 +41988,7 @@
   <int value="-632030508" label="NativeWindowNavButtons:disabled"/>
   <int value="-631740127" label="inert-visual-viewport"/>
   <int value="-631614101" label="CameraSystemWebApp:enabled"/>
+  <int value="-630946893" label="TabSearchFixedEntrypoint:enabled"/>
   <int value="-626329144" label="SyncSupportTrustedVaultPassphrase:enabled"/>
   <int value="-626211146" label="DesktopMinimalUI:disabled"/>
   <int value="-625695291" label="AccessibilityExposeARIAAnnotations:enabled"/>
@@ -42153,6 +42157,7 @@
   <int value="-460313418" label="ProgressBarThrottle:enabled"/>
   <int value="-460081932" label="CustomFeedbackUi:disabled"/>
   <int value="-459318667" label="AccessiblePDFForm:enabled"/>
+  <int value="-457584339" label="TemporaryHoldingSpace:enabled"/>
   <int value="-457292000" label="HappinessTrackingSurveysForDesktop:enabled"/>
   <int value="-457174225" label="Av1Decoder:enabled"/>
   <int value="-456321929" label="ForceEnableSystemAec:disabled"/>
@@ -42340,6 +42345,7 @@
       label="AutofillSaveCreditCardUsesStrikeSystemV2:enabled"/>
   <int value="-268357961" label="enable-feature-policy"/>
   <int value="-265697837" label="PhoneHub:disabled"/>
+  <int value="-263645996" label="InteractiveWindowCycleList:enabled"/>
   <int value="-263150202" label="BundledConnectionHelp:disabled"/>
   <int value="-262122630" label="ArcEnableDocumentsProviderInFilesApp:enabled"/>
   <int value="-258081634" label="AutofillAssistantDirectActions:disabled"/>
@@ -42603,6 +42609,7 @@
   <int value="8891567" label="CaptionSettings:enabled"/>
   <int value="10458238" label="disable-print-preview-simplify"/>
   <int value="11698808" label="enable-dom-distiller-button-animation"/>
+  <int value="12016364" label="OmniboxBubbleUrlSuggestions:enabled"/>
   <int value="15614295" label="Portals:enabled"/>
   <int value="19629326" label="OmniboxExperimentalKeywordMode:enabled"/>
   <int value="19815558" label="EnableSettingsShortcutSearch:disabled"/>
@@ -42804,6 +42811,7 @@
   <int value="266702296" label="disable-plugin-power-saver"/>
   <int value="268535107"
       label="ash-new-touch-support-for-screen-magnification"/>
+  <int value="270118291" label="SyncAutofillWalletOfferData:disabled"/>
   <int value="270267831" label="enable-scripts-require-action"/>
   <int value="272108480" label="SmbFs:enabled"/>
   <int value="272631627" label="BookmarkAppsMac:enabled"/>
@@ -43410,6 +43418,7 @@
   <int value="879699575" label="disable-gesture-tap-highlight"/>
   <int value="879992337" label="disable-pull-to-refresh-effect"/>
   <int value="880510010" label="enable-permissions-bubbles"/>
+  <int value="880677998" label="TabSearchFixedEntrypoint:disabled"/>
   <int value="883190338" label="PrintWithReducedRasterization:disabled"/>
   <int value="884106779" label="supervised-user-safesites"/>
   <int value="885004540" label="ArcEnableApplicationZoomFeature:enabled"/>
@@ -44008,6 +44017,7 @@
   <int value="1496571153" label="enable-webapk"/>
   <int value="1497609106" label="ContextualNudges:enabled"/>
   <int value="1497924954" label="js-flags"/>
+  <int value="1498398756" label="TemporaryHoldingSpace:disabled"/>
   <int value="1499163193" label="PostScriptPrinting:disabled"/>
   <int value="1500390299" label="NtpCustomizationMenuV2:disabled"/>
   <int value="1505194447" label="disable-transition-compositing"/>
@@ -44526,6 +44536,7 @@
   <int value="2032558514"
       label="RemoveUsageOfDeprecatedGaiaSigninEndpoint:enabled"/>
   <int value="2034198538" label="TabHoverCards:enabled"/>
+  <int value="2037562553" label="OmniboxBubbleUrlSuggestions:disabled"/>
   <int value="2037756154" label="enable-impl-side-painting"/>
   <int value="2038036881" label="NewShortcutMapping:enabled"/>
   <int value="2039276757" label="EnableOverviewRoundedCorners:enabled"/>
@@ -68581,6 +68592,7 @@
   <int value="5" label="Voice"/>
   <int value="6" label="Calculator"/>
   <int value="7" label="Favicon"/>
+  <int value="8" label="Trends"/>
 </enum>
 
 <enum name="SuggestionPedalType">
@@ -75602,6 +75614,15 @@
   <int value="2" label="Signature whitelist"/>
 </enum>
 
+<enum name="WidgetClosedReason">
+  <int value="0" label="Unspecified"/>
+  <int value="1" label="EscKeyPressed"/>
+  <int value="2" label="CloseButtonClicked"/>
+  <int value="3" label="LostFocus"/>
+  <int value="4" label="CancelButtonClicked"/>
+  <int value="5" label="AcceptButtonClicked"/>
+</enum>
+
 <enum name="WiFiAp80211kSupport">
   <int value="0" label="Does not support 802.11k"/>
   <int value="1" label="Supports 802.11k neighbor lists"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 65de6fc..03a558e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -28953,8 +28953,12 @@
 
 <histogram name="Compositing.Browser.GPUMemoryForTilingsInKb" units="Kb"
     expires_after="M87">
+  <obsolete>
+    Removed September, 2020. Memory.GPU.PeakMemoryUsage.PageLoad can be used to
+    track overall GPU memory usage.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
-  <owner>ericrk@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
   <summary>
     The GPU memory allocated for tilings (in a browser process), summed over all
     picture layers. Emitted once per drawing. Nothing is recorded if there are
@@ -28962,8 +28966,6 @@
 
     Tilings are used for decomposition of the layer's entire contents at a
     picture screenspace resolution to do threaded rasterization.
-
-    Team: paint-dev@chromium.org.
   </summary>
 </histogram>
 
@@ -29655,6 +29657,9 @@
 <histogram
     name="Compositing.Renderer.DirectlyCompositedImage.AvoidRasterAdjustmentWithTransformTrigger"
     units="boolean" expires_after="M87">
+  <obsolete>
+    Removed September 2020 because the research it was used for is complete.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -29670,6 +29675,9 @@
 <histogram
     name="Compositing.Renderer.DirectlyCompositedImage.RasterScaleDirectlyComposited"
     units="boolean" expires_after="M87">
+  <obsolete>
+    Removed September 2020 because the research it was used for is complete.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -29682,6 +29690,9 @@
 
 <histogram name="Compositing.Renderer.DirectlyCompositedImage.TileAreaAdded"
     units="pixels" expires_after="M87">
+  <obsolete>
+    Removed September 2020 because the research it was used for is complete.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -29695,6 +29706,9 @@
 
 <histogram name="Compositing.Renderer.DirectlyCompositedImage.TileAreaMatches"
     units="boolean" expires_after="M87">
+  <obsolete>
+    Removed September 2020 because the research it was used for is complete.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -29709,6 +29723,9 @@
 
 <histogram name="Compositing.Renderer.DirectlyCompositedImage.TileAreaSaved"
     units="pixels" expires_after="M87">
+  <obsolete>
+    Removed September 2020 because the research it was used for is complete.
+  </obsolete>
   <owner>pdr@chromium.org</owner>
   <owner>dlibby@microsoft.com</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -29760,9 +29777,9 @@
 </histogram>
 
 <histogram name="Compositing.Renderer.GPUMemoryForTilingsInKb" units="Kb"
-    expires_after="M87">
+    expires_after="M90">
   <owner>pdr@chromium.org</owner>
-  <owner>ericrk@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
   <summary>
     The GPU memory allocated for tilings (in a renderer process), summed over
     all picture layers. Emitted once per drawing. Nothing is recorded if there
@@ -29770,8 +29787,6 @@
 
     Tilings are used for decomposition of the layer's entire contents at a
     picture screenspace resolution to do threaded rasterization.
-
-    Team: paint-dev@chromium.org.
   </summary>
 </histogram>
 
@@ -100155,9 +100170,15 @@
   </summary>
 </histogram>
 
-<histogram name="Net.HttpProxy.ConnectLatency" units="ms" expires_after="M85">
+<histogram name="Net.HttpProxy.ConnectLatency" units="ms"
+    expires_after="2021-09-14">
+  <owner>eroman@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
-  <summary>Time taken to establish the connection to the HTTP proxy.</summary>
+  <summary>
+    Time taken to establish the connection to the HTTP proxy. Note that the
+    histogram expired briefly for M-86 and M-87 non-stable versions. Exact
+    counts during that period may not be meaningful.
+  </summary>
 </histogram>
 
 <histogram name="Net.HttpProxySocketRequestTime" units="ms"
@@ -104456,6 +104477,28 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.ZeroRttReasonGoogle"
+    enum="SSLHandshakeEarlyDataReason" expires_after="2021-05-11">
+  <owner>renjietang@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Indicates whether a QUIC handshake attempted or used 0-RTT on the
+    connection, and if 0-RTT was not used, it provides reasons why. Only records
+    connections with Google hosted servers.
+  </summary>
+</histogram>
+
+<histogram name="Net.QuicSession.ZeroRttReasonNonGoogle"
+    enum="SSLHandshakeEarlyDataReason" expires_after="2021-05-11">
+  <owner>renjietang@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Indicates whether a QUIC handshake attempted or used 0-RTT on the
+    connection, and if 0-RTT was not used, it provides reasons why. Only records
+    connections with non-Google servers.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.ZeroRttState" enum="ZeroRttState"
     expires_after="2021-05-11">
   <owner>renjietang@chromium.org</owner>
@@ -112720,6 +112763,17 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.Modules.ShownTime" units="ms"
+    expires_after="2021-01-01">
+  <owner>tiborg@chromium.org</owner>
+  <owner>yyushkina@chromium.org</owner>
+  <owner>chrome-desktop-ntp@google.com</owner>
+  <summary>
+    Histogram of the time, in milliseconds since navigation start, it took until
+    the modules showed up on the NTP.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.MostVisited" enum="MostVisitedTileIndex"
     expires_after="never">
 <!-- expires-never: part of top-line metric (internal: go/chrome-browser-nsm) -->
@@ -192210,6 +192264,18 @@
   </summary>
 </histogram>
 
+<histogram name="WebApp.InstallConfirmation.CloseReason"
+    enum="WidgetClosedReason" expires_after="M95">
+  <owner>dmurph@chromium.org</owner>
+  <owner>desktop-pwa-team@google.com</owner>
+  <summary>
+    Records the reason that the WebApp install confirmation dialog is closed.
+    This dialog is shown when the user clicks on the 'install' icon in the
+    omnibox for websites that are installable. This metric is intended to give
+    data for an experiment to choose the best install icon.
+  </summary>
+</histogram>
+
 <histogram name="Webapp.InstallDuration.System" units="ms"
     expires_after="2021-03-07">
   <owner>calamity@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 0c83629..0f30ecb 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/6b0afc48ae3d07d8cf6598be674562f9844333ec/trace_processor_shell"
         },
         "linux": {
-            "hash": "2b1c2e7aeb587514deb69cce2d6223c3d2aad6f6",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/9edd1a906f57a995531db1050629b608424ee202/trace_processor_shell"
+            "hash": "a4dde16c3a65a90950fbef9de7003dc503bfefa8",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/bef393a5a340a4d6303b263cba7adee98372ab7f/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 1702583..71ff201 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -162,6 +162,7 @@
  <item id="isolated_prerender_canary_check" hash_code="60914954" type="0" content_hash_code="2991117" os_list="linux,windows" file_path="chrome/browser/prerender/isolated/isolated_prerender_origin_prober.cc"/>
  <item id="isolated_prerender_loader" hash_code="2181152" type="0" content_hash_code="113933667" os_list="linux,windows" file_path="chrome/browser/prerender/isolated/isolated_prerender_url_loader.cc"/>
  <item id="isolated_prerender_probe" hash_code="115160522" type="0" content_hash_code="75211055" os_list="linux,windows" file_path="chrome/browser/prerender/isolated/isolated_prerender_origin_prober.cc"/>
+ <item id="javascript_report_error" hash_code="109607776" type="0" content_hash_code="30837893" os_list="linux,windows" file_path="components/crash/content/browser/error_reporting/send_javascript_error_report.cc"/>
  <item id="kaleidoscope_service" hash_code="49759694" type="0" content_hash_code="14307563" os_list="linux,windows" file_path="chrome/browser/media/kaleidoscope/kaleidoscope_service.cc"/>
  <item id="kids_chrome_management_client_classify_url" hash_code="109987793" type="0" deprecated="2019-07-30" content_hash_code="112740597" file_path=""/>
  <item id="lib_address_input" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 6637b65..588ea5e 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -330,6 +330,9 @@
       <traffic_annotation unique_id="cros_recovery_image_download"/>
       <traffic_annotation unique_id="download_recovery_component"/>
     </sender>
+    <sender name="Crash Reports">
+      <traffic_annotation unique_id="javascript_report_error"/>
+    </sender>
     <sender name="Chrome Performance, Feedback, and Usage Statistics">
       <traffic_annotation unique_id="background_performance_tracer"/>
       <traffic_annotation unique_id="chrome_feedback_report_app"/>
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc
index aae4825f..1ec54927 100644
--- a/ui/gl/direct_composition_surface_win.cc
+++ b/ui/gl/direct_composition_surface_win.cc
@@ -38,6 +38,8 @@
 bool g_supports_overlays = false;
 // Whether the DecodeSwapChain is disabled or not.
 bool g_decode_swap_chain_disabled = false;
+// Whether to force the nv12 overlay support.
+bool g_force_nv12_overlay_support = false;
 
 // The lock to guard g_overlay_caps_valid and g_supports_overlays.
 base::Lock& GetOverlayLock() {
@@ -295,6 +297,12 @@
       &bgra8_overlay_support_flags, &rgb10a2_overlay_support_flags,
       &overlay_monitor_size);
 
+  if (g_force_nv12_overlay_support) {
+    supports_overlays = true;
+    nv12_overlay_support_flags = DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+    overlay_format_used = DXGI_FORMAT_NV12;
+  }
+
   if (supports_overlays != SupportsOverlays() ||
       overlay_format_used != g_overlay_format_used) {
     // Record the new histograms
@@ -639,6 +647,13 @@
   g_enable_bgra8_overlays_with_yuv_overlay_support = true;
 }
 
+// static
+void DirectCompositionSurfaceWin::ForceNV12OverlaySupport() {
+  // This has to be set before initializing overlay caps.
+  DCHECK(!OverlayCapsValid());
+  g_force_nv12_overlay_support = true;
+}
+
 bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
   d3d11_device_ = QueryD3D11DeviceObjectFromANGLE();
   if (!d3d11_device_) {
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h
index 799ffa8..0a608315 100644
--- a/ui/gl/direct_composition_surface_win.h
+++ b/ui/gl/direct_composition_surface_win.h
@@ -100,6 +100,10 @@
   // unsupported. So allow manually enabling BGRA8 overlay support.
   static void EnableBGRA8OverlaysWithYUVOverlaySupport();
 
+  // Forces to enable NV12 overlay support regardless of the query results from
+  // IDXGIOutput3::CheckOverlaySupport().
+  static void ForceNV12OverlaySupport();
+
   // GLSurfaceEGL implementation.
   bool Initialize(GLSurfaceFormat format) override;
   void Destroy() override;
diff --git a/ui/views/controls/link.cc b/ui/views/controls/link.cc
index 0ed41be..7dc1a389 100644
--- a/ui/views/controls/link.cc
+++ b/ui/views/controls/link.cc
@@ -85,7 +85,7 @@
   if (GetEnabled() &&
       (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) &&
       HitTestPoint(event.location()))
-    OnClick(event.flags());
+    OnClick(event);
 }
 
 void Link::OnMouseCaptureLost() {
@@ -101,7 +101,7 @@
     return false;
 
   SetPressed(false);
-  OnClick(event.flags());
+  OnClick(event);
   return true;
 }
 
@@ -112,7 +112,7 @@
   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
     SetPressed(true);
   } else if (event->type() == ui::ET_GESTURE_TAP) {
-    OnClick(event->flags());
+    OnClick(*event);
   } else {
     SetPressed(false);
     return;
@@ -182,10 +182,10 @@
   }
 }
 
-void Link::OnClick(int event_flags) {
+void Link::OnClick(const ui::Event& event) {
   RequestFocus();
-  if (!callback_.is_null())
-    callback_.Run(event_flags);
+  if (callback_)
+    callback_.Run(event);
 }
 
 void Link::RecalculateFont() {
diff --git a/ui/views/controls/link.h b/ui/views/controls/link.h
index b7ae40b..802ce73 100644
--- a/ui/views/controls/link.h
+++ b/ui/views/controls/link.h
@@ -32,7 +32,7 @@
 
   // A callback to be called when the link is clicked.  Closures are also
   // accepted; see below.
-  using ClickedCallback = base::RepeatingCallback<void(int event_flags)>;
+  using ClickedCallback = base::RepeatingCallback<void(const ui::Event& event)>;
 
   explicit Link(const base::string16& title = base::string16(),
                 int text_context = style::CONTEXT_LABEL,
@@ -44,9 +44,10 @@
   // way.
   void set_callback(base::RepeatingClosure callback) {
     // Adapt this closure to a ClickedCallback by discarding the extra arg.
-    callback_ = base::BindRepeating(
-        [](base::RepeatingClosure closure, int) { closure.Run(); },
-        std::move(callback));
+    callback_ =
+        base::BindRepeating([](base::RepeatingClosure closure,
+                               const ui::Event& event) { closure.Run(); },
+                            std::move(callback));
   }
   void set_callback(ClickedCallback callback) {
     callback_ = std::move(callback);
@@ -76,7 +77,7 @@
  private:
   void SetPressed(bool pressed);
 
-  void OnClick(int event_flags);
+  void OnClick(const ui::Event& event);
 
   void RecalculateFont();
 
diff --git a/ui/views/controls/link_unittest.cc b/ui/views/controls/link_unittest.cc
index 4ae16fa0..490079e 100644
--- a/ui/views/controls/link_unittest.cc
+++ b/ui/views/controls/link_unittest.cc
@@ -53,8 +53,7 @@
 TEST_F(LinkTest, TestLinkClick) {
   bool link_clicked = false;
   link()->set_callback(base::BindRepeating(
-      [](bool* link_clicked, int event_flags) { *link_clicked = true; },
-      &link_clicked));
+      [](bool* link_clicked) { *link_clicked = true; }, &link_clicked));
   link()->SizeToPreferredSize();
   gfx::Point point = link()->bounds().CenterPoint();
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
@@ -67,8 +66,7 @@
 TEST_F(LinkTest, TestLinkTap) {
   bool link_clicked = false;
   link()->set_callback(base::BindRepeating(
-      [](bool* link_clicked, int event_flags) { *link_clicked = true; },
-      &link_clicked));
+      [](bool* link_clicked) { *link_clicked = true; }, &link_clicked));
   link()->SizeToPreferredSize();
   gfx::Point point = link()->bounds().CenterPoint();
   ui::GestureEvent tap_event(point.x(), point.y(), 0, ui::EventTimeForNow(),
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 036d1b1..e6a9668 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2389,13 +2389,16 @@
     // This is a top-level menu, position it relative to the anchor bounds.
     const gfx::Rect& anchor_bounds = state_.initial_bounds;
 
+    using MenuPosition = MenuItemView::MenuPosition;
+
     // First the size gets reduced to the possible space.
     if (!monitor_bounds.IsEmpty()) {
       int max_width = monitor_bounds.width();
       int max_height = monitor_bounds.height();
       // In case of bubbles, the maximum width is limited by the space
       // between the display corner and the target area + the tip size.
-      if (state_.anchor == MenuAnchorPosition::kBubbleAbove) {
+      if (state_.anchor == MenuAnchorPosition::kBubbleAbove ||
+          item->actual_menu_position() == MenuPosition::kAboveBounds) {
         // Don't consider |border_and_shadow_insets| because when the max size
         // is enforced, the scroll view is shown and the md shadows are not
         // applied.
@@ -2432,6 +2435,7 @@
         y = anchor_bounds.bottom() - border_and_shadow_insets.top() +
             menu_config.touchable_anchor_offset;
       }
+      item->set_actual_menu_position(MenuPosition::kAboveBounds);
     } else if (state_.anchor == MenuAnchorPosition::kBubbleLeft ||
                state_.anchor == MenuAnchorPosition::kBubbleRight) {
       if (state_.anchor == MenuAnchorPosition::kBubbleLeft) {
@@ -2461,18 +2465,35 @@
       const int y_below = anchor_bounds.y() - border_and_shadow_insets.top();
       const int y_above = anchor_bounds.bottom() - menu_size.height() +
                           border_and_shadow_insets.bottom();
-      if (y_below + menu_size.height() <= monitor_bounds.bottom()) {
+
+      const bool able_to_show_below =
+          (y_below + menu_size.height() <= monitor_bounds.bottom());
+      const bool able_to_show_above = (y_above >= monitor_bounds.y());
+
+      // Respect the actual menu position calculated earlier if possible, to
+      // prevent changing positions during menu size updates.
+
+      if (item->actual_menu_position() == MenuPosition::kBelowBounds &&
+          able_to_show_below) {
+        y = y_below;
+      } else if (item->actual_menu_position() == MenuPosition::kAboveBounds &&
+                 able_to_show_above) {
+        y = y_above;
+      } else if (able_to_show_below) {
         // Show below the anchor. Align the top of the menu with the top of the
         // anchor.
         y = y_below;
-      } else if (y_above >= monitor_bounds.y()) {
+        item->set_actual_menu_position(MenuPosition::kBelowBounds);
+      } else if (able_to_show_above) {
         // No room below, but there is room above. Show above the anchor. Align
         // the bottom of the menu with the bottom of the anchor.
         y = y_above;
+        item->set_actual_menu_position(MenuPosition::kAboveBounds);
       } else {
         // No room above or below. Show as low as possible. Align the bottom of
         // the menu with the bottom of the screen.
         y = monitor_bounds.bottom() - menu_size.height();
+        item->set_actual_menu_position(MenuPosition::kBestFit);
       }
     }
     // The above adjustments may have shifted a large menu off the screen.
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 9a8ec213..4ac27d0 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -988,6 +988,70 @@
   ResetSelection();
 }
 
+// Verifies that the context menu bubble should prioritize its cached menu
+// position (above or below the anchor) after its size updates
+// (https://crbug.com/1126244).
+TEST_F(MenuControllerTest, VerifyMenuBubblePositionAfterSizeChanges) {
+  constexpr gfx::Rect monitor_bounds(0, 0, 500, 500);
+  constexpr gfx::Size menu_size(100, 200);
+  const gfx::Insets border_and_shadow_insets =
+      BubbleBorder::GetBorderAndShadowInsets(
+          MenuConfig::instance().touchable_menu_shadow_elevation);
+
+  // Calculate the suitable anchor point to ensure that if the menu shows below
+  // the anchor point, the bottom of the menu should be one pixel off the
+  // bottom of the display. It means that there is insufficient space for the
+  // menu below the anchor.
+  const gfx::Point anchor_point(monitor_bounds.width() / 2,
+                                monitor_bounds.bottom() + 1 -
+                                    menu_size.height() +
+                                    border_and_shadow_insets.top());
+
+  MenuBoundsOptions options;
+  options.menu_anchor = MenuAnchorPosition::kBubbleRight;
+  options.monitor_bounds = monitor_bounds;
+  options.anchor_bounds = gfx::Rect(anchor_point, gfx::Size());
+
+  // Case 1: There is insufficient space for the menu below `anchor_point` and
+  // there is no cached menu position. The menu should show above the anchor.
+  {
+    options.menu_size = menu_size;
+    ASSERT_GT(options.anchor_bounds.y() - border_and_shadow_insets.top() +
+                  menu_size.height(),
+              monitor_bounds.bottom());
+    CalculateBubbleMenuBounds(options);
+    EXPECT_EQ(MenuItemView::MenuPosition::kAboveBounds,
+              menu_item()->ActualMenuPosition());
+  }
+
+  // Case 2: There is insufficient space for the menu below `anchor_point`. The
+  // cached position is below the anchor. The menu should show above the anchor.
+  {
+    options.menu_size = menu_size;
+    options.menu_position = MenuItemView::MenuPosition::kBelowBounds;
+    CalculateBubbleMenuBounds(options);
+    EXPECT_EQ(MenuItemView::MenuPosition::kAboveBounds,
+              menu_item()->ActualMenuPosition());
+  }
+
+  // Case 3: There is enough space for the menu below `anchor_point`. The cached
+  // menu position is above the anchor. The menu should show above the anchor.
+  {
+    // Shrink the menu size. Verify that there is enough space below the anchor
+    // point now.
+    constexpr gfx::Size updated_size(menu_size.width(), menu_size.height() / 2);
+    options.menu_size = updated_size;
+    EXPECT_LE(options.anchor_bounds.y() - border_and_shadow_insets.top() +
+                  updated_size.height(),
+              monitor_bounds.bottom());
+
+    options.menu_position = MenuItemView::MenuPosition::kAboveBounds;
+    CalculateBubbleMenuBounds(options);
+    EXPECT_EQ(MenuItemView::MenuPosition::kAboveBounds,
+              menu_item()->ActualMenuPosition());
+  }
+}
+
 // Tests that opening the menu and pressing 'Home' selects the first menu item.
 TEST_F(MenuControllerTest, FirstSelectedItem) {
   SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc
index fa2388f32..8429239f 100644
--- a/ui/views/controls/styled_label.cc
+++ b/ui/views/controls/styled_label.cc
@@ -37,7 +37,7 @@
     base::RepeatingClosure callback) {
   // Adapt this closure to a Link::ClickedCallback by discarding the extra arg.
   return CreateForLink(base::BindRepeating(
-      [](base::RepeatingClosure closure, int) { closure.Run(); },
+      [](base::RepeatingClosure closure, const ui::Event&) { closure.Run(); },
       std::move(callback)));
 }
 
diff --git a/ui/views/layout/flex_layout.cc b/ui/views/layout/flex_layout.cc
index bd4e32e..045daa6 100644
--- a/ui/views/layout/flex_layout.cc
+++ b/ui/views/layout/flex_layout.cc
@@ -507,6 +507,7 @@
 
     const base::Optional<int> available_cross =
         GetAvailableCrossAxisSize(*data, view_index, bounds);
+    SetCrossAxis(&child_layout.available_size, orientation(), available_cross);
 
     flex_child.preferred_size =
         GetPreferredSizeForRule(flex_child.flex.rule(), child, available_cross);
@@ -516,25 +517,19 @@
       flex_child.current_size =
           GetCurrentSizeForRule(flex_child.flex.rule(), child,
                                 NormalizedSizeBounds(0, available_cross));
-      SetCrossAxis(&child_layout.available_size, orientation(),
-                   available_cross);
-
       DCHECK_GE(flex_child.preferred_size.main(),
                 flex_child.current_size.main())
           << " in " << child->GetClassName();
-
-      // Keep track of non-hidden flex controls.
-      const bool can_flex =
-          flex_child.flex.weight() > 0 ||
-          flex_child.current_size.main() < flex_child.preferred_size.main();
-      if (can_flex)
-        (*flex_order_to_index)[flex_child.flex.order()].push_back(view_index);
-
     } else {
       // All non-flex or unbounded controls get preferred size.
       flex_child.current_size = flex_child.preferred_size;
     }
 
+    // Keep track of non-hidden flex controls.
+    if (flex_child.flex.weight() > 0 ||
+        flex_child.current_size.main() < flex_child.preferred_size.main())
+      (*flex_order_to_index)[flex_child.flex.order()].push_back(view_index);
+
     child_layout.visible = flex_child.current_size.main() > 0;
   }
 }
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index 032f77d..bb19a2cc 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -144,14 +144,16 @@
   // reason for menu or for the main Chrome browser, as we have no reason to
   // specifically differentiate those yet.
   //
-  // Add additional values as needed.
+  // Add additional values as needed. Do not change any existing values, as this
+  // enum is logged to UMA.
   enum class ClosedReason {
-    kUnspecified = 0,      // No reason was given for the widget closing.
-    kEscKeyPressed,        // The ESC key was pressed to cancel the widget.
-    kCloseButtonClicked,   // The [X] button was explicitly clicked.
-    kLostFocus,            // The widget destroyed itself when it lost focus.
-    kCancelButtonClicked,  // The widget's cancel button was clicked.
-    kAcceptButtonClicked   // The widget's done/accept button was clicked.
+    kUnspecified = 0,         // No reason was given for the widget closing.
+    kEscKeyPressed = 1,       // The ESC key was pressed to cancel the widget.
+    kCloseButtonClicked = 2,  // The [X] button was explicitly clicked.
+    kLostFocus = 3,           // The widget destroyed itself when it lost focus.
+    kCancelButtonClicked = 4,  // The widget's cancel button was clicked.
+    kAcceptButtonClicked = 5,  // The widget's done/accept button was clicked.
+    kMaxValue = kAcceptButtonClicked
   };
 
   struct VIEWS_EXPORT InitParams {
diff --git a/ui/webui/resources/cr_components/chromeos/BUILD.gn b/ui/webui/resources/cr_components/chromeos/BUILD.gn
index 87fb0e39..12583695 100644
--- a/ui/webui/resources/cr_components/chromeos/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/BUILD.gn
@@ -9,21 +9,17 @@
 group("closure_compile") {
   deps = [
     "bluetooth:closure_compile",
+    "bluetooth:closure_compile_module",
     "cellular_setup:closure_compile",
+    "cellular_setup:closure_compile_module",
     "multidevice_setup:closure_compile",
     "network:closure_compile",
+    "network:closure_compile_module",
     "network_health:closure_compile",
-    "quick_unlock:closure_compile",
-    "smb_shares:closure_compile",
-
-    # Targets for auto-generated Polymer 3 JS Modules
-    # TODO: Uncomment as Polymer3 migration makes progress.
-    # "network:closure_compile_module",
-
-    "quick_unlock:closure_compile_module",
-    "cellular_setup:closure_compile_module",
-    "bluetooth:closure_compile_module",
     "network_health:closure_compile_module",
+    "quick_unlock:closure_compile",
+    "quick_unlock:closure_compile_module",
+    "smb_shares:closure_compile",
     "smb_shares:closure_compile_module",
   ]
 }
diff --git a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
index 5db23a0..3e1dec3 100644
--- a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
@@ -274,7 +274,7 @@
     ":network_property_list_mojo.m",
     #  ":network_proxy.m",
     #  ":network_proxy_exclusions.m",
-    #  ":network_proxy_input.m",
+    ":network_proxy_input.m",
     ":network_select.m",
     ":network_shared_css.m",
     ":network_siminfo.m",
@@ -527,7 +527,11 @@
 js_library("network_proxy_input.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":network_shared_css.m",
+    ":onc_mojo.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":network_proxy_input_module" ]
 }
@@ -734,6 +738,8 @@
   js_file = "network_proxy_input.js"
   html_file = "network_proxy_input.html"
   html_type = "dom-module"
+  auto_imports = cr_components_chromeos_auto_imports
+  namespace_rewrites = cr_components_chromeos_namespace_rewrites
 }
 
 polymer_modulizer("network_select") {
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
index 6e4dcff..c4e3bb4 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
@@ -3,6 +3,7 @@
 <link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
 <link rel="import" href="../../../html/i18n_behavior.html">
 <link rel="import" href="network_shared_css.html">
+<link rel="import" href="onc_mojo.html">
 
 <dom-module id="network-proxy-input">
   <template>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
index 7d649f9..40c3f6f4 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
@@ -8,8 +8,6 @@
  * event is fired with the combined url and port values passed as a single
  * string, url:port.
  */
-(function() {
-'use strict';
 
 Polymer({
   is: 'network-proxy-input',
@@ -66,4 +64,3 @@
     this.fire('proxy-input-change', this.value);
   }
 });
-})();
diff --git a/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js b/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
index d9dd579..dc03da1 100644
--- a/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
@@ -2,8 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/services/network/public/mojom/ip_address.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-lite.js';
+// #import 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-lite.js';
+
 // #import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
-// #import {loadTimeData} from '../../../js/load_time_data.m.js';
+// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+// cland-format on
 
 /**
  * @fileoverview Utilities supporting network_config.mojom types. The strings
diff --git a/ui/webui/resources/cr_components/cr_components_resources_v3.grdp b/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
index 3a5d9c4..5455dc06 100644
--- a/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
+++ b/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
@@ -108,6 +108,10 @@
            file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.m.js"
            use_base_dir="false"
            type="BINDATA" />
+    <include name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_M_JS"
+           file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
     <include name="IDR_WEBUI_CHROMEOS_NETWORK_SELECT_M_JS"
            file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_select.m.js"
            use_base_dir="false"
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
index 40eb420..4759677 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
@@ -38,5 +38,6 @@
   ITab createTab() = 11;
 
   void setTopViewAndScrollingBehavior(in IObjectWrapper view, in int minHeight,
-                                      in boolean pinToContentTop, in boolean animate) = 12;
+                                      in boolean onlyExpandControlsAtPageTop,
+                                      in boolean animate) = 12;
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/Browser.java b/weblayer/public/java/org/chromium/weblayer/Browser.java
index 0d2e9d18..5265dd89 100644
--- a/weblayer/public/java/org/chromium/weblayer/Browser.java
+++ b/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -224,23 +224,25 @@
      * @param view The new top-view, or null to remove the view.
      * @param minHeight The minimum height in pixels that the top controls can scoll up to. A value
      *        of 0 means the top-view should scroll entirely off screen.
-     * @param pinToContentTop Whether the top-view should only be expanded when the web
+     * @param onlyExpandControlsAtPageTop Whether the top-view should only be expanded when the web
      *        content is scrolled to the top. A true value makes the top-view behave as though it
-     *        were inserted into the top of the page content.
+     *        were inserted into the top of the page content. If true, the top-view should NOT be
+     *        used to display the URL, as this will prevent it from expanding in security-sensitive
+     *        contexts where the URL should be visible to the user.
      * @param animate Whether or not any height/visibility changes that result from this call
      *        should be animated.
      *
      * @since 86
      */
-    public void setTopView(
-            @Nullable View view, int minHeight, boolean pinToContentTop, boolean animate) {
+    public void setTopView(@Nullable View view, int minHeight, boolean onlyExpandControlsAtPageTop,
+            boolean animate) {
         ThreadCheck.ensureOnUiThread();
         if (WebLayer.getSupportedMajorVersionInternal() < 86) {
             throw new UnsupportedOperationException();
         }
         try {
             mImpl.setTopViewAndScrollingBehavior(
-                    ObjectWrapper.wrap(view), minHeight, pinToContentTop, animate);
+                    ObjectWrapper.wrap(view), minHeight, onlyExpandControlsAtPageTop, animate);
         } catch (RemoteException e) {
             throw new APICallException(e);
         }